---
title: "Fastify security middleware: quickstart · camada"
description: "Keep credential stuffers, scrapers and exploit scanners out of your Fastify app: npm i @camada/node, two lines and one env var."
url: https://www.camada.app/docs/quickstart/fastify
last_updated: 2026-10-30
---

Quickstart

# Fastify quickstart

Package `@camada/node`, on npm.

## 1. Install

```sh
npm i @camada/node
```

## 2. Environment

The key is printed once when you create a project in the app. Set these as environment variables: export them in the shell that starts your app, or keep them in .env and start it with node --env-file=.env (Node 20.6+). Node reads no .env file on its own.

shell

```sh
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787   # dev only; defaults to production ingest
```

## 3. Wire it in

```js
import { camadaFastify } from '@camada/node/fastify';
await app.register(camadaFastify);
```

- Register the plugin at the root instance so every route is covered.

## 4. Add the beacon to HTML pages

Optional but recommended. The beacon cuts false positives by showing which sessions are real browsers. Use the helper, or `<CamadaBeacon/>` from @camada/react.

```js
import camada from '@camada/node';
reply.type('text/html').send(`<head>${camada.scriptTag(request.raw)}</head>…`);
```

## 5. Send app outcomes

Login failures, signups and checkout declines are the signals no edge position sees. The identifier is HMAC-hashed in your process. How they catch [credential stuffing](https://www.camada.app/use-cases/credential-stuffing).

```js
import camada from '@camada/node';
camada.track(request.raw, 'login_failed', { user: email });
```

Runtime

## What it does per request

1. Refreshes the blocklist snapshot off-path (30 s poll with ETag). A cold start fails open.
2. Resolves the client IP per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it.
3. A blocked IP or path gets a 403 with x-block-reason before your app runs. The event still ships.
4. Serves /_cam/b.js (the beacon, first-party) and relays /_cam/fp posts to ingest with the resolved client IP.
5. Sets x-rid and the _sfp session cookie, and on response finish ships one batched, redacted event.

Limits

## Caveats at this position

- Enforcement at this position covers IP and path entries. ASN and TLS-fingerprint entries fail open in-app.
- Memory: about 5 MB per loaded snapshot.
- CAMADA_SERVERLESS=1 switches to lazy refresh with no interval timer.

Kill switch: `CAMADA_DISABLED=1`, checked per request. All other variables are listed in the [docs index](https://www.camada.app/docs).

## Sitemap

[Every page on this site](https://www.camada.app/sitemap.md)
