rest

Generated by tinox doc

tinox.core.rest, split out of the bundled stdlib as a standalone tinox-central package.

ModuleVersionGroup
http (no local docs)1.3.5tinox.core
http_server (no local docs)1.2.8tinox.core
json (no local docs)1.1.7tinox.core
jwt (no local docs)1.1.7tinox.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 MediaTypeHelper

* Helper to convert MediaType enum values to their string representations

Methods

fn toString(mediaType: MediaType) → String
* Converts a MediaType enum value to its MIME type string * * @param mediaType the MediaType enum value * @return the MIME type string (e.g., "application/json")

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

baseUrlString The base URL
reqHeadersList<String> Custom headers for this request
queryParamsList<String> Query parameters for this request
reqBodyString 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

baseUrlString The base URL for all requests
headersList<String> Default headers applied to every request
timeoutInt64 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

statusCodeInt64 The HTTP status code
bodyString The response body as a string
okBool 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

valueString

Methods

fn new(value: String) → Auth

@annotationclass Consumes

* Specifies the accepted request content type. * @Consumes(MediaType::Json)

Fields

valueMediaType

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

pathString

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

pathString

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; * }

class MediaTypeHelper

* Converts MediaType enum values to their MIME type strings.

Methods

fn toString(mediaType: MediaType) → String

@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

rolesList<String>

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

pathString

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

pathString

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

pathString

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

valueString

Methods

fn new(value: String) → Path

@annotationclass Produces

* Specifies the response content type (Content-Type header). * @Produces(MediaType::Json)

Fields

valueMediaType

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

serverHttpServer The underlying HTTP server
routeEntriesList<RouteEntry> All registered route entries from controllers
globalMiddlewareList<fnc(HttpContext) -> Bool> Global middleware functions
corsEnabledBool Whether CORS is enabled
corsOriginString Allowed origins for CORS
authValidatorfnc(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

messageString

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

routesList<RouteEntry> Route entries registered for this controller
basePathString 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

methodString HTTP method (GET, POST, PUT, PATCH, DELETE)
pathString Full URL path pattern
handlerfnc(HttpContext) -> Nothing Handler function
statusCodeInt64 Default status code from @StatusCode annotation
producesMediaType Response content type from @Produces annotation
consumesMediaType Request content type from @Consumes annotation
authTypeString 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

valueInt64

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