Fastify best practices skill
93
97%
Does it follow best practices?
Impact
85%
1.37xAverage score across 4 eval scenarios
Passed
No known issues
A fintech startup built a Fastify expense tracking API three months ago but shipped it with zero test coverage. The team has just hired a new backend engineer whose first task is to write a comprehensive test suite before any new features are added. The CI pipeline currently has a placeholder for tests but nothing runs.
The team tech lead wants the tests to be lightweight and fast — they should not spin up a real database or make actual network calls. The tests need to be runnable in CI without any additional tooling beyond what comes with Node.js. The tech lead specifically does not want to introduce Jest or any other test framework as a dependency since it slows down the install step and adds complexity.
The existing application source files are provided below. Write a complete test suite covering all routes, including validation error cases and not-found scenarios.
Produce the following files:
test/expenses.test.ts — the primary test file with all testspackage.json — updated with a test script that runs the testsThe test script in package.json should run the tests without any flags not available in Node.js 22+.
Ensure the tests can be run immediately after npm install.
The following files are provided as inputs. Extract them before beginning.
=============== FILE: src/app.ts =============== import Fastify from 'fastify' import type { FastifyInstance } from 'fastify' import { expenseRoutes } from './routes/expenses.js'
export async function buildApp(): Promise<FastifyInstance> { const app = Fastify({ logger: true }) await app.register(expenseRoutes) return app } =============== END FILE ===============
=============== FILE: src/routes/expenses.ts =============== import type { FastifyInstance } from 'fastify'
interface Expense { id: string description: string amount: number category: string date: string }
const expenses: Record<string, Expense> = {} let nextId = 1
export async function expenseRoutes(fastify: FastifyInstance) { fastify.get('/expenses', async () => { return { expenses: Object.values(expenses) } })
fastify.post('/expenses', { schema: { body: { type: 'object', properties: { description: { type: 'string', minLength: 1 }, amount: { type: 'number', exclusiveMinimum: 0 }, category: { type: 'string', minLength: 1 }, date: { type: 'string', format: 'date' }, }, required: ['description', 'amount', 'category', 'date'], additionalProperties: false, }, }, }, async (request, reply) => { const body = request.body as Omit<Expense, 'id'> const id = String(nextId++) expenses[id] = { id, ...body } reply.code(201) return expenses[id] })
fastify.get('/expenses/:id', async (request, reply) => { const { id } = request.params as { id: string } const expense = expenses[id] if (!expense) { reply.code(404) return { error: 'Expense not found' } } return expense })
fastify.put('/expenses/:id', { schema: { body: { type: 'object', properties: { description: { type: 'string', minLength: 1 }, amount: { type: 'number', exclusiveMinimum: 0 }, category: { type: 'string', minLength: 1 }, date: { type: 'string', format: 'date' }, }, additionalProperties: false, }, }, }, async (request, reply) => { const { id } = request.params as { id: string } if (!expenses[id]) { reply.code(404) return { error: 'Expense not found' } } const body = request.body as Partial<Omit<Expense, 'id'>> expenses[id] = { ...expenses[id], ...body } return expenses[id] })
fastify.delete('/expenses/:id', async (request, reply) => { const { id } = request.params as { id: string } if (!expenses[id]) { reply.code(404) return { error: 'Expense not found' } } delete expenses[id] reply.code(204) }) } =============== END FILE ===============
=============== FILE: package.json =============== { "name": "expense-tracker-api", "version": "1.0.0", "type": "module", "scripts": { "start": "node src/app.ts", "test": "echo 'no tests yet'" }, "dependencies": { "fastify": "^5.0.0" }, "devDependencies": { "@types/node": "^22.0.0", "typescript": "^5.0.0" } } =============== END FILE ===============