Use Clerk with Fastify
Learn how to use Clerk to easily add authentication focused on security, speed, and DX to your Fastify server.
After following this guide, you should have a working Fastify app with public and private routes, authenticated using the clerkPlugin
and getAuth
helpers.
If you're looking for a more complete example, check out our Fastify example app(opens in a new tab).
Install @clerk/fastify
terminalnpm install @clerk/fastify
terminalyarn add @clerk/fastify
terminalpnpm add @clerk/fastify
Set environment keys
Below is an example of an .env
file. If you are signed into your Clerk Dashboard, your keys should become visible by clicking on the eye icon.
.envCLERK_PUBLISHABLE_KEY={{pub_key}} CLERK_SECRET_KEY={{secret}}
This examples uses dotenv
to load the environment variables. You can use any other library or method, if you so wish.
terminalnpm install dotenv npm install -D @types/dotenv
terminalyarn add dotenv yarn add -D @types/dotenv
terminalpnpm add dotenv pnpm add -D @types/dotenv
Configure clerkPlugin
in your Fastify application
The Clerk plugin can be registered globally or for specific routes. This examples registers the plugin globally.
index.tsimport * as dotenv from "dotenv"; dotenv.config(); import Fastify from "fastify"; import { clerkClient, clerkPlugin, getAuth } from "@clerk/fastify"; const fastify = Fastify({ logger: true }); fastify.register(clerkPlugin); const start = async () => { try { await fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();
Accessing auth state using getAuth
The getAuth
helper can be used to access the auth state of the current request.
index.tsimport * as dotenv from "dotenv"; dotenv.config(); import Fastify from "fastify"; import { clerkClient, clerkPlugin, getAuth } from "@clerk/fastify"; const fastify = Fastify({ logger: true }); fastify.register(clerkPlugin); fastify.get("/", async (req, reply) => { /** * Access the auth state for this request. * In this example, the userId loads the whole User object * from the Clerk servers */ const { userId } = getAuth(req); const user = userId ? await clerkClient.users.getUser(userId) : null; return { user }; }); const start = async () => { try { await fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();
Require authentication for a route
To protect a route using Clerk, you can use getAuth
to check if the user is authenticated. If the user is not authenticated, you can return a 403 error.
index.tsfastify.get("/protected", async (request, reply) => { const { userId } = getAuth(request); if (!userId) { return reply.code(403).send(); } const user = await clerkClient.users.getUser(userId); return { user }; });
Using Clerk for specific routes only
If you want to use Clerk for specific routes only, you can register the plugin for specific routes. In this example, the routes are split into two groups: one for public routes and one for private routes.
index.tsimport * as dotenv from "dotenv"; dotenv.config(); import Fastify, { FastifyPluginCallback } from "fastify"; import { clerkClient, clerkPlugin, getAuth } from "@clerk/fastify"; const fastify = Fastify({ logger: true }); /** * Register Clerk only for a subset of your routes */ const protectedRoutes: FastifyPluginCallback = (instance, opts, done) => { instance.register(clerkPlugin); instance.get("/protected", async (request, reply) => { const { userId } = getAuth(request); if (!userId) { return reply.code(403).send(); } const user = await clerkClient.users.getUser(userId); return { user }; }); done(); }; const publicRoutes: FastifyPluginCallback = (instance, opts, done) => { instance.get("/", async (request, reply) => { return { message: "This is a public endpoint. Request /protected to test the Clerk auth middleware" }; }); done(); }; /** * Register your routes as you normally would */ fastify.register(protectedRoutes); fastify.register(publicRoutes); const start = async () => { try { await fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();