Fastify → Postman collection
Fastify routes already carry JSON Schema, so they make good Postman requests. routeman-cli reads those schemas straight from your plugins, without @fastify/swagger and without starting the server, and writes the collection.
$ npx routeman-cli
What routeman-cli reads from a Fastify app
- Routes:
app.register(plugin, { prefix })at any depth,@fastify/autoload(folder prefixes and_paramdirectories),fastify-plugin, shorthand methods andapp.route({...}). - Schemas:
body,querystringandparamswritten as JSON Schema or TypeBox. Example values respectminLength/maxLength,minimum/maximum,enum, unions of literals,formatanddefault. - Names:
schema.summarybecomes the request name. - Auth:
@fastify/jwtwithrequest.jwtVerify(), inside a decorator or anonRequest/preHandlerhook. Plugins without the hook stay public.
Example
// src/app.js
const app = Fastify({ logger: true });
app.register(jwt, { secret: process.env.JWT_SECRET });
app.decorate('authenticate', async (request) => { await request.jwtVerify(); });
app.register(authRoutes, { prefix: '/v1/auth' });
app.register(noteRoutes, { prefix: '/v1/notes' });
app.listen({ port: 8080 });
// src/routes/notes.js
const Note = Type.Object({
title: Type.String({ minLength: 1, maxLength: 200 }),
body: Type.String(),
tags: Type.Array(Type.String(), { maxItems: 10 }),
pinned: Type.Boolean({ default: false }),
color: Type.Union([Type.Literal('yellow'), Type.Literal('blue'), Type.Literal('green')]),
});
export default async function noteRoutes(app) {
app.addHook('onRequest', app.authenticate);
app.get('/', { schema: { summary: 'List notes', querystring: Type.Object({
limit: Type.Integer({ minimum: 1, maximum: 100, default: 20 }),
tag: Type.Optional(Type.String()),
}) } }, listNotes);
app.post('/', { schema: { summary: 'Create a note', body: Note } }, createNote);
app.get('/:noteId', { schema: { params: Type.Object({ noteId: Type.String({ format: 'uuid' }) }) } }, getNote);
app.put('/:noteId', { schema: { summary: 'Replace a note', body: Note } }, replaceNote);
app.delete('/:noteId', deleteNote);
}
$ npx routeman-cli
✓ fastify: 6 requests (2 POST, 2 GET, 1 PUT, 1 DELETE)
✓ auth: bearer (login: POST /v1/auth/token)
✓ wrote postman/notes-api.postman_collection.json
✓ wrote postman/notes-api.local.postman_environment.json
done in 0.05s - import the files in Postman (File → Import)
$ npx routeman-cli routes
POST /v1/auth/token json: username, password
GET /v1/notes 🔒
POST /v1/notes 🔒 json: title, body, tags, pinned, color
DELETE /v1/notes/{noteId} 🔒
GET /v1/notes/{noteId} 🔒
PUT /v1/notes/{noteId} 🔒 json: title, body, tags, pinned, color
6 routes, auth: bearer (login: POST /v1/auth/token)
This is real output from routeman-cli 0.1.0. In Postman:
- Create a note sends
{"title": "Sample title", "body": "Sample text", "tags": ["sample"], "pinned": false, "color": "yellow"}. The union of literals gives a valid colour, and the boolean takes its default. - List notes has
limit=20switched on, because the parameter is required and has a default, andtagswitched off, because it'sType.Optional. {{noteId}}is a UUID in the environment, because the params schema saysformat: 'uuid'.- The
onRequesthook makes every notes route authenticated. Get an access token, named from itssummary, is the login request, and its script saves theaccessTokenfrom the response. base_urlishttp://localhost:8080, read fromapp.listen({ port: 8080 }).
Autoload projects
With @fastify/autoload, folder names become prefixes and _id folders become path parameters, just as Fastify loads them. If routeman-cli starts from the wrong file, pass --entry src/app.ts.