@miqro/core
v5.1.3
Published
promise based http router with TypeScript inference.
Readme
@miqro/core
promise based http router with TypeScript inference.
Router
import { Router } from "@miqro/core";
const router = new Router();
router.use(handler); // all methods, all paths
router.use(handler, "/path"); // all methods, /path
router.use(handler, "/path", "GET"); // GET /path
router.get("/path", handler); // GET /path
router.catch(errorHandler); // error handler
await router.run(req, res); // returns true if no response senthandler return values:
return false; // stop chain, no response sent
return true; // continue chain
return undefined; // continue chain
return { ... }; // pushed to req.results, continue chainApp
http.createServer with router built in.
import { App } from "@miqro/core";
const app = new App({
loggerFactory: (uuid, req) => logger,
onUpgrade: (req, socket, head) => { } // WebSocket upgrade
});
app.use(router);
await app.listen("3000");
await app.close();APIRoute
complete endpoint declaration.
import { APIRoute, JSONParser } from "@miqro/core";
export default {
name: "create post",
description: "creates a post",
path: "/posts",
method: "POST",
policy: {
groups: ["editor"],
groupPolicy: "at_least_one"
},
middleware: [JSONParser()],
request: {
body: {
title: "string",
content: "string",
tags: "string[]?"
},
query: {
draft: "boolean?"
}
},
response: {
status: [200, 400],
body: {
id: "number"
}
},
handler: async (req, res) => {
//req.body.title // is coerced to string
return res.json({ id: 1 });
}
} as APIRoute;APIRoute with TypeScript inference (experimental)
complete endpoint declaration.
import { defineRoute, JSONParser } from "@miqro/core";
export default defineRoute({
name: "create post",
description: "creates a post",
path: "/posts",
method: "POST",
policy: {
groups: ["editor"],
groupPolicy: "at_least_one"
},
middleware: [JSONParser()],
request: {
body: {
title: "string",
content: "string",
tags: "string[]?"
},
query: {
draft: "boolean?"
}
},
response: {
status: [200, 400],
body: {
id: "number"
}
},
handler: async (req, res) => {
//req.body.title // is coerced and typed string
return res.json({ id: 1 });
}
});method: "use" matches all methods — use for middleware:
export default {
path: "/admin",
method: "use",
handler: adminRouter
} as APIRoute;middleware
JSONParser
parses application/json body into req.body.
import { JSONParser } from "@miqro/core";
router.use(JSONParser());URLEncodedParser
parses application/x-www-form-urlencoded into req.body.
import { URLEncodedParser } from "@miqro/core";
router.use(URLEncodedParser());ReadBuffer
reads raw body into req.buffer.
import { ReadBuffer } from "@miqro/core";
router.use(ReadBuffer());TextParser
reads body as string into req.body.
import { TextParser } from "@miqro/core";
router.use(TextParser());CORS
import { CORS } from "@miqro/core";
router.use(CORS({
origins: ["https://myapp.com"],
methods: "GET,POST,PUT,DELETE"
}));without options reads CORS_ORIGINS, CORS_METHODS env vars. default: all origins.
invalid origins throw BadRequestError.
SessionHandler
verifies token and sets req.session.
import { SessionHandler } from "@miqro/core";
router.use(SessionHandler({
verify: async ({ token }) => {
// return session or null
}
}));token location via env vars: TOKEN_VERIFY_LOCATION (header|query|cookie), TOKEN_HEADER, TOKEN_QUERY, TOKEN_COOKIE.
or via remote endpoint: TOKEN_VERIFY_ENDPOINT.
GroupPolicyHandler
restricts access by req.session.groups.
import { GroupPolicyHandler } from "@miqro/core";
router.use(GroupPolicyHandler({
groups: ["admin", "editor"],
groupPolicy: "at_least_one" // or "all"
}));throws ForbiddenError if policy not met.
ParseRequest
validates req.body, req.query, req.params, req.headers against schema.
import { ParseRequest } from "@miqro/core";
router.use(ParseRequest({
body: { title: "string", count: "number?" },
query: { page: "number?" },
params: { id: "integer" }
}));throws BadRequestError if validation fails.
errors
import {
BadRequestError, // 400
UnAuthorizedError, // 401
ForbiddenError, // 403
NotFoundError, // 404
MethodNotImplementedError // 501
} from "@miqro/core";
// throw from any handler
throw new BadRequestError("invalid input");
throw new UnAuthorizedError();
throw new ForbiddenError();caught by router.catch() handlers or the default error handler.
Logger
import { Logger, ConsoleTransport, FileTransport } from "@miqro/core";
const logger = new Logger("MY_SERVICE", "info", {
transports: [ConsoleTransport(), FileTransport("server.log")]
});
logger.error("message %s", value);
logger.warn("...");
logger.info("...");
logger.debug("...");
logger.trace("...");level override via env: LOG_LEVEL=debug, LOG_LEVEL_MY_SERVICE=trace.
Request
import { Request } from "@miqro/core";
// all properties set before handlers run
req.path // normalized pathname
req.query // { [key]: string | string[] } — Object.create(null)
req.params // { [key]: string } — Object.create(null)
req.cookies // { [name]: string } — Object.create(null)
req.uuid // randomUUID()
req.startMS // Date.now() at request start
req.logger // per-request logger
req.results // []
req.body // set by body parser middleware
req.buffer // set by ReadBuffer
req.session // set by SessionHandlerStatic
serve static files.
import { Static } from "@miqro/core";
router.use(new Static({
directory: "./public",
autoIndex: true
}));Proxy
proxy requests to another server.
import { Proxy } from "@miqro/core";
router.use(new Proxy({
url: "http://backend:3001"
}));