Node.js and Express
Your API verifies the session token your frontend sends and manages users with the secret key. The frontend here is
the React (Vite) quickstart’s app on http://localhost:5173.
Before you start
Section titled “Before you start”- Node.js 24 or later, and Express 5.
- An application in the dashboard: its publishable key and a secret key (shown once when you create it) are on the instance’s API keys page.
Install
Section titled “Install”npm i @zautha/backend express corsAdd the code
Section titled “Add the code”api/server.ts is the API: zauthaMiddleware verifies the token of every request and accepts it only when it was
issued for one of appOrigins; requireAuth() answers 401 without a signed-in user; /api/me reads the user with
the secret key and returns only what the browser may see. web/src/use-api.ts is the frontend’s side: it calls your
API with the session token in the Authorization header.
import cors from "cors";import express from "express";import { createZauthaClient } from "@zautha/backend";import { getAuth, requireAuth, zauthaMiddleware } from "@zautha/backend/express";
// The frontends that call this API: allowed by CORS, and the only accepted origin of their session tokens.const appOrigins = ["http://localhost:5173"];// The secret key (create one under API keys) stays on your server.const zautha = createZauthaClient({ secretKey: process.env.ZAUTHA_SECRET_KEY! });
const app = express();app.use(cors({ origin: appOrigins, allowedHeaders: ["Authorization"] }));app.use(zauthaMiddleware({ publishableKey: "pk_test_...", authorizedParties: appOrigins }));
app.get("/api/me", requireAuth(), async (req, res) => { const state = getAuth(req); if (state.status !== "signed-in") return; const user = await zautha.users.get(state.auth.userId); // Only what the browser may see: private metadata stays on the server. res.json({ id: user.id, primary_email_address: user.primary_email_address, first_name: user.first_name, last_name: user.last_name, });});
app.listen(3001);import { useAuth } from "@zautha/react";
/** Calls your API as the signed-in user: the session token goes in the Authorization header. */export function useApi() { const { getToken } = useAuth(); return async (path: string): Promise<unknown> => { const response = await fetch(`http://localhost:3001${path}`, { headers: { Authorization: `Bearer ${await getToken()}` }, }); return response.json(); };}Run it
Section titled “Run it”ZAUTHA_SECRET_KEY=sk_test_... node api/server.tsThe API listens on http://localhost:3001. Sign in on the React app and call useApi()("/api/me"): it answers with
your user; without a session it answers 401 with code unauthenticated.
Next steps
Section titled “Next steps”@zautha/backend: reverification, roles and permissions, the Backend API client, webhooks.- Sessions and tokens: what is in the session token.
Zautha’s end-to-end tests run this setup in an Express example API, called by the React example app.