Skip to content

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.

  • 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.
Terminal window
npm i @zautha/backend express cors

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.

api/server.ts
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);
web/src/use-api.ts
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();
};
}
Terminal window
ZAUTHA_SECRET_KEY=sk_test_... node api/server.ts

The 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.

Zautha’s end-to-end tests run this setup in an Express example API, called by the React example app.