rest
Generated by tinox doc
tinox.core.rest, split out of the bundled stdlib as a standalone tinox-central package.
| Module | Version | Group |
|---|
http (no local docs) | 1.3.5 | tinox.core |
http_server (no local docs) | 1.2.8 | tinox.core |
json (no local docs) | 1.1.7 | tinox.core |
jwt (no local docs) | 1.1.7 | tinox.core |
Client call
import tinox.core.rest.client
class Main
{
fnc main() -> Int32
{
const client: RestClient = RestClient::new("https://api.example.com")
client.bearerAuth("my-token")
const response: RestResponse = client.get("/users/42")
if (response.isOk())
{
println("body: " + response.text())
}
else
{
println("request failed with status " + response.status().toString())
}
const created: RestResponse = client.post("/users", "{\"name\":\"Ada\"}")
created.ensureSuccess()
println("created: " + created.text())
return 0
}
}
Annotated controller
import tinox.core.rest.server
import tinox.core.json
// Annotation-driven: no explicit main()/RestApi anywhere in the program —
// the compiler discovers @ApplicationComponent classes and generates the
// server entry point itself.
@ApplicationComponent
@Path("/api/v1")
class GreetController
{
@GET
@Path("/hello/:name")
@Produces("application/json")
fn hello(@PathParam name: String, @HttpContext ctx: HttpContext) -> HttpContext
{
ctx.response.json("{\"message\":\"Hello, " + name + "!\"}")
return ctx
}
}
Annotated controller entry point
import GreetController
// Every Tinox program still needs its own class Main as the mandatory
// entry point (since 2026-08-09) -- @ApplicationComponent's REST server
// spawns and runs alongside it on its own thread, it doesn't replace it.
// This is the required sibling file for the annotated controller above.
class Main
{
fnc main() -> Int32
{
return 0
}
}
class HttpStatusHelper
* Helper to convert between HttpStatus enum and status code integers
Methods
fn fromCode(code: Int64) → HttpStatus
* Converts an HTTP status integer to a HttpStatus enum value
*
* @param code the numeric status code
* @return the matching HttpStatus enum value, or NotFound as fallback
fn toCode(status: HttpStatus) → Int64
* Converts a HttpStatus enum value to its numeric status code
*
* @param status the HttpStatus enum value
* @return the numeric HTTP status code
fn isSuccess(code: Int64) → Bool
* Checks if a status code represents a success (2xx)
*
* @param code the numeric status code
* @return true if the code is in the 200-299 range
fn isClientError(code: Int64) → Bool
* Checks if a status code represents a client error (4xx)
*
* @param code the numeric status code
* @return true if the code is in the 400-499 range
fn isServerError(code: Int64) → Bool
* Checks if a status code represents a server error (5xx)
*
* @param code the numeric status code
* @return true if the code is in the 500-599 range
class RequestBuilder
* HTTP request builder for constructing requests
* with a fluent API (method chaining pattern)
*
* Example usage:
* RequestBuilder::new("https://api.example.com")
* .header("Accept", "application/json")
* .get("/users")
Fields
baseUrl | String | The base URL |
reqHeaders | List<String> | Custom headers for this request |
queryParams | List<String> | Query parameters for this request |
reqBody | String | The request body |
Methods
fn new(baseUrl: String) → RequestBuilder
* Creates a new RequestBuilder
*
* @param baseUrl the base URL for requests
* @return a new RequestBuilder instance
fn header(name: String, value: String) → RequestBuilder
* Adds a header to the request
*
* @param name the header name
* @param value the header value
* @return this builder for chaining
fn query(key: String, value: String) → RequestBuilder
* Adds a query parameter
*
* @param key the parameter key
* @param value the parameter value
* @return this builder for chaining
fn body(body: String) → RequestBuilder
* Sets the request body
*
* @param body the request body string
* @return this builder for chaining
fn jsonBody(jsonBody: String) → RequestBuilder
* Sets the request body as JSON with Content-Type header
*
* @param jsonBody the JSON string to send
* @return this builder for chaining
fn bearer(token: String) → RequestBuilder
* Adds Bearer authentication header
*
* @param token the bearer token
* @return this builder for chaining
fn get(path: String) → RestResponse
* Executes a GET request
*
* @param path the endpoint path
* @return the REST response
fn post(path: String) → RestResponse
* Executes a POST request
*
* @param path the endpoint path
* @return the REST response
fn put(path: String) → RestResponse
* Executes a PUT request
*
* @param path the endpoint path
* @return the REST response
fn patch(path: String) → RestResponse
* Executes a PATCH request
*
* @param path the endpoint path
* @return the REST response
fn delete(path: String) → RestResponse
* Executes a DELETE request
*
* @param path the endpoint path
* @return the REST response
fn buildUrl(path: String) → String
* Builds the full URL with query parameters
*
* @param path the endpoint path
* @return the complete URL string
fn applyHeaders() → Nothing
* Applies all stored headers to the HTTP client
class RestClient
* REST client for making HTTP requests to RESTful APIs
*
* Supports building requests with custom headers,
* query parameters, and JSON request/response bodies.
Fields
baseUrl | String | The base URL for all requests |
headers | List<String> | Default headers applied to every request |
timeout | Int64 | Request timeout in milliseconds |
Methods
fn new(baseUrl: String) → RestClient
* Creates a new RestClient with the given base URL
*
* @param baseUrl the base URL for all requests (e.g., "https://api.example.com")
* @return a new RestClient instance
fn newWithHeaders(baseUrl: String, defaultHeaders: List<String>) → RestClient
* Creates a new RestClient with base URL and default headers
*
* @param baseUrl the base URL for all requests
* @param defaultHeaders list of "Key: Value" header strings
* @return a new RestClient instance
fn setTimeout(timeoutMs: Int64) → Nothing
* Sets the request timeout
*
* @param timeoutMs timeout in milliseconds
fn addHeader(name: String, value: String) → Nothing
* Adds a default header to all requests
*
* @param name the header name
* @param value the header value
fn clearHeaders() → Nothing
* Clears all default headers
fn get(path: String) → RestResponse
* Performs a GET request
*
* @param path the endpoint path (appended to baseUrl)
* @return the server response
fn getWithParams(path: String, params: Map<String, String>) → RestResponse
* Performs a GET request with query parameters
*
* @param path the endpoint path
* @param params map of query parameter key-value pairs
* @return the server response
fn post(path: String, body: String) → RestResponse
* Performs a POST request with a JSON body
*
* @param path the endpoint path
* @param body the request body as a string
* @return the server response
fn put(path: String, body: String) → RestResponse
* Performs a PUT request with a JSON body
*
* @param path the endpoint path
* @param body the request body as a string
* @return the server response
fn patch(path: String, body: String) → RestResponse
* Performs a PATCH request with a JSON body
*
* @param path the endpoint path
* @param body the request body as a string
* @return the server response
fn delete(path: String) → RestResponse
* Performs a DELETE request
*
* @param path the endpoint path
* @return the server response
fn bearerAuth(token: String) → Nothing
* Adds Bearer token authentication header
*
* @param token the bearer token
fn basicAuth(username: String, password: String) → Nothing
* Adds Basic authentication header
*
* @param username the username
* @param password the password
class RestResponse
* REST response wrapper providing convenient access to
* status codes, headers, and parsed JSON bodies
Fields
statusCode | Int64 | The HTTP status code |
body | String | The response body as a string |
ok | Bool | Whether the request was successful (status 2xx) |
Methods
fn new(statusCode: Int64, body: String) → RestResponse
* Creates a new RestResponse
*
* @param statusCode the HTTP status code
* @param body the response body
* @return a new RestResponse instance
fn fromHttp(response: HttpClientResponse) → RestResponse
* Creates a RestResponse from an HttpClientResponse
*
* @param response the raw HTTP response
* @return a RestResponse instance
fn text() → String
* Returns the response body as a string
*
* @return the response body
fn json() → JsonValue
* Parses the response body as JSON
*
* @return a JsonValue representing the parsed JSON
fn isOk() → Bool
* Checks if the response status is a success (2xx)
*
* @return true if status code is between 200 and 299
fn isClientError() → Bool
* Checks if the response status is a client error (4xx)
*
* @return true if status code is between 400 and 499
fn isServerError() → Bool
* Checks if the response status is a server error (5xx)
*
* @return true if status code is between 500 and 599
fn status() → Int64
* Returns the HTTP status code
*
* @return the status code (e.g., 200, 404, 500)
fn ensureSuccess() → RestResponse
* Throws an error if the response was not successful
*
* @return this response for chaining
* @throws an error message with status code and body if not 2xx
class Url
* URL builder utility for constructing URLs with
* path segments, query parameters, and fragments
Methods
fn build(baseUrl: String, path: String) → String
* Builds a full URL from a base URL and a path
*
* @param baseUrl the base URL (e.g., "https://api.example.com")
* @param path the path to append (e.g., "/users")
* @return the combined URL
fn buildWithParams(baseUrl: String, path: String, params: Map<String, String>) → String
* Builds a URL with query parameters
*
* @param baseUrl the base URL
* @param path the endpoint path
* @param params map of query parameter key-value pairs
* @return the full URL with encoded query string
fn encode(s: String) → String
* Encodes a string for use in a URL (percent-encoding)
*
* Replaces reserved characters with their percent-encoded equivalents.
* Characters A-Z, a-z, 0-9, -, _, ., ~ are left unencoded.
*
* @param s the string to encode
* @return the percent-encoded string
fn isUnreserved(ch: String) → Bool
* Checks if a character is unreserved in URL encoding
*
* Unreserved characters are A-Z, a-z, 0-9, -, _, ., ~
*
* @param ch a single character string
* @return true if the character is unreserved
fn toHex(n: Int64) → String
* Converts an integer to a two-digit uppercase hex string
*
* @param n the value to convert (0-255)
* @return the hex string (e.g., "0F" for 15)
fn joinPath(segments: List<String>) → String
* Joins multiple path segments into a single path
*
* Removes duplicate slashes between segments
*
* @param segments list of path segments
* @return a clean joined path
@annotationclass ApplicationComponent
* Lazy singleton — one instance for the entire lifetime of the application.
* Created on first access.
@annotationclass Auth
* Marks a method or class as requiring authentication.
* @Auth("bearer")
* @Auth("basic")
Fields
Methods
fn new(value: String) → Auth
@annotationclass Consumes
* Specifies the accepted request content type.
* @Consumes(MediaType::Json)
Fields
Methods
fn new(value: MediaType) → Consumes
@annotationclass DELETE
* Marks a method as a DELETE endpoint.
* @DELETE("/path") or @DELETE for the class base path
Fields
Methods
fn new(path: String) → DELETE
fn default() → DELETE
@annotationclass GET
* Marks a method as a GET endpoint.
* @GET("/path") or @GET for the class base path
Fields
Methods
fn new(path: String) → GET
fn default() → GET
@annotationclass HttpRequestScoped
* One instance per HTTP request. Created fresh for each incoming request
* and lives exactly as long as that request.
@annotationclass Inject
* Marks a field for compile-time dependency injection.
* The compiler resolves the dependency from the DI container and
* wires it up without any runtime lookup.
*
* Example:
* @ApplicationComponent
* class UserController
* {
* @Inject
* var service: UserService;
* }
@annotationclass OIDCRolesAllowed
* Requires a verified OIDC access token carrying at least one of the
* listed realm roles. The Authorization: Bearer <token> header is
* verified (RS256 signature via the IdP's JWKS, issuer, audience,
* exp/nbf) live against the IdP configured via the OIDC_ISSUER/
* OIDC_JWKS_URI/OIDC_AUDIENCE environment variables -- no key
* material or role list is ever hardcoded.
*
* @OIDCRolesAllowed(["admin", "api-user"])
* fn adminOnly(ctx: HttpContext) -> Nothing { ... }
Fields
Methods
fn new(roles: List<String>) → OIDCRolesAllowed
class OidcGuard
Methods
@Throwsfnc checkRoles(ctx: HttpContext, rolesCsv: String) → Bool
@Throwsfnc verifyToken(token: String, issuer: String, jwksUri: String, audience: String) → Map<String, JsonValue>
@annotationclass PATCH
* Marks a method as a PATCH endpoint.
* @PATCH("/path") or @PATCH for the class base path
Fields
Methods
fn new(path: String) → PATCH
fn default() → PATCH
@annotationclass POST
* Marks a method as a POST endpoint.
* @POST("/path") or @POST for the class base path
Fields
Methods
fn new(path: String) → POST
fn default() → POST
@annotationclass PUT
* Marks a method as a PUT endpoint.
* @PUT("/path") or @PUT for the class base path
Fields
Methods
fn new(path: String) → PUT
fn default() → PUT
@annotationclass Path
* Sets the base path for a controller class or a path prefix for a method.
* @Path("/api/v1")
Fields
Methods
fn new(value: String) → Path
@annotationclass Produces
* Specifies the response content type (Content-Type header).
* @Produces(MediaType::Json)
Fields
Methods
fn new(value: MediaType) → Produces
class RestApi
* Main REST framework class for building and starting an HTTP API server.
*
* Combines the annotation-driven approach with explicit registration,
* supporting graceful migration once the compiler processes annotations natively.
*
* Example:
* const app: RestApi = RestApi::new(8080);
*
* // Register an annotated controller
* app.register(userController);
*
* // Or use fluent API directly
* app.get("/ping", ctx => {
* ctx.response.text("pong");
* });
*
* // Add global middleware
* app.use(loggingMiddleware);
* app.use(corsMiddleware);
*
* app.start();
Fields
server | HttpServer | The underlying HTTP server |
routeEntries | List<RouteEntry> | All registered route entries from controllers |
globalMiddleware | List<fnc(HttpContext) -> Bool> | Global middleware functions |
corsEnabled | Bool | Whether CORS is enabled |
corsOrigin | String | Allowed origins for CORS |
authValidator | fnc(String, String) -> Bool |
* Validates the credential extracted from an @Auth-protected route's
* Authorization header: called as (authType, credential) where
* credential is the header value with the "Bearer "/"Basic " scheme
* prefix already stripped. Defaults to rejecting everything (Bug
* 104) -- previously @Auth only checked that the header started
* with the right scheme prefix and never validated the credential
* itself, so any syntactically-shaped Authorization header passed.
* Wire up real validation with setAuthValidator(), e.g. checking a
* bearer token against Jwt::verify() or a basic-auth credential
* against a user store.
|
Methods
fn new(port: Int64) → RestApi
fn setAuthValidator(validator: fnc(String, String) -> Bool) → RestApi
* Registers the credential validator used by @Auth-protected routes
* (see authValidator). Must be set explicitly -- without it, every
* @Auth-protected route rejects all requests.
*
* @param validator fn(authType, credential) -> Bool
* @return this RestApi for chaining
fn register(controller: RestController) → RestApi
* Registers all routes from a RestController.
*
* Routes defined via the controller's .route() calls are added
* to the framework's routing table and the underlying server.
*
* @param controller the controller to register
* @return this RestApi for chaining
fn get(path: String, handler: fnc(HttpContext) -> Nothing) → RestApi
* Registers a GET route directly (fluent API)
*
* @param path the URL path pattern
* @param handler the handler function
* @return this RestApi for chaining
fn post(path: String, handler: fnc(HttpContext) -> Nothing) → RestApi
* Registers a POST route directly (fluent API)
*
* @param path the URL path pattern
* @param handler the handler function
* @return this RestApi for chaining
fn put(path: String, handler: fnc(HttpContext) -> Nothing) → RestApi
* Registers a PUT route directly (fluent API)
*
* @param path the URL path pattern
* @param handler the handler function
* @return this RestApi for chaining
fn patch(path: String, handler: fnc(HttpContext) -> Nothing) → RestApi
* Registers a PATCH route directly (fluent API)
*
* @param path the URL path pattern
* @param handler the handler function
* @return this RestApi for chaining
fn delete(path: String, handler: fnc(HttpContext) -> Nothing) → RestApi
* Registers a DELETE route directly (fluent API)
*
* @param path the URL path pattern
* @param handler the handler function
* @return this RestApi for chaining
fn use(mware: fnc(HttpContext) -> Bool) → RestApi
* Adds a global middleware function.
* Middleware runs before route handlers in registration order.
* Return false to stop the request chain (sends 403).
*
* @param mware the middleware function
* @return this RestApi for chaining
fn enableCors(origin: String) → RestApi
* Enables CORS for all responses
*
* @param origin the allowed origin (default "*")
* @return this RestApi for chaining
fn defaultJson() → RestApi
* Adds default JSON Content-Type to all responses
*
* @return this RestApi for chaining
fn start() → Nothing
* Starts the REST API server.
*
* Registers all route entries with the underlying HttpServer
* and begins listening for connections. This is a blocking call.
fn stop() → Nothing
* Stops the REST API server.
fn registerRoutes() → Nothing
* Registers all collected route entries with the underlying HttpServer,
* applying annotation metadata (status code, content types, auth).
fn wrapHandler(entry: RouteEntry) → fnc(HttpContext) -> Nothing
* Wraps a route handler to apply annotation metadata:
* - @StatusCode sets the default response status
* - @Produces sets Content-Type if not already set
* - @Consumes validates the request Content-Type
* - @Auth validates authentication
*
* @param entry the route entry with metadata
* @return a wrapped handler that applies metadata before calling the original
fn addRouteEntry(entry: RouteEntry) → Nothing
* Adds a RouteEntry to the internal list
@Errorclass RestAuthError
* A bearer token was rejected by the OIDC guard.
Fields
class RestController
* Abstract base class for REST controllers.
*
* Extend this class and use annotations on your methods.
* Register the controller with RestApi::register().
*
* Example:
* @Path("/api/users")
* class UserController : RestController
* {
* @GET("/")
* @Produces("application/json")
* fn list(ctx: HttpContext) -> Nothing
* {
* ctx.response.json("[]");
* }
* }
Fields
routes | List<RouteEntry> | Route entries registered for this controller |
basePath | String | Base path for all routes in this controller |
Methods
fn new() → RestController
fn route(method: String, path: String, handler: fnc(HttpContext) -> Nothing) → RouteEntry
* Manually registers a route on this controller.
* Called by the framework during register() to wire up annotated methods.
*
* @param method the HTTP method
* @param path the route path (appended to basePath)
* @param handler the handler function
* @return the RouteEntry for further configuration
class RouteEntry
* Represents a registered route with its method, path, handler,
* and metadata extracted from annotations.
Fields
method | String | HTTP method (GET, POST, PUT, PATCH, DELETE) |
path | String | Full URL path pattern |
handler | fnc(HttpContext) -> Nothing | Handler function |
statusCode | Int64 | Default status code from @StatusCode annotation |
produces | MediaType | Response content type from @Produces annotation |
consumes | MediaType | Request content type from @Consumes annotation |
authType | String | Auth type from @Auth annotation ("bearer", "basic", or empty) |
Methods
fn new(method: String, path: String, handler: fnc(HttpContext) -> Nothing) → RouteEntry
fn withStatus(code: Int64) → RouteEntry
fn withProduces(mediaType: MediaType) → RouteEntry
fn withConsumes(mediaType: MediaType) → RouteEntry
fn withAuth(authType: String) → RouteEntry
@annotationclass Startup
* Eager singleton — created immediately at application startup, before any
* request is handled. Useful for services that need initialization work
* (e.g. DB connection pools, background workers).
@annotationclass StatusCode
* Sets the default HTTP status code for the response.
* @StatusCode(201)
Fields
Methods
fn new(value: Int64) → StatusCode
class UrlBuilder
* URL path builder for joining base paths and route paths
Methods
fn join(base: String, path: String) → String
* Joins a base path and a route path, avoiding duplicate slashes
*
* @param base the base path (e.g., "/api/users")
* @param path the route path (e.g., "/:id")
* @return the combined path