Route (Dynamic)

The Route class (variant dynamic internally) defines an HTTP endpoint with automatic registration to the global router. It accepts a flexible address (a path string, a "VERB /path" string, or an object with method and path) and a handler that receives the request context. Routes can optionally include a model for request/response validation and type safety.

Contents
  1. Usage
  2. Constructor Parameters
  3. Properties

Usage

Routes can be instantiated directly with new. The constructor automatically registers the route to the global router store.

Simple GET route

import { C } from "@ozanarslan/corpus";

// GET /users
new C.Route("/users", () => [{ id: 1, name: "Alice" }]);

Route with specific HTTP method

Either use the object form or prefix the path string with an HTTP verb:

import { C } from "@ozanarslan/corpus";

// POST /users
new C.Route({ method: C.Method.POST, path: "/users" }, (c) => {
	return { created: c.body.name };
});

// DELETE /users/:id
new C.Route("DELETE /users/:id", (c) => {
	return { deleted: c.params.id };
});

Route with validation model

import { C } from "@ozanarslan/corpus";
import { z } from "zod";

const UserModel = {
	body: z.object({ name: z.string(), email: z.email() }),
	response: z.object({ id: z.number(), name: z.string() }),
};

new C.Route(
	{ method: C.Method.POST, path: "/users" },
	(c) => {
		// c.body is typed as { name: string; email: string }
		return { id: 1, name: c.body.name };
	},
	UserModel,
);

Handler return types

Handlers can return:

// Automatic JSON response
new C.Route("/users", () => ({ users: [] }));

// Custom Res
new C.Route("/error", () => {
	return new C.Res("Not Found", { status: 404 });
});

Extending the abstract class

I wouldn't recommend extending since the model parsing basically becomes useless.

class MyRoute extends C.RouteAbstract {
	constructor() {
		super();
		// this method needs to be called to register it to the router
		// here or where you instantiate
		this.register();
	}

	override endpoint: string = "/extended";
	override method: C.Method = C.Method.GET;
	override handler: Func<[context: C.Context<unknown, unknown, unknown, unknown>], unknown> = () =>
		"extended";
}

Constructor Parameters

address

RouteAddress<E>

The route address. A plain path string defaults to GET. A string containing whitespace must start with a valid HTTP verb (case-insensitive, resolved to uppercase) followed by the path, otherwise resolution throws. The object form is equivalent to the verb-prefixed string.

type RouteAddress<E extends string = string> = E | `${Method} ${E}` | { method: Method; path: E };
ValueResult
"/users"GET /users
"DELETE /users/:id"DELETE /users/:id
{ method: "POST", path: "/users" }POST /users

callback

(context: Context<B, S, P, R>) => MaybePromise<R>

The route handler function. Receives the request context with typed access to body (c.body), search params (c.search), URL params (c.params), Req (c.req), and Res for response manipulation without returning a Res (c.res).

model (optional)

RouteModel<B, S, P, R>

Optional validation model for the request body, search params, URL params, and response. When provided, the context properties are typed and validated automatically. See Model. You can pass generics if you don't want to bother with validation but still typecast your data: Route<B, S, P, R, E extends string>

// type Schema is any standard schema library validator.
type RouteModel<B = unknown, S = unknown, P = unknown, R = unknown> = {
	response?: Schema<R>;
	body?: Schema<B>;
	search?: Schema<S>;
	params?: Schema<P>;
};

Properties

All constructor options are stored as properties after resolve methods:

PropertyTypeDescription
idstringUnique route identifier ({METHOD} {endpoint}, e.g. GET /users)
methodMethodHTTP method resolved from the address, defaults to GET
endpointEResolved path
handlerFuncThe route handler function
modelRouteModel | undefinedValidation model if provided
variantRouteVariant.dynamicFixed to dynamic for this class