Skip to content

Fastify

You should build your application by registering routes on a FastifyInstance returned from a buildApp() factory. The seed exposes a single GET / route that returns "Hello, World!" — extend it by adding more routes, plugins, and decorators.

Entry file

The solution files are organized under src/:

  • src/main.ts — the bootstrap script that calls buildApp() and starts listening on port 3000 (preloaded, hidden, read-only).
  • src/app.ts — the buildApp() factory where you register routes and plugins (editable solution file).

Splitting the factory from the listener lets you reuse buildApp() in tests with app.inject() instead of binding to a real port.

Server configuration for API Tester

The server is started by src/main.ts (buildApp().listen({ port: 3000, host: '0.0.0.0' })) so it is running when you use the API Tester tab. The API Tester sends real HTTP requests against your Fastify app and renders the response.

typescript
import { buildApp } from './app';

const app = buildApp();

app.listen({ port: 3000, host: '0.0.0.0' }).catch((error) => {
  console.error('Server failed to start:', error);
  process.exit(1);
});

Version

Running on Node.js v22.0.0 with Fastify 5.

Supported languages

TypeScript

Testing framework

Vitest

Special reminders and implementation details

  • Route handlers should be async whenever they do I/O — Fastify will await the returned promise and serialize the resolved value as the response body.
  • Don't both call reply.send() AND return a value — Fastify will warn about double-reply. Pick one style per handler.
  • Tests use Fastify's built-in app.inject() HTTP injector so you don't have to actually bind to a port. Always await app.close() in afterEach so ports don't leak across spec files.

Example with Vitest + app.inject():

typescript
import { describe, it, expect, afterEach } from 'vitest';
import type { FastifyInstance } from 'fastify';
import { buildApp } from './src/app';

describe('GET /', () => {
  let app: FastifyInstance;

  afterEach(async () => {
    await app?.close();
  });

  it("returns 'Hello, World!' with status 200", async () => {
    app = buildApp();
    const response = await app.inject({ method: 'GET', url: '/' });
    expect(response.statusCode).toBe(200);
    expect(response.body).toBe('Hello, World!');
  });
});

Included libraries

How to debug

Fastify is a backend framework — your code runs in Node, not the browser. Three main ways to inspect runtime behavior:

1. Logs from route handlers

Use request.log or console.log. To use the request logger, enable the logger when building the app:

typescript
const app = Fastify({ logger: true });

app.get('/items', async (request) => {
  request.log.info('GET /items called');
  return [];
});

Output appears in the dev server terminal (or in the embedded Preview's debug panel).

2. API Tester

Use the API Tester tab in the right panel to send real HTTP requests (GET / POST / PUT / PATCH / DELETE) to your running Fastify server. Pick a method, type a path (e.g. /items), add headers/params/body, and hit Send.

3. Steps to Debug Using Console Logs

  1. Identify the Problem Area: Decide whether the issue is in a route handler, a plugin, or the buildApp() wiring.

  2. Insert console.log() Statements: Add log calls before and after suspicious code. All Fastify logs go to the server terminal.

  3. Check the Results Output of your tests

  4. Use Debug Icon in Preview Area: Click on the debug icon in the Preview area to check the terminal output.

  5. Analyze the Output: Compare logs against the API Tester responses to spot wrong values, missed routes, or unhandled errors.

Common pitfalls

  • Forgetting async on a handler that does I/O → return values get serialized before the promise resolves. Always declare async handlers as async (request, reply) => { ... }.
  • Calling reply.send() AND returning a value → Fastify will warn about double-reply. Pick one style per handler.
  • Not closing the app in tests → port leaks accumulate across spec files. Always await app.close() in afterEach / afterAll.