Getting Started
TypeScript Quickstart
Add CoreSDK to an Express, Fastify, or Next.js service.
Phase 2 — not yet available. The TypeScript SDK (@coresdk/sdk) ships in Phase 2. The code below reflects the planned API and the package is not yet published.
TypeScript Quickstart
Prerequisites
- Node.js 18+
- TypeScript 5+
- A running identity provider that exposes a JWKS endpoint (Auth0, Clerk, Keycloak, etc.)
Install
npm install @coresdk/sdk
# or with pnpm / yarn
pnpm add @coresdk/sdk
yarn add @coresdk/sdkInitialize the SDK
Create the SDK once and export it — import it wherever you need middleware or type-safe user access:
// lib/sdk.ts
import { CoreSDK } from "@coresdk/sdk";
export const sdk = new CoreSDK({
tenant: "acme",
jwksUrl: "https://your-idp.com/.well-known/jwks.json",
policyDir: "./policies",
otelEndpoint: "http://localhost:4317", // optional
});| Option | Description |
|---|---|
tenant | Your tenant slug — stamped on every span and error response |
jwksUrl | JWKS endpoint used to verify incoming JWTs |
policyDir | Directory of .rego policy files (watched for live reload) |
otelEndpoint | OTLP gRPC endpoint for traces and metrics (optional) |
Full Express example
A complete Express application with typed request objects:
// server.ts
import express from "express";
import { sdk } from "./lib/sdk";
import type { CoreSDKUser, CoreSDKTenant } from "@coresdk/sdk";
// Augment the Express Request type so req.user and req.tenant are typed everywhere
declare global {
namespace Express {
interface Request {
user: CoreSDKUser;
tenant: CoreSDKTenant;
}
}
}
const app = express();
app.use(express.json());
// CoreSDKMiddleware validates JWTs, resolves tenant context, and populates
// req.user and req.tenant for every request before route handlers run.
app.use(CoreSDKMiddleware);
// GET /api/orders — requires the "orders:read" policy action
app.get(
"/api/orders",
require_role("orders:read"),
async (req, res) => {
// req.user and req.tenant are fully typed at this point
const orders = await db.orders.forUser(req.user.id, req.tenant.id);
res.json(orders);
}
);
// GET /api/orders/:id
app.get(
"/api/orders/:id",
require_role("orders:read"),
async (req, res) => {
const order = await db.orders.get(req.params.id, req.tenant.id);
if (!order) return res.status(404).json({ error: "not found" });
res.json(order);
}
);
// POST /api/orders — requires "orders:write"
app.post(
"/api/orders",
require_role("orders:write"),
async (req, res) => {
const order = await db.orders.create({
description: req.body.description,
userId: req.user.id,
tenantId: req.tenant.id,
});
res.status(201).json(order);
}
);
// DELETE /api/orders/:id — requires "orders:delete"
app.delete(
"/api/orders/:id",
require_role("orders:delete"),
async (req, res) => {
await db.orders.delete(req.params.id, req.tenant.id);
res.status(204).send();
}
);
// Profile endpoint — auth required but no policy action enforced
app.get("/me", CoreSDKMiddleware, async (req, res) => {
res.json({
id: req.user.id,
email: req.user.email,
role: req.user.role,
tenantId: req.user.tenantId,
claims: req.user.rawClaims,
});
});
app.listen(3000, () => {
console.log("listening on http://localhost:3000");
});TypeScript types
@coresdk/sdk exports full TypeScript types. You can import them for use in service layers, tests, and other files that don't import Express directly:
import type { CoreSDKUser, CoreSDKTenant } from "@coresdk/sdk";
// CoreSDKUser — populated on req.user by the middleware
interface CoreSDKUser {
id: string; // stable user ID (JWT sub claim)
email: string; // from the identity provider
role: string; // resolved from tenant membership
tenantId: string; // tenant the user belongs to
rawClaims: Record<string, unknown>; // full JWT claims object
}
// CoreSDKTenant — populated on req.tenant by the middleware
interface CoreSDKTenant {
id: string;
name: string;
plan: "free" | "team" | "business" | "enterprise";
metadata: Record<string, unknown>;
}require_role() options
// Require a single action
app.get("/api/orders", require_role("orders:read"), handler);
// Require any one of several actions (OR semantics)
app.get("/api/orders", sdk.requireAny(["orders:read", "orders:admin"]), handler);
// Pass resource context to the Rego policy for attribute-based decisions
app.get(
"/api/orders/:id",
require_role("orders:read", {
resource: (req) => ({ id: req.params.id }),
}),
handler
);Fastify example
import Fastify from "fastify";
import { coreSDKPlugin } from "@coresdk/sdk/fastify";
const fastify = Fastify({ logger: true });
await fastify.register(coreSDKPlugin, {
tenant: "acme",
jwksUrl: "https://your-idp.com/.well-known/jwks.json",
policyDir: "./policies",
});
fastify.get(
"/api/orders",
{ preHandler: fastify.corerequire_role("orders:read") },
async (request, reply) => {
// request.user and request.tenant are typed by the plugin
return db.orders.forUser(request.user.id, request.tenant.id);
}
);
fastify.post(
"/api/orders",
{ preHandler: fastify.corerequire_role("orders:write") },
async (request, reply) => {
const order = await db.orders.create({
...(request.body as { description: string }),
userId: request.user.id,
tenantId: request.tenant.id,
});
reply.status(201).send(order);
}
);
await fastify.listen({ port: 3000, host: "0.0.0.0" });Next.js App Router example
Use withCoreSDK to wrap individual Route Handlers in the App Router:
// app/api/orders/route.ts
import { withCoreSDK } from "@coresdk/sdk/next";
import { sdk } from "@/lib/sdk";
export const GET = withCoreSDK(
sdk,
{ action: "orders:read" },
async (req, { user, tenant }) => {
const orders = await db.orders.forUser(user.id, tenant.id);
return Response.json(orders);
}
);
export const POST = withCoreSDK(
sdk,
{ action: "orders:write" },
async (req, { user, tenant }) => {
const body = await req.json();
const order = await db.orders.create({
description: body.description,
userId: user.id,
tenantId: tenant.id,
});
return Response.json(order, { status: 201 });
}
);For middleware-level protection (protecting entire route segments), add CoreSDK to middleware.ts:
// middleware.ts
import { createNextMiddleware } from "@coresdk/sdk/next";
import { sdk } from "./lib/sdk";
// Runs on every request matching the config.matcher pattern
export const middleware = createNextMiddleware(sdk);
export const config = {
matcher: ["/api/:path*"],
};Run locally
# Install dependencies
npm install
# Start with ts-node (development)
npx ts-node server.ts
# Or compile and run
npx tsc && node dist/server.js
# Test with a valid JWT (replace with a real token from your IdP)
curl http://localhost:3000/api/orders \
-H "Authorization: Bearer <your-jwt>"
# Without a token — expect 401
curl http://localhost:3000/api/orders
# Check your identity
curl http://localhost:3000/me \
-H "Authorization: Bearer <your-jwt>"Next steps
- Writing Rego policies — controlling what each role can do
- JWT configuration — audience, issuer, clock skew
- Multi-tenancy — tenant isolation and row-level security
- Observability — traces, metrics, and the CoreSDK dashboard
- Error reference — full list of error types and status codes