UNPKG

yup-server

Version:

A lightweight server that uses the amazing Yup library for validation

173 lines (172 loc) 7.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createServer = exports.useConnectMiddleware = void 0; const http_errors_1 = require("http-errors"); function executeRequestHandler({ requestHandler, req, res, pattern, ctx, }) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const { handler, bodySchema, paramsSchema, querySchema, responseSchema } = requestHandler; if (bodySchema) { let body = ""; req.on("data", function (chunk) { body += chunk; }); yield new Promise((resolve) => req.on("end", resolve)); let validatedBody; try { validatedBody = yield bodySchema.validate(JSON.parse(body)); } catch (err) { throw new http_errors_1.UnprocessableEntity(err.message); } ctx.body = validatedBody; } if (paramsSchema) { const regexResult = pattern.exec(((_a = req.url) === null || _a === void 0 ? void 0 : _a.split("?")[0]) || ""); if (!regexResult) { throw new http_errors_1.UnprocessableEntity("no url parameters found"); } let validatedParams; try { validatedParams = yield paramsSchema.validate(regexResult.groups); } catch (err) { throw new http_errors_1.UnprocessableEntity(err.message); } ctx.params = validatedParams; } if (querySchema) { const queryString = ((_b = req.url) === null || _b === void 0 ? void 0 : _b.split("?")[1]) || ""; const query = Object.fromEntries(new URLSearchParams(queryString).entries()); let validatedQuery; try { validatedQuery = yield querySchema.validate(query); } catch (err) { throw new http_errors_1.UnprocessableEntity(err.message); } ctx.query = validatedQuery; } let handlerRes = yield handler({ ctx, req, res }); // the handler took care of the response if (res.headersSent) return; // the handler didn't return any data to send so we don't return anything // this handler was likely middleware if (handlerRes === undefined) return; if (responseSchema) { // data from response shouldn't need to be parsed/coerced handlerRes = yield responseSchema.validate(handlerRes, { strict: true }); } // promisify and await so this function doesn't return until after response // is sent yield new Promise((resolve) => { res .writeHead(200, { "Content-Type": "application/json" }) .end(JSON.stringify(handlerRes), resolve); }); }); } /** * Generate a request handler function suitable for use with http.createServer() */ function createServer(routes) { return function requestHandler(req, res) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { try { const url = (_a = req.url) === null || _a === void 0 ? void 0 : _a.split("?")[0]; const method = (_b = req.method) === null || _b === void 0 ? void 0 : _b.toUpperCase(); if (!url || !method) throw new http_errors_1.ImATeapot(); // this should never happen const ctx = { body: undefined, user: undefined, query: undefined, params: undefined, }; for (let route of routes) { if (!route.pattern.test(url)) continue; const allRequestMethodsHandler = route["*"]; if (allRequestMethodsHandler) { yield executeRequestHandler({ requestHandler: allRequestMethodsHandler, req, res, pattern: route.pattern, ctx, }); } if (res.headersSent) return; const specificRequestMethodHandler = route[method]; if (!specificRequestMethodHandler) continue; yield executeRequestHandler({ requestHandler: specificRequestMethodHandler, req, res, pattern: route.pattern, ctx, }); if (res.headersSent) return; } throw new http_errors_1.NotFound(`${method} ${url}`); } catch (err) { if (process.env.NODE_ENV !== "production") console.log(err); if (res.headersSent) return; let message = err.message; const statusCode = err.statusCode || 500; if (statusCode >= 500 && process.env.NODE_ENV === "production") { message = "internal server error"; } res .writeHead(statusCode, { "Content-Type": "application/json" }) .end(JSON.stringify({ message })); } }); }; } exports.createServer = createServer; /** * Generate a request handler function that will execute connect-style middleware. * * @param connectMiddleware The middleware to execute * @param errorHandler The error handler to execute if the middleware returns an error * * @returns A request handler function * */ function useConnectMiddleware(connectMiddleware, errorHandler) { return function ({ req, res }) { return __awaiter(this, void 0, void 0, function* () { let middlewareError = yield new Promise((resolve) => { connectMiddleware(req, res, (err) => { resolve(err); }); }); if (middlewareError) { if (!errorHandler) { throw new http_errors_1.InternalServerError(`Middleware error: ${middlewareError}`); } yield errorHandler(middlewareError); } }); }; } exports.useConnectMiddleware = useConnectMiddleware;