UNPKG

@nestjs/platform-fastify

Version:

Run NestJS on Fastify: the HTTP adapter with Fastify's plugins, JSON schemas and serialization.

313 lines (312 loc) • 19.9 kB
import { FastifyCorsOptions } from '@fastify/cors'; import { Logger, type NestApplicationOptions, type RequestMethod, type VersioningOptions } from '@nestjs/common'; import { FastifyBaseLogger, FastifyBodyParser, FastifyInstance, FastifyPluginAsync, FastifyPluginCallback, FastifyRegister, FastifyReply, FastifyRequest, FastifyServerOptions, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault, RequestGenericInterface, RouteGenericInterface } from 'fastify'; import * as http from 'http'; import * as http2 from 'http2'; import * as https from 'https'; import { InjectOptions, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'; import { type SecurityRequestHook, type VersionValue } from '@nestjs/common/internal'; import { AbstractHttpAdapter } from '@nestjs/core'; import { FastifyMultipartOptions, FastifyStaticOptions, FastifyViewOptions } from '../interfaces/external/index.js'; import { NestFastifyBodyParserOptions } from '../interfaces/index.js'; type FastifyAdapterBaseOptions<Server extends RawServerBase = RawServerDefault, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyServerOptions<Server, Logger> & { skipMiddie?: boolean; /** * Controls the "@fastify/multipart" plugin (an optional peer dependency), * which the upload interceptors from "@nestjs/platform-fastify/multipart" * require. * * - unset (default): the adapter registers the plugin when an upload * interceptor is used, unless it is already registered. * - an object: plugin options (e.g. `limits`); the plugin is registered * when the application initializes. * - `true`: the same, with the plugin defaults. * - `false`: the adapter never registers the plugin. */ multipart?: boolean | FastifyMultipartOptions; }; type FastifyHttp2SecureOptions<Server extends http2.Http2SecureServer, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyAdapterBaseOptions<Server, Logger> & { http2: true; https: http2.SecureServerOptions; }; type FastifyHttp2Options<Server extends http2.Http2Server, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyAdapterBaseOptions<Server, Logger> & { http2: true; http2SessionTimeout?: number; }; type FastifyHttpsOptions<Server extends https.Server, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyAdapterBaseOptions<Server, Logger> & { https: https.ServerOptions; }; type FastifyHttpOptions<Server extends http.Server, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyAdapterBaseOptions<Server, Logger> & { http: http.ServerOptions; }; type VersionedRoute<TRequest, TResponse> = ((req: TRequest, res: TResponse, next: Function) => Function) & { version: VersionValue; versioningOptions: VersioningOptions; }; /** * The following type assertion is valid as we enforce "middie" plugin registration * which enhances the FastifyRequest.RawRequest with the "originalUrl" property. * ref https://github.com/fastify/middie/pull/16 * ref https://github.com/fastify/fastify/pull/559 */ type FastifyRawRequest<TServer extends RawServerBase> = RawRequestDefaultExpression<TServer> & { originalUrl?: string; }; /** * @publicApi */ export declare class FastifyAdapter<TServer extends RawServerBase = RawServerDefault, TRawRequest extends FastifyRawRequest<TServer> = FastifyRawRequest<TServer>, TRawResponse extends RawReplyDefaultExpression<TServer> = RawReplyDefaultExpression<TServer>, TRequest extends FastifyRequest<RequestGenericInterface, TServer, TRawRequest> = FastifyRequest<RequestGenericInterface, TServer, TRawRequest>, TReply extends FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse> = FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse>, TInstance extends FastifyInstance<TServer, TRawRequest, TRawResponse> = FastifyInstance<TServer, TRawRequest, TRawResponse>> extends AbstractHttpAdapter<TServer, TRequest, TReply> { protected readonly logger: Logger; protected readonly instance: TInstance; protected _pathPrefix?: string; private readonly openConnections; private isClosing; private isShuttingDown; private return503OnClosing; private _isParserRegistered; private readonly registeredContentTypes; private onRequestHook?; private onResponseHook?; private securityHook?; private corsHook?; private isMiddieRegistered; private multipartMode; private multipartOptions; private isMultipartRequested; private pendingMiddlewares; private readonly middieContexts; private versioningOptions?; private readonly versionExtractorErrors; private readonly versionConstraint; get isParserRegistered(): boolean; constructor(instanceOrOptions?: TInstance | FastifyHttp2Options<any> | FastifyHttp2SecureOptions<any> | FastifyHttpsOptions<any> | FastifyHttpOptions<any> | FastifyAdapterBaseOptions<TServer>); private runLifecycleHook; setOnRequestHook(hook: (request: TRequest, reply: TReply, done: (err?: Error) => void) => void | Promise<void>): void; setOnResponseHook(hook: (request: TRequest, reply: TReply, done: (err?: Error) => void) => void | Promise<void>): void; init(): Promise<void>; listen(port: string | number, callback?: () => void): void; listen(port: string | number, hostname: string, callback?: () => void): void; private isValidPort; get(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; post(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; head(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; delete(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; put(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; patch(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; options(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; search(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; query(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; propfind(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; proppatch(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; mkcol(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; copy(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; move(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; lock(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; unlock(...args: any[]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; all(...args: any[]): any; applyVersionFilter(handler: Function, version: VersionValue, versioningOptions: VersioningOptions): VersionedRoute<TRequest, TReply>; reply(response: TRawResponse | TReply, body: any, statusCode?: number): FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>; status(response: TRawResponse | TReply, statusCode: number): any; end(response: TRawResponse | TReply, message?: string): void; render(response: TReply & { view: Function; }, view: string, options: any): FastifyReply<RouteGenericInterface, RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>; redirect(response: TReply, statusCode: number, url: string): FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>; setErrorHandler(handler: Parameters<TInstance['setErrorHandler']>[0]): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProvider>; setNotFoundHandler(handler: Function): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>; getHttpServer<T = TServer>(): T; getInstance<T = TInstance>(): T; register<TRegister extends Parameters<FastifyRegister<FastifyInstance<TServer, TRawRequest, TRawResponse>>>>(plugin: TRegister['0'], opts?: TRegister['1']): any; /** * Registers the "@fastify/multipart" plugin, unless the adapter's * `multipart` option is `false`, or the plugin has been registered * already by the time it loads. Idempotent. Called by the upload * interceptors from "@nestjs/platform-fastify/multipart". * * The plugin is loaded lazily, and a missing package fails the * application's startup (`ready()`) with an error naming the package, * rather than exiting the process. */ useMultipart(): void; inject(): LightMyRequestChain; inject(opts: InjectOptions | string): Promise<LightMyRequestResponse>; beforeClose(): void; close(): Promise<void>; initHttpServer(options?: NestApplicationOptions): void; useStaticAssets(options: FastifyStaticOptions): any; setViewEngine(options: FastifyViewOptions | string): any; isHeadersSent(response: TRawResponse | TReply): boolean; getHeader(response: TRawResponse | TReply, name: string): string | number | string[] | undefined; setHeader(response: TRawResponse | TReply, name: string, value: string): void | http.ServerResponse<http.IncomingMessage> | FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>; appendHeader(response: TRawResponse | TReply, name: string, value: string): void | http.ServerResponse<http.IncomingMessage> | FastifyReply<RouteGenericInterface, TServer, TRawRequest, TRawResponse, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>; getRequestHostname(request: TRequest): string; getRequestMethod(request: TRequest): string; getRequestUrl(request: TRequest): string; getRequestUrl(request: TRawRequest): string; enableCors(options?: FastifyCorsOptions): void; /** * Copies the headers `@fastify/cors` set on the reply to the Node.js * response, so that a response a middleware ends itself, or the error * response of a middleware that throws (sent through a new reply), carries * them too. Fastify merges them into the response it sends, in which the * reply's own values win. */ private copyCorsHeadersToRaw; /** * Runs the request hook of the built-in HTTP security features in an * `onRequest` hook added by the constructor: before middie (Nest middleware * and `app.use()` middleware, whenever they are registered), content-type * parsing, guards and handlers, and also for unmatched routes. Headers set * by the hook go to the Node.js response (`reply.raw`): Fastify merges them * into every response it sends, errors and `404`s included, while * `reply.header()` / `@Header()` values take precedence, and responses * written to `reply.raw` directly (e.g. `@Sse()`, or middleware) carry them * too. A rejection goes to `done(error)`, i.e. to the Nest exception layer * installed with `setErrorHandler()`. */ registerSecurityHook(hook: SecurityRequestHook<TRequest>): void; registerParserMiddleware(prefix?: string, rawBody?: boolean): void; useBodyParser(type: string | string[] | RegExp, rawBody: boolean, options?: NestFastifyBodyParserOptions, parser?: FastifyBodyParser<Buffer, TServer>): void; /** * Runs a parser given to `useBodyParser()`, like `runLifecycleHook()` runs * a hook: it completes once, a synchronous throw is passed to `done`, and so * is the rejection of the promise the parser returns, which Fastify would * not see (the content type parser above returns nothing). A parser that * declares `done` completes through it; the value any other one resolves * with is the parsed body. The promise is not returned to Fastify, which * would call `done` a second time for an async parser that calls it. */ private runBodyParser; createMiddlewareFactory(requestMethod: RequestMethod): Promise<(path: string, callback: Function) => any>; /** * Matches the paths of a middleware (path-to-regexp syntax) with a request * path as `sanitizeUrl()` returns it, the way the router matches the route * of the same path. `isMatchedByMiddie` tells whether the RegExp of * @fastify/middie, which sees the request path in another form, matches the * same paths; the path must be matched with `matches()` otherwise. */ private createPathMatcher; private runMiddleware; getType(): string; isRouteOrderSensitive(): boolean; getRouteMatchingOptions(): { caseSensitive: boolean; ignoreTrailingSlash: boolean; removesTrailingSlash: boolean; maxParamLength: number; useSemicolonDelimiter: boolean; caseSensitiveParamSuffix: boolean; }; /** * Returns the "maxParamLength" the router uses. The one of "routerOptions" * wins, but "initialConfig" fills it in with the default (100) when only * the top-level option is set, so the lower one is taken then. A value of * 0 means the default, as for find-my-way. */ private getMaxParamLength; use(...args: any[]): any; private wrapMiddleware; mapException(error: unknown): unknown; private isHttpFastifyError; protected registerWithPrefix(factory: FastifyPluginCallback<any> | FastifyPluginAsync<any> | Promise<{ default: FastifyPluginCallback<any>; }> | Promise<{ default: FastifyPluginAsync<any>; }>, prefix?: string): FastifyInstance<TServer, TRawRequest, TRawResponse, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault> & PromiseLike<undefined> & { __linterBrands: "SafePromiseLike"; }; /** * Fastify pipes a stream into the response, whose write() throws for a chunk * that is neither a string nor bytes, inside the "data" listener of pipe(), * where nothing catches it. A source in object mode (e.g. "Readable.from()" * over values) is checked as it is read instead, by a stream that Fastify * sends in its place, and that fails on such a chunk before writing it. */ private toWritableChunks; /** * A reply around a raw response, e.g. the one middleware and the exception * filters of its errors get. When a stream it sends fails or the client goes * away, Fastify reads the request, server and logger of the reply, and runs * its error handler if no data was sent yet: these are those of the instance. */ private createReplyForRawResponse; private isNativeResponse; private registerJsonContentParser; private registerUrlencodedContentParser; private hasCustomBodyParser; private normalizeContentType; private getDefaultBodyParser; private registerMiddie; private trackMiddieContexts; private getRequestOriginalUrl; private injectRouteOptions; private addHttpMethod; /** * "find-my-way" matches an empty path segment for a route parameter * ("/users//profile" -> { id: "" }), while "path-to-regexp", which * middleware paths (and the ExpressAdapter's routes) are matched with, * needs at least one character per parameter. Requests with an empty route * parameter are passed to the not-found handler instead, as with the * ExpressAdapter, so that middleware and routes always match the same * requests. Only the wildcard ("*") may be empty. */ private requireNonEmptyParams; /** * For a route path whose parameters "find-my-way" does not read as * path-to-regexp does, returns a function that matches the path of a * request as the middleware of that path is matched (see * `createMiddlewareFactory()`), which needs at least one character per * parameter: * - "find-my-way" keeps one value per parameter name, so for a route path * that uses a name more than once ("/projects/:id/tasks/:id"), * "request.params" only holds the last value, and an empty earlier one * cannot be seen there; * - it ends a parameter name at "-", "." or "/" only, so ":lat,:lng" is a * single parameter that matches any segment ("/geo/12"), which is also * logged when the route is registered. * A route path that path-to-regexp cannot read gets no such function, as * `createMiddlewareFactory()` does not take that path either. */ private createRouteParamsMatcher; /** * Fastify still accepts the router options ("ignoreTrailingSlash", * "caseSensitive", ...) at the top level, but "initialConfig.routerOptions" * only reflects them when they are passed through "routerOptions". As the * adapter always passes "routerOptions" (for the version constraint), the * top-level values are folded in so that plugins relying on * "initialConfig.routerOptions" (like @fastify/middie) normalize request * paths exactly like the router does. */ private getTopLevelRouterOptions; private sanitizeUrl; private removeDuplicateSlashes; private trimLastSlash; /** * Mirrors the absolute-form request target handling of "find-my-way". * Returns the path of an absolute-form target ("http://host/path" -> "/path", * "http://host?a=b" -> "/?a=b") and leaves any other request target * untouched. */ private getPathFromRequestTarget; /** * Whether the request target is in origin-form ("/users") or in the * absolute-form of an "http(s)" URL ("http://host/users"), the only forms * middleware paths can match. * * Node.js accepts other targets too (e.g. "GET *users HTTP/1.1"). * "find-my-way" routes those as if their first character were the leading * "/" ("*users" is served by the "/users" route), while no middleware path * matches them, so the route would run without its middleware. They are * answered by the not-found handler instead, as with the ExpressAdapter, * whose router never matches a path that does not start with "/". * * That includes the asterisk-form "OPTIONS *" (RFC 9112, section 3.2.4), * which asks about the server as a whole rather than about a resource: * Nest has no handler for it, and "find-my-way" would serve it with the * "OPTIONS /" route. */ private isRoutableRequestTarget; private trackOpenConnections; private closeOpenConnections; } export {};