Read Request Parameters

Pull path segments and query parameters into typed handler arguments.

Parameter decorators extract request data and hand it to your handler as typed arguments — no manual parsing.

Path parameters

Declare :name segments in the route, extract them with @HttpPath:

import { HttpPath } from '@system-inc/base-foundation/router/decorators/HttpPath';

@HttpRoute('GET', '/notes/:id')
get(@HttpPath('id') id: string): Response { ... }

@HttpRoute('GET', '/books/:author/:year/:title')
find(
    @HttpPath('author') author: string,
    @HttpPath('year', () => Number) year: number,
    @HttpPath('title') title: string,
): Response { ... }

Path values are strings by default; pass a type thunk (() => Number) to coerce.

Query parameters

@HttpQuery works the same way for ?key=value pairs:

import { HttpQuery } from '@system-inc/base-foundation/router/decorators/HttpQuery';

@HttpRoute('GET', '/search')
search(
    @HttpQuery('term') term: string,
    @HttpQuery('limit', () => Number) limit: number,
): Response { ... }

Bind the whole query string to a class

For endpoints with several parameters, define a serializable class and bind the entire query string in one argument:

import { SerializableField } from '@system-inc/base-foundation/serialization/decorators/SerializableField';
import { SerializableObject } from '@system-inc/base-foundation/serialization/decorators/SerializableObject';

@SerializableObject()
export class SearchQuery {
    @SerializableField(() => String)
    term: string;

    @SerializableField(() => Number)
    limit: number;
}
@HttpRoute('GET', '/search')
search(@HttpQuery(() => SearchQuery) query: SearchQuery): Response {
    return Response.json({ term: query.term, limit: query.limit });
}

Fields are deserialized and coerced per their @SerializableField types, and any validation decorators on the class run before your handler — the same machinery as request bodies.