An HTTP server module for @uon/core applications: decorator-based routing,
request-scoped dependency injection, body/query validation guards, CORS,
cookies, caching, byte-range streaming, TLS, connection upgrades and a
serverless adapter.
npm i @uon/httpPeer dependencies: @uon/core, @uon/model, @uon/router. Decorator metadata
must be enabled in your tsconfig.json (experimentalDecorators +
emitDecoratorMetadata), and reflect-metadata imported once at your entry
point (it is imported by @uon/core).
import{Module,Application}from'@uon/core';import{HttpModule}from'@uon/http';
@Module({imports: [HttpModule.WithConfig({plainPort: 8080}),],})exportclassMyAppModule{}Application.Bootstrap(MyAppModule).start();HttpModule.WithConfig(config) accepts an HttpConfig:
| Option | Description |
|---|---|
plainPort | Port for the plain HTTP server. |
port | Port for the HTTPS server (requires a TLSProvider). |
host | Interface to bind. |
providers | Extra providers added to every request-scoped injector. |
traceContextErrors | console.error errors thrown from outlets. |
routerToken | Which router token requests are matched against (defaults to HTTP_ROUTER). |
serverless | Run without listening on a socket (for mockRequest / lambda). |
modelAdapter | Model layer used by the guards (validation / formatting / (de)serialization). Defaults to the @uon/model implementation — see Pluggable model layer. |
Routing is built on @uon/router. The module provides two router tokens:
HTTP_ROUTER and HTTP_REDIRECT_ROUTER (the latter for the automatic
http → https redirect).
Declare a @RouterOutlet whose methods are decorated with @HttpRoute:
import{RouterOutlet,RouteParams}from'@uon/router';import{HttpRoute,OutgoingResponse}from'@uon/http';
@RouterOutlet()exportclassMyAppOutlet{constructor(privateresponse: OutgoingResponse){}
@HttpRoute({method: 'GET',path: '/say-hello/:name'})sayHello(params: RouteParams){this.response.send(`Hello World! ${params.name}`);}}Then bind a route tree to a router token via RouterModule.For:
import{RouterModule}from'@uon/router';import{HTTP_ROUTER}from'@uon/http';constroutes=[{path: '/my-base-path',outlet: MyAppOutlet}];
@Module({imports: [RouterModule.For(HTTP_ROUTER,routes)]})exportclassMyAppModule{}@HttpRoute.method may be a string or an array of methods; the router only
matches an exact method.
Inject IncomingRequest into a constructor or handler:
constructor(privaterequest: IncomingRequest){}// request.method, request.uri, request.headers, request.secure,// request.clientIp, request.userAgent// await request.body -> Buffer (see the body-size note below)Security — X-Forwarded- headers.*
request.clientIpandrequest.secureare derived fromX-Forwarded-For/X-Forwarded-Protounconditionally. Only rely on them when the server sits behind a trusted proxy that sets these headers; a direct client can spoof them.
Body size.
request.bodybuffers the whole body with a hard safety ceiling (100 MB) to avoid memory exhaustion. For tighter, per-route limits use a body guard'smaxLength.
Inject OutgoingResponse:
response.send(data);// Buffer | string | nullresponse.json(obj,{ pretty, prefixOutput, keep });response.redirect('/somewhere',/* permanent */false);response.setHeader(name,value);response.assignHeaders({ ... });response.stream(readableStream);response.use(modifier);// IOutgoingReponseModifier in the pipelinejson() options: pretty (tab-indent), prefixOutput (prepend the )]}',\n
XSSI guard), keep (whitelist of keys to emit).
JsonBodyGuard(type?, options?)+RequestBody— parse/validate a JSON body against a@Modeltype. Options:validate,validateArray,throwOnValidation,maxLength. A malformed JSON body responds400.FormDataBodyGuard— multipart/form-data bodies.QueryGuard+RequestQuery— coerce/validate the query string.RouteParamsGuard— validate path params.
RequestBody / RequestQuery expose .raw, .value (coerced) and
.validation.
The guards don't talk to @uon/model directly — they go through an
HttpModelAdapter resolved from the HTTP_MODEL_ADAPTER token. The default
adapter (UonModelAdapter) wraps @uon/model, so out of the box everything
behaves as before. To use a different model/validation library, implement the
interface and pass it as modelAdapter:
import{HttpModelAdapter,HttpModelValidationResult,HttpModule}from'@uon/http';classMyModelAdapterimplementsHttpModelAdapter{isModel(type){/* is `type` a model you handle? */}deserialize(type,data){/* already-parsed JSON object -> instance */}deserializeFromString(type,raw){/* coerce a string-keyed map (query/form) -> instance */}serialize(value){/* instance -> JSON-ready value, before JSON.stringify() */}asyncvalidate(subject,validators,injector,key){// ...run validation, return an HttpModelValidationResult-shaped resultreturnnewHttpModelValidationResult(key);}applyFormatting(subject){/* mutate `subject` in place, or no-op */}}
@Module({imports: [HttpModule.WithConfig({modelAdapter: newMyModelAdapter()})],})exportclassMyAppModule{}The adapter only covers the operations that depend on the model library. The
result/failure/validator shapes are owned by @uon/http and are
library-agnostic — exported alongside the helpers that operate on them:
HttpValidationResult/HttpModelValidationResult— the result types (key,failures,children, and a computedvalid). Your adapter'svalidatemust return something satisfyingHttpModelValidationResult, and your validators must throwHttpValidationFailure-shaped objects.HttpValidator/HttpValidatorMap— the validator function shape used in guardvalidateoptions.RunValidators(params, validators, result)— runs validators and collects failures (used byRouteParamsGuard; needs no adapter).FlattenValidationResult(result)— flattens a result tree to{ path, errors }entries for an error body (used byHttpErrorJsonHandler).IsValidationResult(x)— structural guard for validation results.
import{CorsGuard}from'@uon/http';
@HttpRoute({method: 'GET',path: '/api',guards: [CorsGuard({origin: '*'})]})CorsGuardOptions: origin ('*' | string | string[]), methods, headers,
exposeHeaders, credentials, maxAge.
Security — CORS. The allowed origin is decided only from the request's
Originheader (never theHostheader), and aVary: Originresponse header is set so caches don't share one origin's response with another.
Cookies—getCookie/getCookies/setCookie(name, value, CookieSetOptions). Malformed incoming cookie values fall back to their raw value instead of throwing.Expires— conditional caching (If-Modified-Since→304),ExpiresandLast-Modifiedheaders viaconfigure({ expiresIn, lastModified }).Range— byte-range file streaming viaconfigure({ path, maxChunkSize }). Supportsbytes=N-,bytes=N-Mand suffixbytes=-Nranges, responds206(or200for the full file) and416for unsatisfiable ranges.AcceptLanguage/AcceptEncoding—getBestMatch(available)content negotiation.
import{HttpError}from'@uon/http';thrownewHttpError(404);Provide a custom handler via the HTTP_ERROR_HANDLER token (swap the default
HttpErrorPlainTextHandler for HttpErrorJsonHandler), or implement
onHttpError(err) on a @RouterOutlet controller (OnHttpError).
Provide an HTTP_TLS_PROVIDER (TLSProvider) and set port. With both port
and plainPort, requests to the plain port are redirected to https via
HTTP_REDIRECT_ROUTER.
Register an HttpUpgradeHandler under HTTP_UPGRADE_HANDLER and call
HttpContext.upgrade(type, headers?) from a handler.
Set serverless: true and either drive HttpServer.mockRequest(options)
directly or use CreateHttpAwsLambdaHandler(app) for AWS API Gateway. Binary
responses are base64-encoded automatically (isBase64Encoded).
Each request gets an HttpContext and a child injector seeded with
HttpContext, IncomingRequest, OutgoingResponse, ActivatedRoute,
RouteParams, RouteData, the config.providers, and the outlet's own
providers. HttpServer emits request / response / error events.
MIT