Skip to main content

Overview

This guide covers deploying the Cognit Backend API to production, including building the application, configuring environment variables, and implementing security best practices.

Building for Production

The application is written in TypeScript and must be compiled to JavaScript before deployment.
1

Build the application

npm run build
This command:
  • Runs the TypeScript compiler (tsc)
  • Compiles all .ts files in src/ to JavaScript
  • Outputs compiled files to the dist/ directory
  • Uses configuration from tsconfig.json
2

Verify the build

Check that the dist/ directory was created:
ls -la dist/
You should see compiled JavaScript files matching your source structure.
3

Test the production build locally

npm start
This runs node ./dist/index.js (package.json:9)
The dist/ directory should be added to .gitignore - never commit compiled code.

Production Environment Variables

Create a .env file for production with secure values:
# Server Configuration
PORT=3005
NODE_ENV=production

# Database - Use production PostgreSQL instance
DATABASE_URL=postgresql://prod_user:SECURE_PASSWORD@prod-db-host:5432/cognit_production

# JWT - Use a strong, unique secret
JWT_SECRET=GENERATE_A_SECURE_RANDOM_STRING_MINIMUM_64_CHARACTERS

# Email - Production SMTP credentials
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_USER=apikey
EMAIL_PASS=YOUR_SENDGRID_API_KEY

# Frontend - Production domain
FRONTEND_URL=https://cognit.website
Security Checklist:
  • Use unique JWT_SECRET (minimum 64 characters)
  • Use strong database passwords (16+ characters, mixed case, numbers, symbols)
  • Never reuse development credentials in production
  • Store secrets securely (use secret managers like AWS Secrets Manager, HashiCorp Vault)
  • Never commit .env files to version control

Generating Secure Secrets

# Generate a secure JWT secret
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

# Generate a secure database password
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Database Setup

Production Database Configuration

1

Create production database

On your production PostgreSQL server:
CREATE DATABASE cognit_production;
CREATE USER cognit_prod WITH PASSWORD 'SECURE_RANDOM_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE cognit_production TO cognit_prod;
2

Update database sync mode

The development server uses db.sync({ alter: true }) which is not safe for production.Modify src/server.ts for production:
async function connectDB() {
    try {
        await db.authenticate()
        
        // Only sync in development
        if (process.env.NODE_ENV !== 'production') {
            db.sync({ alter: true })
        }
        
        console.log(colors.blue.bold('Database connection successfull'))
    } catch (error) {
        console.log(colors.red('Database connection failed'))
    }
}
3

Initialize database schema

For the first deployment, you can temporarily enable sync:
NODE_ENV=development npm start
# Tables will be created
# Then stop the server and switch to NODE_ENV=production
Or manually create tables based on your Sequelize models.
For production applications, consider using database migration tools like Sequelize migrations or Umzug.

Running the Production Server

Available Scripts

From package.json:
ScriptCommandPurpose
npm run devnodemon src/index.tsDevelopment with auto-reload
npm run dev:apinodemon src/index.ts --apiDevelopment in API mode
npm run buildtscCompile TypeScript to JavaScript
npm startnode ./dist/index.jsRun production server
npm testjestRun tests

Starting the Server

# After running npm run build
npm start
PM2 provides:
  • Automatic restarts on crashes
  • Load balancing (cluster mode)
  • Log management
  • Zero-downtime deployments

Security Considerations

The application implements several security measures that are automatically enabled:

1. Helmet (Security Headers)

app.use(helmet());
Location: src/server.ts:26 Helmet sets HTTP headers to protect against common vulnerabilities:
  • Prevents clickjacking (X-Frame-Options)
  • Blocks MIME-type sniffing (X-Content-Type-Options)
  • Enables XSS protection
  • Content Security Policy

2. CORS (Cross-Origin Resource Sharing)

app.use(cors({
    origin: ['https://cognit.website', 'http://localhost:5175'],
    credentials: true,
}));
Location: src/server.ts:27-30
Production CORS Configuration:
  • Remove localhost origins
  • Only allow your production frontend domain
  • Keep credentials: true for cookie-based auth
app.use(cors({
    origin: ['https://cognit.website', 'https://app.cognit.website'],
    credentials: true,
}));

3. Rate Limiting

export const limiter = rateLimit({
    windowMs: 60 * 1000,      // 1 minute
    limit: 5,                  // 5 requests
    message: {"error": "Max limit requests"}
})
Location: src/config/limiter.ts:3-7 Applied to: All /api/auth/* routes (src/routes/authRouter.ts:10)
Production Rate Limiting Recommendations:
// Stricter limits for production
export const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,  // 15 minutes
    limit: 100,                 // 100 requests
    message: {"error": "Too many requests, please try again later"},
    standardHeaders: true,
    legacyHeaders: false,
})

// Separate limiter for authentication
export const authLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    limit: 5,  // Only 5 login attempts per 15 min
    message: {"error": "Too many authentication attempts"}
})

4. JWT Authentication

Token Generation: src/utils/jwt.ts:3-7 Token Verification: src/middleware/auth.ts:31 Best practices:
  • Use long, random JWT_SECRET (64+ characters)
  • Tokens expire after 30 days (consider shorter for production)
  • Tokens transmitted via Authorization: Bearer <token> header

5. Password Hashing

The application uses bcrypt for password hashing:
  • Passwords are never stored in plaintext
  • Uses salt rounds for added security

Additional Security Recommendations

1

Enable HTTPS

Always use HTTPS in production. Use a reverse proxy like Nginx with Let’s Encrypt:
server {
    listen 443 ssl http2;
    server_name api.cognit.website;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:3005;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
2

Environment variable security

  • Never log environment variables
  • Use secret management services (AWS Secrets Manager, Azure Key Vault)
  • Rotate secrets regularly
  • Use different credentials for each environment
3

Database security

  • Use SSL/TLS for database connections
  • Restrict database access by IP
  • Use principle of least privilege for database users
  • Enable database audit logging
4

Monitoring and logging

Install monitoring tools:
npm install winston  # Structured logging
npm install @sentry/node  # Error tracking

Docker Deployment

Create a Dockerfile:
FROM node:18-alpine

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY . .

# Build TypeScript
RUN npm run build

# Expose port
EXPOSE 3005

# Start application
CMD ["npm", "start"]
Create docker-compose.yml:
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3005:3005"
    environment:
      - NODE_ENV=production
      - PORT=3005
      - DATABASE_URL=postgresql://postgres:password@db:5432/cognit
      - JWT_SECRET=${JWT_SECRET}
      - EMAIL_HOST=${EMAIL_HOST}
      - EMAIL_PORT=${EMAIL_PORT}
      - EMAIL_USER=${EMAIL_USER}
      - EMAIL_PASS=${EMAIL_PASS}
      - FRONTEND_URL=${FRONTEND_URL}
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=cognit
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:
Deploy:
# Build and start
docker-compose up -d

# View logs
docker-compose logs -f app

# Stop
docker-compose down

Platform-Specific Deployment

# Install Heroku CLI
npm install -g heroku

# Login
heroku login

# Create app
heroku create cognit-backend

# Add PostgreSQL
heroku addons:create heroku-postgresql:mini

# Set environment variables
heroku config:set JWT_SECRET=your-secret
heroku config:set EMAIL_HOST=smtp.sendgrid.net
heroku config:set EMAIL_PORT=587
heroku config:set EMAIL_USER=apikey
heroku config:set EMAIL_PASS=your-sendgrid-key
heroku config:set FRONTEND_URL=https://cognit.website

# Deploy
git push heroku main

# View logs
heroku logs --tail
Add Procfile:
web: npm start

Health Checks and Monitoring

Add a health check endpoint:
// src/server.ts
app.get('/health', async (req, res) => {
    try {
        await db.authenticate();
        res.status(200).json({ 
            status: 'healthy',
            database: 'connected',
            timestamp: new Date().toISOString()
        });
    } catch (error) {
        res.status(503).json({ 
            status: 'unhealthy',
            database: 'disconnected',
            error: error.message 
        });
    }
});

Post-Deployment Checklist

1

Verify environment variables

  • All required variables are set
  • No development values in production
  • Secrets are secure and unique
2

Test API endpoints

curl https://api.cognit.website/health
curl https://api.cognit.website/api/auth/users
3

Check security headers

curl -I https://api.cognit.website/health
Verify headers like X-Frame-Options, X-Content-Type-Options, etc.
4

Verify CORS configuration

Test from your frontend application that requests are not blocked.
5

Monitor logs

Check for errors, warnings, or unusual activity.
6

Test rate limiting

Verify rate limits are working by making rapid requests.
7

Verify database connectivity

Check that all database operations work correctly.

Troubleshooting

Database Connection Issues

# Test DATABASE_URL format
node -e "console.log(process.env.DATABASE_URL)"

# Test connection with psql
psql "$DATABASE_URL"

Port Already in Use

# Find process using port 3005
lsof -i :3005

# Kill the process
kill -9 <PID>

Build Failures

# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
npm run build

Next Steps

API Reference

Explore all available endpoints

Configuration

Fine-tune your configuration

Build docs developers (and LLMs) love