API routes
Add trusted HTTP handlers with native Request/Response APIs and verified context.
/api/scoreBrowser request + session cookie
API routes keep credentials, authorization, database access, and other trusted work outside the Activity browser. Ludicord discovers method exports in app/api/**/route.ts and serves them from the matching /api path.
#Create a route
1import type { LudicordRequest } from "ludicord/server";23export function GET(request: LudicordRequest) {4 const name = request.ludicord?.user.displayName ?? "player";5 return Response.json({ message: `Hello, ${name}!` });6}
Routes use the standard Web Request and Response APIs. A handler must return a Response or a promise resolving to one.
#Export HTTP methods
Supported exports are GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. Ludicord supplies HEAD from GET and a basic OPTIONS response when you do not export them.
1export async function POST(request: LudicordRequest) {2 const input = await request.json() as unknown;3 const score = validateScore(input);4 return Response.json({ score }, { status: 201 });5}
Malformed JSON, bodies larger than the configured limit, unsupported methods, and route timeouts receive consistent runtime errors.
#Read session and verification context
LudicordRequest extends Request with three fields:
1const session = requireLudicordSession(request);23if (request.verification.activityInstance !== "verified") {4 return Response.json({ error: "Verified Activity instance required" }, { status: 403 });5}
Authentication proves who signed in. Authorization still belongs to the route: verify that the session user, guild, channel, role, or instance may perform the requested action.
#Dynamic routes
1app/api/players/[userId]/route.ts → /api/players/:userId
1import type { LudicordRequest, RouteContext } from "ludicord/server";23export function GET(request: LudicordRequest, context: RouteContext) {4 return Response.json({ requested: context.params.userId });5}
Route params are untrusted strings. Never use a browser-provided user or guild ID as proof of identity.
#Public routes
API modules require a valid session by default. Export public = true or auth = false only for endpoints that are intentionally anonymous.
1export const public = true;2export function GET() {3 return Response.json({ ok: true });4}
Do not make a route public just to work around a broken OAuth or cookie setup.
#Handle cancellation
request.signal aborts when the client disconnects or the route timeout expires. Pass it to database or HTTP clients that support cancellation.
1const result = await fetch(upstreamUrl, { signal: request.signal });
#Call the route from React
1const response = await fetch("/api/score", {2 method: "POST",3 headers: { "content-type": "application/json" },4 body: JSON.stringify({ score }),5});
Same-origin requests automatically include the Ludicord session cookie. Do not copy session data into request bodies.
#Next step
Use WebSockets when the server must push changes to connected Activities instead of waiting for another HTTP request.