Skip to content

API Routes

Define endpoints with the api() function. Cooper's Rust runtime handles HTTP serving — your handler just returns data.

Basic route

ts
import { api } from "cooper-stack/api";

export const getUser = api(
  { method: "GET", path: "/users/:id" },
  async ({ id }: { id: string }) => {
    const user = await db.queryRow("SELECT * FROM users WHERE id = $1", [id]);
    return { user };
  }
);

Route config

OptionTypeDescription
methodGET | POST | PUT | PATCH | DELETEHTTP method (default: GET)
pathstringURL path with :param placeholders
authbooleanRequire authentication (default: false)
validateZod schemaRequest body validation
stream"sse" | "websocket"Streaming response type
middlewareMiddlewareFn[]Per-route middleware

Path parameters

ts
// Path: /users/:userId/posts/:postId
export const getPost = api(
  { method: "GET", path: "/users/:userId/posts/:postId" },
  async ({ userId, postId }: { userId: string; postId: string }) => {
    // params are extracted and typed
  }
);

Request body

For POST, PUT, PATCH — the request body is parsed as JSON and passed as the first argument:

ts
export const createUser = api(
  { method: "POST", path: "/users" },
  async (body: { name: string; email: string }) => {
    // body is the parsed JSON request body
  }
);

Protected routes

ts
export const deleteUser = api(
  { method: "DELETE", path: "/users/:id", auth: true },
  async ({ id }, principal) => {
    // principal is injected by your auth handler
    if (principal.role !== "admin") {
      throw new CooperError("PERMISSION_DENIED", "Admins only");
    }
  }
);

See Auth for setting up the auth handler.

Per-route middleware

ts
import { middleware } from "cooper-stack/middleware";

const rateLimiter = middleware(async (req, next) => {
  // ...
  return next(req);
});

export const sensitiveRoute = api(
  { method: "POST", path: "/transfer", middleware: [rateLimiter] },
  async (body) => { /* ... */ }
);

Apache-2.0 Licensed