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
// Admin-only route
app.get('/api/admin/users', requireUserType('admin'), async (c) => {
const users = await db.selectFrom('users').selectAll().execute();
return c.json({ users });
});
// Teacher-only route
app.get('/api/teacher/classes', requireUserType('teacher'), async (c) => {
const classes = await getTeacherClasses(c.get('user_id'));
return c.json({ classes });
});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
typescript
// Different routes for different roles
app.get('/api/dashboard', requireAuth(), async (c) => {
const userType = c.get('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);
}
});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 email = c.get('email'); // Email
const userType = c.get('user_type'); // User type
const sessionId = c.get('session_id'); // Session ID
const permissions = c.get('permissions'); // Permissions array
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 protected routes - Use
optionalAuth()for public routes with personalization - Use validation modes appropriately
- Handle errors gracefully
- Chain middleware for complex requirements
❌ Don't
- Skip authentication on sensitive routes
- Use
DISABLEDmode in production - Ignore authentication errors
- Hardcode user types in routes
- Forget to validate permissions
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 });
});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