Implementation:Langfuse Langfuse CreateAuthedProjectAPIRoute
Appearance
| Knowledge Sources | |
|---|---|
| Domains | API Security, Trace Ingestion |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Concrete tool for wrapping Next.js API route handlers with authentication, rate limiting, schema validation, and OpenTelemetry context propagation provided by Langfuse.
Description
createAuthedProjectAPIRoute is a higher-order function that accepts a RouteConfig object describing an API endpoint and returns a fully instrumented Next.js API handler. The returned handler performs the following steps in order:
- Authentication: Calls
verifyAuthwhich attempts admin API key authentication (if allowed) followed by Basic Auth. Basic Auth decodes theAuthorization: Basic base64(publicKey:secretKey)header and verifies the key pair viaApiAuthService, which queries the database with Redis caching. Admin API key authentication usescrypto.timingSafeEqualfor constant-time comparison of the key against theADMIN_API_KEYenvironment variable. Admin auth requires the key in both the Bearer token and thex-langfuse-admin-api-keyheader for defense-in-depth.
- Rate Limiting: The authenticated scope is passed to
RateLimitService.rateLimitRequestalong with the configured rate limit resource (defaulting to"public-api"). If the request exceeds the rate limit, a 429 response is returned with appropriate headers.
- Schema Validation: If
querySchemais provided,req.queryis parsed through it. IfbodySchemais provided,req.bodyis parsed through it. Failed parsing throws a Zod validation error.
- OpenTelemetry Context: An OpenTelemetry context is created with the project ID and request headers, and the handler function is executed within this context via
opentelemetry.context.with.
- Response Validation (dev only): In development mode, the handler's return value is validated against
responseSchema. Failures are logged but do not affect the response.
- Response: The handler's return value is serialized as JSON and sent with the configured success status code (default 200).
Usage
Use this function when creating any new public API endpoint that requires project-scoped authentication. It is the standard pattern for all routes under /web/src/pages/api/public/.
Code Reference
Source Location
- Repository: langfuse
- File:
web/src/features/public-api/server/createAuthedProjectAPIRoute.ts - Lines: L234-319
Signature
export const createAuthedProjectAPIRoute = <
TQuery extends ZodType<any>,
TBody extends ZodType<any>,
TResponse extends ZodType<any>,
>(
routeConfig: RouteConfig<TQuery, TBody, TResponse>,
): ((req: NextApiRequest, res: NextApiResponse) => Promise<void>)
Import
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| routeConfig.name | string |
Yes | Endpoint name used for logging and metrics. |
| routeConfig.querySchema | ZodType<any> |
No | Zod schema for validating req.query. If omitted, query is treated as an empty object.
|
| routeConfig.bodySchema | ZodType<any> |
No | Zod schema for validating req.body. If omitted, body is treated as an empty object.
|
| routeConfig.responseSchema | ZodType<any> |
Yes | Zod schema for the response shape. Validated in development mode only. |
| routeConfig.successStatusCode | number |
No | HTTP status code for successful responses. Defaults to 200. |
| routeConfig.rateLimitResource | RateLimitResource |
No | Rate limit bucket identifier. Defaults to "public-api".
|
| routeConfig.isAdminApiKeyAuthAllowed | boolean |
No | Whether to allow ADMIN_API_KEY Bearer token authentication. Defaults to false. Only works on self-hosted instances.
|
| routeConfig.fn | (params) => Promise<TResponse> |
Yes | The handler function that receives validated query, body, request, response, and auth scope. |
Outputs
| Name | Type | Description |
|---|---|---|
| (return value) | (req: NextApiRequest, res: NextApiResponse) => Promise<void> |
A Next.js API handler function that performs auth, rate limiting, validation, and calls the provided handler. |
| auth.scope.projectId | string |
The authenticated project's ID, available inside the handler via auth.scope.projectId.
|
| auth.scope.accessLevel | "project" |
Always "project" for routes created with this function.
|
| auth.scope.orgId | string |
The organization ID associated with the authenticated API key. |
| auth.scope.plan | string |
The billing plan of the organization (e.g., "oss", "cloud:pro").
|
Usage Examples
Basic Authenticated Endpoint
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
import { z } from "zod/v4";
const handler = createAuthedProjectAPIRoute({
name: "get-traces",
querySchema: z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(50),
}),
responseSchema: z.object({
data: z.array(z.object({ id: z.string(), name: z.string().nullish() })),
meta: z.object({ totalItems: z.number(), page: z.number() }),
}),
fn: async ({ query, auth }) => {
const traces = await getTraces({
projectId: auth.scope.projectId,
page: query.page,
limit: query.limit,
});
return traces;
},
});
export default handler;
Ingestion Endpoint with Custom Rate Limit
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
import { z } from "zod/v4";
const handler = createAuthedProjectAPIRoute({
name: "ingest-events",
rateLimitResource: "ingestion",
bodySchema: z.object({
batch: z.array(z.unknown()),
}),
responseSchema: z.object({
successes: z.array(z.object({ id: z.string(), status: z.number() })),
errors: z.array(z.object({ id: z.string(), status: z.number(), error: z.string().optional() })),
}),
successStatusCode: 207,
fn: async ({ body, auth }) => {
return await processEventBatch(body.batch, auth);
},
});
export default handler;
Admin API Key Endpoint (Self-Hosted)
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
import { z } from "zod/v4";
const handler = createAuthedProjectAPIRoute({
name: "admin-project-config",
isAdminApiKeyAuthAllowed: true,
responseSchema: z.object({ config: z.record(z.string(), z.unknown()) }),
fn: async ({ auth }) => {
// auth.scope.projectId comes from x-langfuse-project-id header for admin auth
const config = await getProjectConfig(auth.scope.projectId);
return { config };
},
});
export default handler;
Related Pages
Implements Principle
Page Connections
Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment