API Guard
The apiGuard utility is the standard way to protect all API routes in DBS. It provides a single function that combines session checking, rate limiting, and optional permission-based authorization in one call.
Location
src/lib/api-guard.tsHow It Works
apiGuard runs three checks in sequence when called at the start of an API route handler:
- Session Check — Verifies the user is authenticated via Better Auth.
- Rate Limit Check — Enforces a default limit of 200 requests per 60 seconds per user.
- Permission Check (optional) — If a permission string is passed, verifies the user holds that permission (super admins bypass this check automatically).
Basic Usage
Session Only
// User must be logged in, no specific permission needed
export async function GET() {
const guard = await apiGuard();
if (guard.error) return guard.error; // Returns 401 if not authenticated
const { session } = guard;
// session.user.id, session.user.email, etc. are available
}Return Type
The function always returns a GuardResult union type. Always check .error before accessing .session.
type GuardSuccess = {
error: null;
session: { user: { id, email, name, permissions: string[], roles: string[] } };
};
type GuardFailure = {
error: NextResponse; // Ready-to-return error response (401 / 403 / 429)
session: null;
};Error Responses
| Condition | HTTP Status | Message |
|---|---|---|
| Not authenticated | 401 Unauthorized | Login required |
| Rate limit exceeded | 429 Too Many Requests | Rate limit exceeded, please try again later. |
| Missing permission | 403 Forbidden | Missing required permission: <permission> |
Super Admin bypass: Users with the super_admin role automatically pass all permission checks. They are still subject to rate limiting.
Creating New Protected Routes
When adding a new API route, always start with apiGuard. Check the RBAC permission list in src/lib/rbac/permission.ts to find the correct permission key to use.
// src/app/api/my-feature/route.ts
import { apiGuard } from '@/lib/api-guard';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const guard = await apiGuard('my_feature.write');
if (guard.error) return guard.error;
const { session } = guard;
const body = await req.json();
// ... your logic here
return NextResponse.json({ success: true });
}Last updated on