Skip to content

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:

javascript
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:

javascript
// 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:

javascript
// 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:

javascript
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:

javascript
// 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:

javascript
// 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:

javascript
// 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:

javascript
// 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:

javascript
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:

javascript
// 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

javascript
// 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

javascript
// 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.

Released under the MIT License.