mtrac — open-source tooling by Michael Tracy
open-source tooling by Michael Tracy
scroll ↓// express-openapi-joi
Define it once.
One route definition. Validation, types and API docs come out of it, so none of them can drift from the others.
widgets.ts
defineRoute(router, {
method: 'post',
path: '/:id',
prefix: '/widgets',
request: {
params: Joi.object({ id: Joi.string().uuid().required() }),
body: Joi.object<{ name: string }>({ name: Joi.string().required() }),
},
responses: { 201: { description: 'Created' } },
handler: (req, res) => {
// req.params.id and req.body.name are both typed
res.status(201).json({ id: req.params.id, name: req.body.name });
},
});
Validated runtime
POST /widgets/not-a-uuid 400 { "message": "\"id\" must be a valid GUID", "details": [{ "type": "string.guid", … }] }
Typed compile time
req.params.id // string req.body.name // string req.body.nmae // ✗ Property 'nmae' does not exist
Documented GET /openapi.json
"/widgets/{id}": { "post": { "operationId": "postWidgetsId", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], "additionalProperties": false } } } }, "responses": { "201": { "description": "Created" } } } }