UNPKG

@ffsm/napi

Version:

Napi - A framework using with Next.js for building APIs.

174 lines (173 loc) 6.14 kB
import { sync } from "./helpers/sync"; import { NapiMethod } from "./enums"; import { NapiRouter } from "./router"; import { matchRoute } from "./helpers/match-route"; import { NapiResponse } from "./response"; import { NapiController } from "./controller"; import { NapiContext } from "./context"; import { NapiConfiguration } from "./configuration"; export class NapiApplication { constructor(options) { this.options = options; this.prefix = ""; this.route = ""; this.params = {}; // Detech from route if (!NapiApplication.controllers) { NapiApplication.controllers = new Map(); } } getRequest() { return this.req; } getRouter() { if (!this.router) { this.initRouter(); } return this.router; } getPayload() { return this.payload; } getController() { return this.controller.getInstance(); } getConfig(name) { return this.configuration.get(name); } getResponse() { if (!this.res) { this.initResponse(); } return this.res; } get method() { return this.req.method.toUpperCase(); } createExcutionContext() { return { getRequest: this.getRequest.bind(this), getRouter: this.getRouter.bind(this), getPayload: this.getPayload.bind(this), getController: this.getController.bind(this), getConfig: this.getConfig.bind(this), getResponse: this.getResponse.bind(this), }; } async initializeConfiguration() { var _a; this.configuration = await NapiConfiguration.initialize((_a = this.options.configs) !== null && _a !== void 0 ? _a : {}); } async extractPrefix() { if (!this.options.prefix) { return this; } const prefix = this.options.prefix; if (typeof prefix === "function") { this.prefix = await prefix(this.configuration); } else if (typeof prefix === "string") { this.prefix = prefix; } } async extractRoute() { const params = await this.payload.params; if (!params) { this.route = ""; return this; } const route = Object.values(params)[0]; this.route = route ? route.join("/") : ""; return this; } initRouter() { if (this.router) { return this; } this.router = new NapiRouter(this.req, this.route, this.params); return this; } async loadController() { var _a; const controllers = (_a = this.options.controllers) !== null && _a !== void 0 ? _a : []; if (!controllers.length) { return; } for (let index = 0; index < controllers.length; index++) { const controller = controllers[index]; if (!NapiController.has(controller)) { continue; } const instance = NapiController.get(controller); const name = instance.getName(); if (!instance.hasPrefix()) { throw new Error(`Please using @Controller() decorator on ${name}`); } const prefix = instance.getPrefix(); const controllerRoute = `/${this.prefix}/${prefix}/`.replace(/\/+/g, "/"); const requestRoute = `/${this.route}/`.replace(/\/+/g, "/"); if (!requestRoute.startsWith(controllerRoute)) { continue; } this.controller = instance; break; } } loadMethods() { if (!this.controller) { return; } if (!this.controller.hasRoutes()) { // TODO: throw error no routes decorated return; } const routes = this.controller.getRoutes(); const routeKeys = Array.from(routes.keys()); for (let index = 0; index < routeKeys.length; index++) { const methodName = routeKeys[index]; const [method, ...remain] = methodName.split(":"); if (method.toUpperCase() !== this.method) { continue; } const route = `/${this.prefix}/${this.controller.getPrefix()}/${remain.join(":")}`.replace(/\/+/g, "/"); const params = matchRoute(route, this.route); if (!params) { continue; } this.controllerMethod = routes.get(methodName); this.params = params; } } async loadParameters() { this.context = new NapiContext(this.prefix, this.controller, this.createExcutionContext(), this.params); this.params = (await this.context.execute(this.route)).getRouteParams(); } initResponse() { this.res = new NapiResponse(this.controller, this.context, this.createExcutionContext()); } async execute() { if (!this.controllerMethod) { return new Response(null); } const instance = this.controller.getInstance(); const method = instance[this.controllerMethod]; const params = this.context.getDecoratorParams(); const result = await method.bind(instance)(...params); return this.res.send(result); } async dispatch(req, payload) { this.req = req; this.payload = payload; await sync([this.initializeConfiguration.bind(this)], [this.extractPrefix.bind(this)], [this.extractRoute.bind(this)], [this.loadController.bind(this)], [this.loadMethods.bind(this)], [this.loadParameters.bind(this)], [this.initRouter.bind(this)], [this.initResponse.bind(this)]); return await this.execute(); } static register(options) { return Object .values(NapiMethod) .filter((method) => typeof method === "string") .reduce((acc, method) => { const upper = method.toUpperCase(); acc[upper] = (req, payload) => (new NapiApplication(options).dispatch(req, payload)); return acc; }, {}); } }