UNPKG

@athenna/http

Version:

The Athenna Http server. Built on top of fastify.

249 lines (248 loc) 7.24 kB
/** * @athenna/http * * (c) João Lenon <lenon@athenna.io> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import fastify from 'fastify'; import { Options, Macroable } from '@athenna/common'; import { FastifyHandler } from '#src/handlers/FastifyHandler'; export class ServerImpl extends Macroable { constructor(options) { super(); this.fastify = fastify.fastify({ ...options, exposeHeadRoutes: false }); this.isListening = false; this.fastify.decorateReply('body', null); this.fastify.decorateRequest('data', null); } /** * Get the representation of the internal radix tree used by the * router. */ getRoutes(options) { return this.fastify.printRoutes(options); } /** * Get the address info of the server. This method will return the * port used to listen the server, the family (IPv4, IPv6) and the * server address (127.0.0.1). */ getAddressInfo() { return this.fastify.server.address(); } /** * Get the port where the server is running. */ getPort() { return this.getAddressInfo()?.port; } /** * Get the host where the server is running. */ getHost() { return this.getAddressInfo()?.address; } /** * Get the fastify version that is running the server. */ getFastifyVersion() { return this.fastify.version; } /** * Return the swagger documentation generated by routes if using the * @fastify/swagger plugin in the server. If the plugin is not installed * will return null. */ async getSwagger(options) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const swagger = this.fastify.swagger; if (!swagger) { return null; } await this.fastify.ready(); return swagger(options); } /** * Set the error handler to handle errors that happens inside the server. */ setErrorHandler(handler) { this.fastify.setErrorHandler(FastifyHandler.error(handler)); this.fastify.setNotFoundHandler(FastifyHandler.notFoundError(handler)); return this; } /** * Register a plugin inside the fastify server. */ async plugin(plugin, options) { await this.fastify.register(plugin, options); } /** * Create a middleware that will be executed before the request gets * inside the route handler. */ middleware(handler) { this.fastify.addHook('preHandler', FastifyHandler.handle(handler)); return this; } /** * Create an interceptor that will be executed before the response * is returned. At this point you can still make modifications in the * response. */ intercept(handler) { this.fastify.addHook('onSend', FastifyHandler.intercept(handler)); return this; } /** * Create and terminator that will be executed after the response * is returned. At this point you cannot make modifications in the * response. */ terminate(handler) { this.fastify.addHook('onResponse', FastifyHandler.terminate(handler)); return this; } /** * Return a request handler to make internal requests to the Http server. */ request(options) { if (!options) { return this.fastify.inject(); } return this.fastify.inject(options); } /** * Make the server start listening for requests. */ async listen(options) { return this.fastify.listen(options).then(() => (this.isListening = true)); } /** * Return the FastifyVite instance if it exists. */ getVitePlugin() { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (!this.fastify.vite) { return; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return this.fastify.vite; } /** * Return ViteDevServer instance if it exists. */ getViteDevServer() { const vite = this.getVitePlugin(); if (!vite) { return; } return vite.getServer(); } /** * Start vite server. */ async viteReady() { const vite = this.getVitePlugin(); if (!vite) { return; } return vite.ready(); } /** * Close the server, */ async close() { if (!this.isListening) { return; } await this.fastify.close().then(() => (this.isListening = false)); } /** * Add a new route to the http server. */ route(options) { if (!options.middlewares) { options.middlewares = {}; } options.middlewares = Options.create(options.middlewares, { middlewares: [], terminators: [], interceptors: [] }); if (options.methods.length === 2 && options.methods.includes('HEAD')) { this.route({ ...options, methods: ['GET'] }); this.route({ ...options, methods: ['HEAD'], fastify: { ...options.fastify, schema: { ...options.fastify.schema, hide: true } } }); return; } const { middlewares, interceptors, terminators } = options.middlewares; const route = { url: options.url, method: options.methods, handler: FastifyHandler.request(options.handler) }; if (middlewares.length) { route.preHandler = middlewares.map(m => FastifyHandler.handle(m)); } if (interceptors.length) { route.onSend = interceptors.map(i => FastifyHandler.intercept(i)); } if (terminators.length) { route.onResponse = terminators.map(t => FastifyHandler.terminate(t)); } this.fastify.route({ ...route, ...options.fastify }); } /** * Add a new GET route to the http server. */ get(options) { this.route({ ...options, methods: ['GET'] }); } /** * Add a new HEAD route to the http server. */ head(options) { this.route({ ...options, methods: ['HEAD'] }); } /** * Add a new POST route to the http server. */ post(options) { this.route({ ...options, methods: ['POST'] }); } /** * Add a new PUT route to the http server. */ put(options) { this.route({ ...options, methods: ['PUT'] }); } /** * Add a new PATCH route to the http server. */ patch(options) { this.route({ ...options, methods: ['PATCH'] }); } /** * Add a new DELETE route to the http server. */ delete(options) { this.route({ ...options, methods: ['DELETE'] }); } /** * Add a new OPTIONS route to the http server. */ options(options) { this.route({ ...options, methods: ['OPTIONS'] }); } }