This guide shows how to protect Fastify API routes using protectApi() from the MonoCloud Backend Node SDK.
You can protect routes globally, per-route, or combine both approaches with scope and group-based authorization.
This guide assumes you've completed the Fastify quickstart or the installation guide.
You should already have:
@monocloud/backend-node SDK installed.envApply protectApi() as an application-level hook to protect all routes.
import "dotenv/config";
import Fastify from "fastify";
import {
protectApi,
type AuthenticatedFastifyRequest,
} from "@monocloud/backend-node/fastify";
const app = Fastify();
const protect = protectApi();
// All routes require a valid access token
app.addHook("onRequest", protect());
app.get("/api/data", async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
});
app.listen({ port: 3000 });
How it works:
protectApi() creates a protection factory from environment variablesprotect() returns a Fastify onRequest hook that validates the Bearer tokenAuthorization: Bearer <token> headerrequest.claimsApply the hook to specific routes using the onRequest route option.
import "dotenv/config";
import Fastify from "fastify";
import {
protectApi,
type AuthenticatedFastifyRequest,
} from "@monocloud/backend-node/fastify";
const app = Fastify();
const protect = protectApi();
// Public route — no token required
app.get("/api/public", async () => {
return { message: "Public data" };
});
// Protected route — requires a valid access token
app.get("/api/protected", { onRequest: protect() }, async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
});
app.listen({ port: 3000 });
Pass scopes to protect() to require specific access token scopes.
app.get(
"/api/admin",
{ onRequest: protect({ scopes: ["write"] }) },
async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
}
);
Behavior:
401 Unauthorized if the token is missing or invalid403 Forbidden if the token is valid but lacks the required scopesPass groups to protect() to require specific group memberships.
app.get(
"/api/team",
{ onRequest: protect({ groups: ["engineering"] }) },
async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
}
);
Behavior:
401 Unauthorized if the token is missing or invalid403 Forbidden if the token is valid but the user is not in the required groupYou can apply global token validation and add scope or group requirements to specific routes.
import "dotenv/config";
import Fastify from "fastify";
import {
protectApi,
type AuthenticatedFastifyRequest,
} from "@monocloud/backend-node/fastify";
const app = Fastify();
const protect = protectApi();
// All routes require a valid access token
app.addHook("onRequest", protect());
// Any authenticated user can access this route
app.get("/api/data", async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
});
// Only users with the write scope and in the engineering group can access this route
app.get(
"/api/admin",
{ onRequest: protect({ scopes: ["write"], groups: ["engineering"] }) },
async (request) => {
const { claims } = request as AuthenticatedFastifyRequest;
return { claims };
}
);
app.listen({ port: 3000 });
| Scenario | Status code | Response |
|---|---|---|
| Missing or invalid token | 401 | { "message": "unauthorized" } |
| Valid token, missing scopes | 403 | { "message": "forbidden" } |
| Valid token, missing groups | 403 | { "message": "forbidden" } |
| Valid token, authorized | — | Route handler executes normally |