Express routes

The server half for Express apps: a mountable router that proxies component requests to the Handset API. Your key stays server-side; your session decides tenant access.

Installation

npx shadcn@latest add @handset/express-routes

Lands at server/handset-routes.ts and expects HANDSET_API_KEY in your environment. Works with Express 4 and 5 on Node 18+.

Mounting

Mount at /api/handset — the base path the components use by default:

import express from "express";
import { handsetRoutes } from "./handset-routes";

const app = express();
app.use("/api/handset", handsetRoutes());

Serving your React app from a different origin? Point the components at your API host with <HandsetProvider baseUrl="https://api.yourapp.com/api/handset"> and handle CORS as you do for the rest of your API.

The auth boundary

One function is yours to implement. Everything the components can see flows through it:

async function resolveTenantId(req: Request): Promise<string | null> {
  if (!req.session?.user) throw { status: 401, message: "Sign in first" };
  return req.session.user.handsetTenantId;   // "tnt_…"
}

The resolved tenant is injected into every read — anything the browser claims is ignored. Return null for single-tenant accounts.

The allowlist

Identical to the Next.js proxy: only the endpoints the components need are forwarded, everything else 404s. Buying numbers, porting, compliance — none of that is reachable from the browser. Extend the list deliberately if you build more.

Environment

HANDSET_API_KEY=sk_test_…        # required; sk_live_… in production
HANDSET_API_URL=                 # optional override, defaults to https://api.handset.dev/v1