# Clerk on Fastify
## Project shape
The Clerk Fastify guide assumes ECMAScript modules: `"type": "module"` in package.json. Mixing `require()` with the ESM-only imports is the first failure.
Env vars (`CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`) must be available before any Clerk module initializes. Run with `node --env-file=.env`, or load your env loader before importing `@clerk/fastify`. Same rule as Express: Clerk reads keys at import time.
## Register the plugin, then protect routes
```ts
import Fastify from 'fastify'
import { clerkClient, clerkPlugin, getAuth } from '@clerk/fastify'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin)
fastify.get('/protected', async (request, reply) => {
const { isAuthenticated, userId } = getAuth(request)
if (!isAuthenticated) {
return reply.code(401).send({ error: 'User not authenticated' })
}
const user = await clerkClient.users.getUser(userId)
return reply.send({ message: 'ok', user })
})
```
Notes:
- `clerkPlugin()` is a Fastify plugin. Register it before the routes that need auth; Fastify plugin encapsulation means registration order and scope matter.
- `getAuth(request)` (not `req`) returns the Auth object. Same as Express, it returns state, it does not send the 401 for you.
- The docs show registering the plugin for all routes, with per-route scoping covered in the clerkPlugin() reference for when only some routes need auth.
## Checklist
- `fastify.register(clerkPlugin)` with no call parens: it is a plugin function, not middleware factory output.
- TypeScript: `getAuth(request)` is typed against the Fastify request; do not pass a raw Node req.
- As with Express, `clerkClient.users.getUser()` is a Backend API call per invocation; cache or avoid in hot paths.