Auth Middleware
Auth Middleware provides flexible route protection patterns for your Flowfull backend. Protect your API routes with minimal code.
Overview
Flowfull provides middleware patterns for different authentication scenarios:
requireAuth()- Require authenticated useroptionalAuth()- Optional authenticationrequireUserType()- Require specific user typerequirePermission()- Require specific permission
Basic Usage
Require Authentication
typescript
import { Hono } from 'hono';
import { requireAuth } from './lib/auth/middleware';
const app = new Hono();
// Protected route - requires authentication
app.get('/api/profile', requireAuth(), async (c) => {
const userId = c.get('user_id');
const email = c.get('email');
return c.json({ userId, email });
});Optional Authentication
typescript
// Public route with optional auth
app.get('/api/posts', optionalAuth(), async (c) => {
const userId = c.get('user_id'); // May be undefined
if (userId) {
// Show personalized posts
return c.json({ posts: await getPersonalizedPosts(userId) });
} else {
// Show public posts
return c.json({ posts: await getPublicPosts() });
}
});Require User Type
typescript
// Single user type - Admin only
app.get('/api/admin/users', requireUserType('admin'), async (c) => {
const users = await db.selectFrom('users').selectAll().execute();
return c.json({ users });
});
// Single user type - Teacher only
app.get('/api/teacher/classes', requireUserType('teacher'), async (c) => {
const classes = await getTeacherClasses(c.get('user_id'));
return c.json({ classes });
});
// Multiple user types - Admin OR Manager
app.get('/api/manager/dashboard', requireUserType(['admin', 'manager']), async (c) => {
const userId = c.get('user_id');
const userType = c.get('session').user_type;
return c.json({
dashboard: await getDashboard(userId, userType)
});
});
// Custom user types - Your own business logic
app.get('/api/premium/content', requireUserType(['premium', 'vip', 'admin']), async (c) => {
return c.json({ content: await getPremiumContent() });
});Require Permission
typescript
// Require specific permission
app.delete('/api/posts/:id', requirePermission('posts.delete'), async (c) => {
const postId = c.req.param('id');
await db.deleteFrom('posts').where('id', '=', postId).execute();
return c.json({ success: true });
});Middleware Patterns
Pattern 1: Public Routes
typescript
// No authentication required
app.get('/api/public/stats', async (c) => {
return c.json({ stats: await getPublicStats() });
});Pattern 2: Protected Routes
typescript
// Authentication required
app.get('/api/profile', requireAuth(), async (c) => {
const userId = c.get('user_id');
return c.json({ profile: await getProfile(userId) });
});Pattern 3: Conditional Routes
typescript
// Different behavior based on auth
app.get('/api/content', optionalAuth(), async (c) => {
const userId = c.get('user_id');
if (userId) {
return c.json({ content: await getPremiumContent(userId) });
} else {
return c.json({ content: await getFreeContent() });
}
});Pattern 4: Role-Based Routes (Simple)
typescript
// Use requireUserType for simple role checks
app.get('/api/admin/dashboard', requireUserType('admin'), async (c) => {
return c.json({ dashboard: await getAdminDashboard() });
});
app.get('/api/teacher/dashboard', requireUserType('teacher'), async (c) => {
return c.json({ dashboard: await getTeacherDashboard() });
});
app.get('/api/student/dashboard', requireUserType('student'), async (c) => {
return c.json({ dashboard: await getStudentDashboard() });
});Pattern 5: Role-Based Routes (Dynamic)
typescript
// Different behavior for different roles in same route
app.get('/api/dashboard', requireAuth(), async (c) => {
const session = c.get('session');
const userType = session.user_type;
switch (userType) {
case 'admin':
return c.json({ dashboard: await getAdminDashboard() });
case 'teacher':
return c.json({ dashboard: await getTeacherDashboard() });
case 'student':
return c.json({ dashboard: await getStudentDashboard() });
default:
return c.json({ error: 'Invalid user type' }, 403);
}
});Pattern 6: Multiple User Types
typescript
// Allow multiple user types to access the same route
app.get('/api/management/reports', requireUserType(['admin', 'manager', 'supervisor']), async (c) => {
const session = c.get('session');
const userId = c.get('user_id');
// All three user types can access, but may see different data
const reports = await getReports(userId, session.user_type);
return c.json({ reports });
});Flexible User Type Management
Why requireUserType is Better Than requireAuth
The requireUserType middleware is more flexible than just using requireAuth because:
- Single or Multiple Types: Accept one user type or an array of types
- Custom User Types: Works with ANY user type you define (not just admin/user)
- Clear Error Messages: Shows exactly what types are allowed
- Type Safety: Validates user types at the middleware level
Common Use Cases
typescript
// 1. Admin-only routes
app.delete('/api/users/:id', requireUserType('admin'), async (c) => {
// Only admins can delete users
});
// 2. Manager routes (admin OR manager)
app.get('/api/reports', requireUserType(['admin', 'manager']), async (c) => {
// Both admins and managers can view reports
});
// 3. Custom business roles
app.post('/api/inventory', requireUserType(['warehouse_manager', 'admin']), async (c) => {
// Custom user types for your business logic
});
// 4. Premium features
app.get('/api/premium/analytics', requireUserType(['premium', 'enterprise', 'admin']), async (c) => {
// Multiple subscription tiers
});
// 5. Educational platform
app.get('/api/grades', requireUserType(['teacher', 'admin', 'principal']), async (c) => {
// Multiple educational roles
});Building Flexible APIs
This is a configurable starter kit for developers. You can define ANY user types:
typescript
// Your custom user types
const USER_TYPES = {
// Basic roles
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest',
// Business roles
MANAGER: 'manager',
EMPLOYEE: 'employee',
CONTRACTOR: 'contractor',
// Educational roles
TEACHER: 'teacher',
STUDENT: 'student',
PARENT: 'parent',
// E-commerce roles
SELLER: 'seller',
BUYER: 'buyer',
AFFILIATE: 'affiliate',
// Subscription tiers
FREE: 'free',
PREMIUM: 'premium',
ENTERPRISE: 'enterprise'
};
// Use them in your routes
app.get('/api/seller/products', requireUserType(USER_TYPES.SELLER), handler);
app.get('/api/teacher/classes', requireUserType(USER_TYPES.TEACHER), handler);
app.get('/api/premium/features', requireUserType([USER_TYPES.PREMIUM, USER_TYPES.ENTERPRISE]), handler);Context Variables
After authentication, these variables are available in the context:
typescript
app.get('/api/me', requireAuth(), async (c) => {
const userId = c.get('user_id'); // User ID
const session = c.get('session'); // Full session object
const userType = session.user_type; // User type
const email = session.email; // Email
const permissions = session.permissions; // Permissions array (if any)
return c.json({ userId, email, userType });
});Validation Modes
You can specify validation mode per route:
typescript
// Standard validation
app.get('/api/profile', requireAuth('STANDARD'), handler);
// Advanced validation for sensitive routes
app.get('/api/billing', requireAuth('ADVANCED'), handler);
// Strict validation for critical operations
app.post('/api/transfer', requireAuth('STRICT'), handler);Learn more about Validation Modes →
Error Handling
typescript
app.get('/api/protected', requireAuth(), async (c) => {
try {
const data = await getData(c.get('user_id'));
return c.json({ data });
} catch (error) {
return c.json({ error: 'Internal server error' }, 500);
}
});Custom Middleware
Create custom middleware for specific needs:
typescript
// Require email verification
export function requireVerifiedEmail() {
return async (c: Context, next: Next) => {
const userId = c.get('user_id');
const user = await db.selectFrom('users')
.where('id', '=', userId)
.select('email_verified')
.executeTakeFirst();
if (!user?.email_verified) {
return c.json({ error: 'Email not verified' }, 403);
}
await next();
};
}
// Usage
app.get('/api/premium', requireAuth(), requireVerifiedEmail(), handler);Chaining Middleware
typescript
// Multiple middleware
app.post(
'/api/admin/users',
requireAuth('STRICT'),
requireUserType('admin'),
requirePermission('users.create'),
async (c) => {
// Create user
}
);Best Practices
✅ Do
- Use
requireAuth()for basic protected routes (any authenticated user) - Use
requireUserType()when you need specific user types - Use
optionalAuth()for public routes with personalization - Use arrays for multiple user types:
requireUserType(['admin', 'manager']) - Define custom user types that match your business logic
- Chain middleware for complex requirements
- Handle errors gracefully with try-catch blocks
- Use validation modes appropriately for security
❌ Don't
- Don't use
requireAuth()when you need specific user types - userequireUserType()instead - Don't hardcode user types in route handlers - validate at middleware level
- Don't skip authentication on sensitive routes
- Don't use
DISABLEDmode in production - Don't ignore authentication errors
- Don't forget to validate permissions for sensitive operations
When to Use Each Middleware
| Middleware | Use Case | Example |
|---|---|---|
requireAuth() | Any authenticated user can access | User profile, settings |
requireUserType('admin') | Only specific user type | Admin dashboard |
requireUserType(['admin', 'manager']) | Multiple user types | Management reports |
optionalAuth() | Public with personalization | Product listings, blog posts |
requirePermission('posts.delete') | Specific permission needed | Delete operations |
Real-World Examples
E-commerce API
typescript
// Public product listing
app.get('/api/products', optionalAuth(), async (c) => {
const userId = c.get('user_id');
const products = await getProducts(userId);
return c.json({ products });
});
// Protected cart
app.get('/api/cart', requireAuth(), async (c) => {
const cart = await getCart(c.get('user_id'));
return c.json({ cart });
});
// Admin product management
app.post('/api/admin/products', requireUserType('admin'), async (c) => {
const product = await createProduct(await c.req.json());
return c.json({ product });
});SaaS Application
typescript
// Public landing page data
app.get('/api/features', async (c) => {
return c.json({ features: await getFeatures() });
});
// User dashboard
app.get('/api/dashboard', requireAuth(), async (c) => {
const dashboard = await getDashboard(c.get('user_id'));
return c.json({ dashboard });
});
// Organization admin
app.get('/api/org/settings', requirePermission('org.manage'), async (c) => {
const settings = await getOrgSettings(c.get('organization_id'));
return c.json({ settings });
});Quick Reference
Import Middleware
typescript
import {
requireAuth,
optionalAuth,
requireUserType,
requirePermission
} from './lib/auth/middleware';Basic Examples
typescript
// 1. Protected route (any authenticated user)
app.get('/api/profile', requireAuth(), handler);
// 2. Public route with optional auth
app.get('/api/posts', optionalAuth(), handler);
// 3. Admin only
app.get('/api/admin/users', requireUserType('admin'), handler);
// 4. Multiple user types
app.get('/api/manager/dashboard', requireUserType(['admin', 'manager']), handler);
// 5. Permission-based
app.delete('/api/posts/:id', requirePermission('posts.delete'), handler);
// 6. Chain multiple middleware
app.post(
'/api/admin/users',
requireAuth(),
requireUserType('admin'),
requirePermission('users.create'),
handler
);Common Patterns
typescript
// Pattern 1: Public API
app.get('/api/public/stats', async (c) => {
return c.json({ stats: await getStats() });
});
// Pattern 2: User-specific data
app.get('/api/my/orders', requireAuth(), async (c) => {
const userId = c.get('user_id');
return c.json({ orders: await getOrders(userId) });
});
// Pattern 3: Role-based access
app.get('/api/reports', requireUserType(['admin', 'manager']), async (c) => {
return c.json({ reports: await getReports() });
});
// Pattern 4: Conditional content
app.get('/api/content', optionalAuth(), async (c) => {
const userId = c.get('user_id');
const content = userId
? await getPremiumContent(userId)
: await getFreeContent();
return c.json({ content });
});Next Steps
- Bridge Validation - How authentication works
- Validation Modes - Security levels
- Protected Routes Guide - Advanced patterns
Need Help?
- 🌐 Notside.com - Professional API development
- 📧 Email: contact@notside.com