Skip to main content

Overview

TailStack uses a clean separation between application configuration (app.ts) and server initialization (server.ts). This architecture makes it easy to test your application logic separately from server concerns.

Server Architecture

The server setup follows a two-file pattern:
  • app.ts - Configures the Express application with middleware and routes
  • server.ts - Initializes the cluster and starts the HTTP server

Application Setup (app.ts)

The app.ts file configures your Express application with middleware and routing:
packages/core/source/Server/src/app.ts
import express from 'express';
import cookieParser from 'cookie-parser';
import { corsMiddleware } from './middlewares/cors';
import routes from './routes';

const app = express();

// Middlewares
app.use(corsMiddleware);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());

// Routes
app.use('/api', routes);

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', message: 'Server is running' });
});

// 404 handler
app.use((req, res) => {
  res.status(404).json({
    error: 'Not Found',
    message: `Route ${req.path} not found`,
  });
});

export default app;

Key Features

1

Middleware Stack

The application uses a carefully ordered middleware stack:
  • CORS middleware for cross-origin requests
  • JSON body parser for API requests
  • URL-encoded body parser for form data
  • Cookie parser for session management
2

API Routes

All API routes are mounted under the /api prefix for clear separation between API and other endpoints.
3

Health Check

A /health endpoint is included for monitoring and load balancer health checks.
4

404 Handler

A catch-all 404 handler provides consistent error responses for undefined routes.

Server Initialization (server.ts)

The server.ts file handles cluster initialization and graceful shutdown:
packages/core/source/Server/src/server.ts
import app from './app';
import { config } from './config';
import { initializeCluster } from './cluster';

initializeCluster(() => {
  const server = app.listen(config.port, () => {
    console.log(`✅ Worker ${process.pid} started on port ${config.port}`);
  });

  // Graceful shutdown
  const gracefulShutdown = (signal: string) => {
    console.log(`${signal} signal received: closing HTTP server for worker ${process.pid}`);
    server.close(() => {
      console.log(`HTTP server closed for worker ${process.pid}`);
      process.exit(0);
    });
  };

  process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
  process.on('SIGINT', () => gracefulShutdown('SIGINT'));
});

Graceful Shutdown

The server implements graceful shutdown to handle:
  • SIGTERM - Termination signal from process managers or orchestrators
  • SIGINT - Interrupt signal from Ctrl+C in development
When a shutdown signal is received:
  1. The server stops accepting new connections
  2. Existing connections are allowed to complete
  3. The process exits cleanly with code 0
Graceful shutdown ensures that in-flight requests are not interrupted, providing a better experience for clients and preventing data loss.

Starting the Server

Start your development server:
npm run dev:server
For production:
npm run build:server
npm run start:server

Environment Variables

The server reads configuration from environment variables:
VariableDefaultDescription
PORT5000Port number for the server
NODE_ENVdevelopmentEnvironment mode
CORS_ORIGINhttp://localhost:5173Allowed CORS origin
WORKERSCPU countNumber of cluster workers
See the Configuration guide for detailed information about environment variables and configuration management.

Next Steps

Clustering

Learn how TailStack uses Node.js clustering for multi-core performance

Routing

Explore Express routing patterns and create API endpoints

Middleware

Configure CORS, security, and custom middleware

Configuration

Manage environment variables and server configuration

Build docs developers (and LLMs) love