Docs

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.

Note

If you're looking for a more complete example, check out our Fastify example app.

terminal
npm install @clerk/fastify
terminal
yarn add @clerk/fastify
terminal
pnpm add @clerk/fastify

Set environment variables

Below is an example of an .env.local file.

Pro tip! If you are signed into your Clerk Dashboard, your secret key should become visible by clicking on the eye icon. Otherwise, you can find your keys in the Clerk Dashboard on the API Keys page.

.env.local
CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY

This examples uses dotenv to load the environment variables. You can use any other library or method, if you so wish.

terminal
npm install dotenv
npm install -D @types/dotenv
terminal
yarn add dotenv
yarn add -D @types/dotenv
terminal
pnpm 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.ts
import * 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.ts
import * 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.ts
fastify.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.ts
import * 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();

Feedback

What did you think of this content?