withAuth helpers for cleaner code.
Perfect for: Protecting sensitive data, user management APIs, payment endpoints, and admin functions.
Method 1: Native Better Auth Protection
Basic Authentication Check
// Direct Better Auth usage
import { auth } from '@/lib/better-auth/auth'
import { NextRequest } from 'next/server'
import { headers } from 'next/headers'
export async function GET(request: NextRequest) {
// Check if user is authenticated
const session = await auth.api.getSession({
headers: await headers()
})
if (!session?.session?.userId) {
return Response.json({ error: 'Non authentifié' }, { status: 401 })
}
// User is authenticated
return Response.json({
message: 'This is protected data',
userId: session.session.userId
})
}Role-Based Check with Better Auth
// With manual role checking
import { auth } from '@/lib/better-auth/auth'
import { getAuthUser } from '@/services/authentication/auth-service'
import { hasRequiredRole } from '@/services/authentication/auth-util'
import { RoleConst } from '@/services/types/domain/auth-types'
export async function GET(request: NextRequest) {
const session = await auth.api.getSession({
headers: await headers()
})
if (!session?.session?.userId) {
return Response.json({ error: 'Non authentifié' }, { status: 401 })
}
// Get full user with role
const authUser = await getAuthUser()
if (!authUser) {
return Response.json({ error: 'Utilisateur non trouvé' }, { status: 404 })
}
// Check role
const hasRole = hasRequiredRole(authUser, RoleConst.ADMIN)
if (!hasRole) {
return Response.json({ error: 'Non autorisé' }, { status: 403 })
}
// Admin logic here
return Response.json({ data: 'Admin data' })
}Method 2: withAuth Helpers (Recommended)
The boilerplate provides clean helper functions that handle authentication and role checking automatically.Static Routes Protection
// src/app/api/projects/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { withAuth, withUserAuth } from '@/lib/api-auth'
import { RoleConst } from '@/services/types/domain/auth-types'
// Basic protection with custom role
export const GET = withAuth(async (request: NextRequest, authUser) => {
// User is authenticated and has USER role (or higher)
// authUser contains full user data including role
const result = await getProjectsService(authUser.id)
return NextResponse.json({ success: true, data: result })
}, RoleConst.USER)
// Shortcut for USER role
export const POST = withUserAuth(async (request: NextRequest, authUser) => {
const body = await request.json()
const newProject = await createProjectService({
...body,
createdBy: authUser.id
})
return NextResponse.json({ success: true, data: newProject }, { status: 201 })
})Dynamic Routes Protection
// src/app/api/projects/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { withDynamicUserAuth } from '@/lib/api-auth'
// For routes with dynamic parameters
export const GET = withDynamicUserAuth(
async (request: NextRequest, authUser, context) => {
const params = await context.params
const projectId = params.id
const project = await getProjectByIdService(projectId)
return NextResponse.json({ success: true, data: project })
}
)
export const PUT = withDynamicUserAuth(
async (request: NextRequest, authUser, context) => {
const params = await context.params
const projectId = params.id
const body = await request.json()
const updatedProject = await updateProjectService({
id: projectId,
...body
})
return NextResponse.json({
success: true,
message: 'Projet mis à jour avec succès',
data: updatedProject
})
}
)Available Helper Functions
import {
withAuth, // Custom role
withUserAuth, // USER role shortcut
withAdminAuth, // ADMIN role shortcut
withRedactorAuth // REDACTOR role shortcut
} from '@/lib/api-auth'
// Usage examples
export const GET = withAuth(async (request, authUser) => {
// Logic here
return NextResponse.json({ data: 'moderator data' })
}, RoleConst.MODERATOR)
export const POST = withUserAuth(async (request, authUser) => {
// Logic here
return NextResponse.json({ success: true })
})
export const DELETE = withAdminAuth(async (request, authUser) => {
// Logic here
return NextResponse.json({ success: true })
})
export const PUT = withRedactorAuth(async (request, authUser) => {
// Logic here
return NextResponse.json({ success: true })
})Comparison: Which Method to Choose?
Advantages:
- Cleaner code - No boilerplate auth logic
- Consistent handling - Standardized error responses
- Type safety -
authUserparameter is fully typed - Role shortcuts -
withUserAuth,withAdminAuth, etc. - Less repetition - DRY principle
Recommendation: Use
withAuth helpers for 95% of your API routes. Only use native Better Auth when you need custom authentication logic or debugging.Error Responses
Both methods return standardized JSON error responses:// 401 Unauthorized
{
"error": "Non authentifié"
}
// 403 Forbidden
{
"error": "Non autorisé"
}
// 404 User Not Found
{
"error": "Utilisateur non trouvé"
}Testing API Protection
1
Test unauthenticated access
- Make API requests without session cookies
- Verify 401 "Non authentifié" responses
- Check error message formatting
2
Test role-based endpoints
- Test with users of different roles (USER, ADMIN, etc.)
- Verify role hierarchy works correctly
- Check 403 "Non autorisé" for insufficient roles
3
Test dynamic routes
- Test routes with
[id]parameters - Verify
context.paramsaccess works - Test parameter validation
APIs secured! Your backend endpoints are now protected with both native Better Auth and convenient helper functions.