Auth Patterns
Basic auth via beforeRequest
const userRoutes = createRelayerRoute(r, 'users', { hooks: { beforeRequest: async (ctx, req) => { const token = req.headers.get('authorization')?.replace('Bearer ', ''); if (!token) throw new Error('Unauthorized'); ctx.user = await verifyToken(token); ctx.context = { currentUserId: ctx.user.id }; }, },});Role-based filtering
Different roles see different data:
export const GET = userRoutes.list({ beforeFind: async (options, ctx) => { const user = ctx.user as { role: string; groupIds: number[] };
if (user.role === 'viewer') { // Viewers only see public records options.where = { ...options.where, isPublic: true }; }
if (user.role === 'group_member') { // Members see records in their groups options.where = { ...options.where, groupId: { in: user.groupIds } }; }
// Admins see everything — no filter added },});Tenant isolation
Automatically scope all queries to the current tenant:
const taskRoutes = createRelayerRoute(r, 'tasks', { hooks: { beforeRequest: async (ctx, req) => { ctx.tenantId = req.headers.get('x-tenant-id'); if (!ctx.tenantId) throw new Error('Missing tenant'); }, },});
export const GET = taskRoutes.list({ beforeFind: async (options, ctx) => { options.where = { ...options.where, tenantId: ctx.tenantId }; },});
export const POST = taskRoutes.create({ beforeCreate: async (data, ctx) => { data.tenantId = ctx.tenantId; return data; },});Using $raw for complex authorization
export const GET = taskRoutes.list({ beforeFind: async (options, ctx) => { const user = ctx.user as { id: number; teamIds: number[] }; options.where = { ...options.where, $raw: ({ table, sql }) => sql`${table.createdBy} = ${user.id} OR ${table.teamId} = ANY(${user.teamIds})`, }; },});CASL integration
If you use CASL for authorization:
import { accessibleBy } from '@casl/prisma'; // or build your own adapter
export const GET = taskRoutes.list({ beforeFind: async (options, ctx) => { const ability = defineAbilityFor(ctx.user); const caslWhere = accessibleBy(ability).Task; options.where = { ...options.where, ...caslWhere }; },});Mutation guards
Prevent unauthorized mutations:
export const DELETE = taskRoutes.remove({ beforeDelete: async (where, ctx) => { const user = ctx.user as { role: string }; if (user.role !== 'admin') { throw new Error('Only admins can delete records'); } return where; },});