Express-based Architecture
BasefloorAPI is built on top of Express.js, giving you access to the full power and flexibility of the Express ecosystem while providing additional abstractions for rapid API development.
Why Express.js?
Express.js provides the foundation that makes BasefloorAPI both powerful and familiar:
- Maximum Flexibility - Use any Express middleware or patterns
- Proven Performance - Battle-tested in production environments
- Rich Ecosystem - Access to thousands of Express-compatible packages
- Developer Familiarity - Leverage existing Express.js knowledge
Direct Express Access
BasefloorAPI exposes the underlying Express application, giving you complete control when needed:
const BasefloorAPI = require('@basefloor/api');
const API = BasefloorAPI({
config: require('./basefloor.config.js')
});
// Direct access to Express app
API.use('/api/v1', (req, res, next) => {
console.log(`${req.method} ${req.originalUrl}`);
next();
});
// Express Router for modular organization
const router = API.express.Router();
router.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
API.use('/api/v1', router);
Express Features in BasefloorAPI
Custom Middleware
Create and use custom middleware for cross-cutting concerns:
// Rate limiting middleware
const rateLimiter = (limit = 10, windowMs = 60000) => {
const requests = new Map();
return (req, res, next) => {
const clientId = req.ip;
const now = Date.now();
if (!requests.has(clientId)) {
requests.set(clientId, []);
}
const clientRequests = requests.get(clientId);
const validRequests = clientRequests.filter(
timestamp => now - timestamp < windowMs
);
if (validRequests.length >= limit) {
return res.status(429).json({
error: 'Too many requests',
retry_after: Math.ceil((validRequests[0] + windowMs - now) / 1000)
});
}
validRequests.push(now);
requests.set(clientId, validRequests);
next();
};
};
// Apply rate limiting
API.use('/api/heavy', rateLimiter(5, 30000));
Request/Response Streaming
Handle large data transfers efficiently with streaming:
// Streaming response
API.get('/api/stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plain',
'Transfer-Encoding': 'chunked'
});
let counter = 0;
const interval = setInterval(() => {
counter++;
res.write(`Chunk ${counter}: ${new Date().toISOString()}\n`);
if (counter >= 10) {
clearInterval(interval);
res.end('\nStream completed!\n');
}
}, 1000);
req.on('close', () => clearInterval(interval));
});
Server-Sent Events (SSE)
Real-time updates to clients using Server-Sent Events:
API.get('/api/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Send initial connection
res.write(`data: ${JSON.stringify({
type: 'connected',
timestamp: new Date()
})}\n\n`);
// Periodic updates
const interval = setInterval(() => {
const data = {
type: 'update',
timestamp: new Date(),
random_value: Math.random()
};
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 2000);
req.on('close', () => clearInterval(interval));
});
Error Handling
Comprehensive error handling with Express middleware:
// Custom error handling middleware
API.use((error, req, res, next) => {
console.error('Express Error:', error);
if (error.type === 'entity.parse.failed') {
return res.status(400).json({
error: 'Invalid JSON in request body',
details: error.message
});
}
if (error.code === 'LIMIT_FILE_SIZE') {
return res.status(413).json({
error: 'File too large',
max_size: error.limit
});
}
res.status(500).json({
error: 'Internal server error',
timestamp: new Date().toISOString()
});
});
Performance Monitoring
Built-in performance tracking and metrics:
// Response time middleware
API.use((req, res, next) => {
const startTime = Date.now();
res.on('finish', () => {
const responseTime = Date.now() - startTime;
res.setHeader('X-Response-Time', `${responseTime}ms`);
});
next();
});
// Metrics endpoint
API.get('/api/metrics', (req, res) => {
res.json({
uptime: process.uptime(),
memory_usage: process.memoryUsage(),
cpu_usage: process.cpuUsage(),
platform: process.platform,
node_version: process.version
});
});
Advanced Express Patterns
Route Organization
Use Express routers for clean code organization:
// Feature-specific router
const userRouter = API.express.Router();
userRouter.get('/', (req, res) => {
// List users
});
userRouter.post('/', (req, res) => {
// Create user
});
userRouter.get('/:id', (req, res) => {
// Get specific user
});
// Mount router
API.use('/api/users', userRouter);
Content Negotiation
Handle different content types and API versioning:
// API versioning middleware
API.use((req, res, next) => {
const version = req.headers['api-version'] || '1.0';
req.apiVersion = version;
res.setHeader('API-Version', version);
next();
});
// Content type handling
API.get('/api/data/:id', (req, res) => {
const { format = 'json' } = req.query;
const data = { id: req.params.id, name: 'Example' };
if (format === 'xml') {
res.set('Content-Type', 'application/xml');
res.send(`<?xml version="1.0"?><item><id>${data.id}</id></item>`);
} else {
res.json(data);
}
});
Request Validation
Custom validation middleware:
const validateJSON = (req, res, next) => {
if (req.method === 'POST' || req.method === 'PUT') {
if (!req.is('application/json')) {
return res.status(400).json({
error: 'Content-Type must be application/json'
});
}
}
next();
};
API.use('/api/data', validateJSON);
Integration with BasefloorAPI Features
Express features work seamlessly with BasefloorAPI's built-in functionality:
// Combine with BasefloorAPI authentication
API.get('/api/protected', [
API.requireAuthentication, // BasefloorAPI auth
rateLimiter(10, 60000), // Custom Express middleware
], (req, res) => {
res.json({
message: 'Authenticated and rate-limited endpoint',
user: req.user
});
});
// Use with BasefloorAPI models
API.post('/api/custom-users', [
API.requireAuthentication,
validateJSON
], async (req, res) => {
try {
const user = await API.models.Users.create(req.body);
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
Best Practices
Middleware Order
// Correct middleware order
API.use(express.json()); // Body parsing first
API.use(rateLimiter()); // Rate limiting
API.use(authMiddleware); // Authentication
API.use('/api', apiRoutes); // Routes
API.use(errorHandler); // Error handling last
Error Boundaries
// Async error handling
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
API.get('/api/async-route', asyncHandler(async (req, res) => {
const data = await someAsyncOperation();
res.json(data);
}));
Performance Tips
- Use compression middleware for large responses
- Implement caching strategies with middleware
- Monitor memory usage in production
- Use clustering for CPU-intensive operations
Example: Complete Express Integration
See the complete Express example for a full implementation showcasing:
- Custom middleware chains
- Rate limiting and validation
- Streaming responses
- Server-sent events
- Performance monitoring
- Error handling
- API versioning
The Express-based architecture gives you the freedom to build exactly what you need while leveraging BasefloorAPI's productivity features.