@athenna/http
Version:
The Athenna Http server. Built on top of fastify.
249 lines (248 loc) • 7.24 kB
JavaScript
/**
* @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'] });
}
}