← ClaudeAtlas

fastifylisted

Enforce Fastify v5 plugin/route conventions for Node/TypeScript. Use when editing .ts/.js/.mjs that import fastify or @fastify/*, or when the user mentions Fastify, plugin, route, schema validation, encapsulation, fastify-plugin/fp, or lifecycle hooks. Forbids manual JSON validation when schema works, missing fp wrapper for cross-scope decorators, and app-root hooks that should be plugin-scoped.
zhaojiannet/canon · ★ 1 · API & Backend · score 77
Install: claude install-skill zhaojiannet/canon
This skill enforces Fastify v5 plugin/route conventions for Node and TypeScript projects. The rule: every plugin is encapsulated by default; expose to parent scope only when needed; routes declare schemas, not manual validation. Apply only when the file imports `fastify` or `@fastify/*`. If the project uses Express or Koa, **STOP** and ask the user before applying these rules. ## Plugin signature ```ts import type { FastifyPluginAsync } from 'fastify' export const userRoutes: FastifyPluginAsync = async (fastify, opts) => { fastify.get('/users/:id', { schema: getUserSchema }, async (req, reply) => { return { id: req.params.id } }) } ``` Use `async (fastify, opts) =>` over `(fastify, opts, done) =>`. Async return is the completion signal. ## fastify-plugin (fp) — when to wrap Wrap with `fp` when: - The plugin calls `fastify.decorate(...)` and parent or sibling scopes need access. - The plugin calls `fastify.addHook(...)` that should propagate to the whole app. - It registers a top-level shared resource (database connection, cache). Do **not** wrap when: - The plugin only registers routes (routes belong in their own encapsulated scope). - The decorators are intentionally local to the subtree. ```ts import fp from 'fastify-plugin' export default fp(async (fastify, opts) => { fastify.decorate('db', await createDb(opts)) }, { name: 'db-plugin' }) ``` ## Route schemas — required Every route declares JSON schema for `body`, `querystring`, `params`, and `respons