> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Fastify - Docs Copy page # Fastify - Docs Use the [PostHog Node.js SDK](/docs/libraries/node.md) to capture events and request errors from your [Fastify](https://fastify.dev/) app. ## Installation Install the SDK in your Fastify app: Terminal PostHog AI ```bash pnpm add posthog-node ``` This guide uses Fastify 5 and `posthog-node` 5.52.1 on Node.js. Get your project token and API host from your [project settings](https://us.posthog.com/settings/project). ## Set up PostHog Create one PostHog client for your app. Register the hooks and error handler in the same Fastify scope as your routes, before those routes. Process-level `enableExceptionAutocapture` does not capture errors that Fastify handles. Use `setErrorHandler` to capture these errors. server.mjs PostHog AI ```javascript import Fastify from "fastify" import { PostHog } from "posthog-node" const app = Fastify() const posthog = new PostHog("", { host: "https://us.i.posthog.com", }) // If you use authentication, register its onRequest hook before this hook. app.addHook("onRequest", (request, _reply, done) => { posthog.withContext( { distinctId: request.user?.id, properties: { $request_method: request.method, request_id: request.id }, }, done, { fresh: true } ) }) app.setErrorHandler((error, request, reply) => { // Match Fastify's status precedence before applying the capture policy. let statusCode = reply.statusCode >= 400 ? reply.statusCode : 500 if (error.status >= 400) { statusCode = error.status } else if (error.statusCode >= 400) { statusCode = error.statusCode } // Optional application policy: capture server errors only. if (statusCode >= 500) { posthog.captureException(error, undefined, { $response_status_code: statusCode, $request_path: request.routeOptions.url, }) } // Keep Fastify's error serialization. reply.code(statusCode).send(error) }) app.addHook("onClose", async () => { await posthog.shutdown() }) app.get("/example", async () => { throw new Error("Example request failed") }) await app.listen({ port: 3000, host: "127.0.0.1" }) ``` ## Request context and identity Keep `onRequest` callback-based, not `async`. Calling `done` inside [`withContext`](/docs/libraries/node.md#contexts) continues the request in that context. `{ fresh: true }` prevents inheritance from an outer context. `request.user` belongs to your application. It is not a built-in Fastify identity. Populate it with a verified user and a string `id` in an earlier authentication hook. Without authentication, this example captures exceptions without creating person profiles. If authentication runs later, pass the verified ID directly to `captureException` instead of `undefined`. Do not use `x-posthog-distinct-id` as proof of identity. Tracing headers are client-controlled analytics values, and this example does not read them. To link Session Replay, you can explicitly add a validated `sessionId` to the context. Never use it for authorization. ## Capture events Add this route before `app.listen()` to capture an event for an authenticated request. The event inherits the verified identity and request properties from `withContext`. JavaScript PostHog AI ```javascript app.get("/hello", async (request) => { if (request.user?.id) { posthog.capture({ event: "hello_requested" }) } return { message: "Hello" } }) ``` See the [Node.js capture documentation](/docs/libraries/node.md#capturing-events) for more event options. ## Error handling The HTTP status filter is optional application logic, not an SDK default. Remove the `if` condition to capture handled 4xx errors too. Unknown-route 404 responses do not pass through `setErrorHandler`. If you already have an error handler, add the capture call there instead of replacing your response behavior. Avoid capturing the same error again in another hook. The example records the route pattern, not the raw URL. This avoids collecting query strings or path parameter values. Review error messages and other properties for sensitive data before capture. Fastify's default error response includes the error message. Use your application's response policy to hide internal details. ## Shutdown During graceful termination, call `await app.close()` from your application's shutdown handler. Fastify then runs `onClose`, which waits for PostHog to drain buffered events. Do not call `shutdown()` per request. Do not exit the process before it finishes. Abrupt termination can lose queued events. ## Next steps See the [Node.js SDK documentation](/docs/libraries/node.md) for Feature Flags, group analytics, and other SDK features. ### Still have questions? Ask PostHog AI ### Was this page useful? HelpfulCould be better