Skip to content

Deploying Expressa

Introduction

Expressa is a middleware library for building powerful backends and APIs rapidly. It provides an automatic admin interface, JSON:API compliant REST endpoints, and flexible data modeling—all without writing boilerplate code. Think of it as an instant backend that turns your database into a full-featured API with a visual admin panel for managing data.

Built on top of Express.js, Expressa allows developers to define collections through simple JSON schemas and immediately get working CRUD endpoints, role-based permissions, data validation, and a user-friendly interface for managing content. Whether you’re building a mobile app backend, a content management system, or prototyping a new product, Expressa accelerates development by eliminating repetitive API code.

Key Features

Expressa empowers rapid development with:

  • Instant Admin Interface: Beautiful, automatically generated admin panel for managing data
  • JSON:API Compliant: RESTful endpoints following the JSON:API specification
  • Schema-Based Collections: Define data models with JSON Schema for automatic validation
  • Role-Based Permissions: Granular access control for users, collections, and individual documents
  • File Uploads: Built-in support for file uploads with configurable storage backends
  • MongoDB Backend: Flexible NoSQL storage for dynamic data structures
  • Authentication: JWT-based authentication with user management built-in
  • Real-Time Queries: Support for complex queries with filtering, sorting, and pagination
  • Relationships: Define relationships between collections (one-to-one, one-to-many, many-to-many)
  • Custom Endpoints: Extend with custom Express routes and middleware
  • Email Integration: Built-in email functionality for notifications and user registration
  • Data Export: Export collection data to CSV or JSON formats
  • Audit Logging: Track all changes to data with automatic audit trails
  • Extensible: Plugin architecture for adding custom functionality
  • TypeScript Support: Full TypeScript definitions for type-safe development

Why Deploy Expressa on Klutch.sh?

Deploying Expressa on Klutch.sh provides significant advantages:

  • Rapid Deployment: Deploy from GitHub with automatic Docker detection
  • Persistent Storage: Built-in volume support for uploaded files and configuration
  • Database Integration: Easy MongoDB setup with TCP connectivity
  • HTTPS by Default: Automatic SSL certificates for secure API access
  • Environment Management: Secure configuration of secrets and API keys
  • Scalable Resources: Adjust compute resources as your API usage grows
  • High Availability: Reliable infrastructure ensures 24/7 API availability
  • Custom Domains: Use your own domain for professional API endpoints
  • Zero Downtime Deploys: Rolling updates keep your API online during deploys

Prerequisites

Before deploying Expressa, ensure you have:

  • A Klutch.sh account
  • A GitHub account with a repository for your deployment configuration
  • A MongoDB database (see our MongoDB deployment guide)
  • Basic familiarity with Docker, REST APIs, and JSON Schema
  • (Optional) SMTP credentials for email notifications

Preparing Your Repository

Create a new GitHub repository for your Expressa deployment with the following structure:

expressa-deploy/
├── Dockerfile
├── package.json
├── server.js
├── collections/
│ └── .gitkeep
├── data/
│ └── .gitkeep
├── .env.example
├── .dockerignore
├── README.md
└── .gitignore

Dockerfile

Create a production-ready Dockerfile for Expressa:

FROM node:20-alpine
# Install system dependencies
RUN apk add --no-cache \
tini \
curl \
ca-certificates
# Create app user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force
# Copy application files
COPY --chown=nodejs:nodejs . .
# Create directories for data and uploads
RUN mkdir -p /app/data /app/uploads /app/collections && \
chown -R nodejs:nodejs /app/data /app/uploads /app/collections
# Switch to non-root user
USER nodejs
# Expose application port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Use tini for proper signal handling
ENTRYPOINT ["/sbin/tini", "--"]
# Start the application
CMD ["node", "server.js"]

Package.json

Create a package.json file with Expressa dependencies:

{
"name": "expressa-app",
"version": "1.0.0",
"description": "Expressa API backend on Klutch.sh",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"keywords": ["expressa", "api", "admin", "backend"],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"expressa": "^0.3.13",
"express": "^4.18.2",
"mongodb": "^6.3.0",
"dotenv": "^16.3.1",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"compression": "^1.7.4",
"morgan": "^1.10.0"
},
"devDependencies": {
"nodemon": "^3.0.2"
},
"engines": {
"node": ">=18.0.0"
}
}

Server.js

Create the main server.js file:

require('dotenv').config();
const express = require('express');
const expressa = require('expressa');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const morgan = require('morgan');
const app = express();
const PORT = process.env.PORT || 3000;
// Security middleware
app.use(helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false
}));
// CORS configuration
app.use(cors({
origin: process.env.CORS_ORIGIN || '*',
credentials: true
}));
// Compression middleware
app.use(compression());
// Logging
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// Initialize Expressa
const expressaSettings = {
// Database connection
mongodb_uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/expressa',
// Storage paths
collection_path: process.env.COLLECTION_PATH || './collections',
data_path: process.env.DATA_PATH || './data',
// File uploads
file_storage: {
type: process.env.FILE_STORAGE_TYPE || 'local',
path: process.env.FILE_STORAGE_PATH || './uploads'
},
// Authentication
jwt_secret: process.env.JWT_SECRET || 'change-this-secret-in-production',
// Email configuration (optional)
email: process.env.SMTP_HOST ? {
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT) || 587,
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD
},
from: process.env.SMTP_FROM || 'noreply@example.com'
} : undefined,
// Admin configuration
admin_user: {
email: process.env.ADMIN_EMAIL || 'admin@example.com',
password: process.env.ADMIN_PASSWORD || 'admin123'
},
// API configuration
api_prefix: process.env.API_PREFIX || '/api',
// Enable features
enforce_permissions: process.env.ENFORCE_PERMISSIONS !== 'false',
// Development mode
dev: process.env.NODE_ENV !== 'production'
};
// Initialize Expressa middleware
expressa.addMiddleware(app, expressaSettings);
// Custom routes can be added here
app.get('/', (req, res) => {
res.json({
message: 'Expressa API is running',
admin: '/admin',
api: expressaSettings.api_prefix,
docs: '/api-docs'
});
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(err.status || 500).json({
error: {
message: err.message || 'Internal server error',
status: err.status || 500
}
});
});
// Start server
app.listen(PORT, () => {
console.log(`Expressa server running on port ${PORT}`);
console.log(`Admin interface: http://localhost:${PORT}/admin`);
console.log(`API endpoint: http://localhost:${PORT}${expressaSettings.api_prefix}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
app.close(() => {
console.log('HTTP server closed');
});
});

Environment Variables Template

Create a .env.example file:

Terminal window
# Application Configuration
NODE_ENV=production
PORT=3000
API_PREFIX=/api
# MongoDB Connection (Required)
MONGODB_URI=mongodb://admin:password@your-mongodb-app.klutch.sh:8000/expressa?authSource=admin
# Security (Required)
JWT_SECRET=generate_with_openssl_rand_base64_64
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=change_this_secure_password
# Storage Paths
COLLECTION_PATH=/app/collections
DATA_PATH=/app/data
FILE_STORAGE_TYPE=local
FILE_STORAGE_PATH=/app/uploads
# CORS Configuration
CORS_ORIGIN=*
# Permissions
ENFORCE_PERMISSIONS=true
# Email Configuration (Optional)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your_app_password
SMTP_FROM=Expressa <noreply@example.com>
# Feature Flags
ENABLE_FILE_UPLOADS=true
ENABLE_EMAIL_NOTIFICATIONS=true
MAX_UPLOAD_SIZE=10485760
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100

Docker Ignore File

Create a .dockerignore file:

.git
.gitignore
.env
.env.*
*.md
README.md
docker-compose.yml
.vscode
.idea
*.log
.DS_Store
node_modules
coverage
.nyc_output
*.test.js
*.spec.js
test/
tests/
__tests__
uploads/*
!uploads/.gitkeep
data/*
!data/.gitkeep
collections/*
!collections/.gitkeep

Git Ignore File

Create a .gitignore file:

.env
.env.local
.env.production
*.log
.DS_Store
.vscode/
.idea/
node_modules/
coverage/
.nyc_output/
uploads/*
!uploads/.gitkeep
data/*
!data/.gitkeep
collections/*.json
!collections/.gitkeep

Initialize Directory Structure

Create placeholder files for directories:

Terminal window
mkdir -p collections data uploads
touch collections/.gitkeep data/.gitkeep uploads/.gitkeep

Local Development (Optional)

Test your Expressa setup locally before deploying:

Using Docker Locally

Terminal window
# Build the Docker image
docker build -t expressa-app .
# Run with environment variables
docker run -d \
--name expressa-test \
-p 3000:3000 \
-e MONGODB_URI=mongodb://localhost:27017/expressa \
-e JWT_SECRET=test_secret \
-e ADMIN_EMAIL=admin@example.com \
-e ADMIN_PASSWORD=admin123 \
-v $(pwd)/uploads:/app/uploads \
-v $(pwd)/data:/app/data \
expressa-app
# View logs
docker logs -f expressa-test
# Stop and remove
docker stop expressa-test
docker rm expressa-test

Using Node.js Directly

Terminal window
# Install dependencies
npm install
# Create .env file
cp .env.example .env
# Edit .env with your local MongoDB connection
# Start development server
npm run dev

Access the admin interface at http://localhost:3000/admin and log in with your admin credentials.

Deploying on Klutch.sh

  1. Set Up MongoDB Database

    Before deploying Expressa, you need a MongoDB database. Follow our MongoDB deployment guide to set up a database on Klutch.sh.

    When creating your MongoDB instance, configure:

    • Database name: expressa
    • Username: expressa
    • Password: Use a strong, randomly generated password
    • Connection details: Note your app URL (e.g., your-mongodb-app.klutch.sh:8000)

    Your MongoDB connection string will be:

    mongodb://expressa:your_password@your-mongodb-app.klutch.sh:8000/expressa?authSource=admin
  2. Generate Security Keys

    Generate a strong JWT secret for authentication:

    Terminal window
    # Generate JWT secret (64 bytes)
    openssl rand -base64 64

    Save this value securely—you’ll need it for your Klutch.sh environment variables.

  3. Push to GitHub

    Commit your Expressa configuration to GitHub:

    Terminal window
    git add .
    git commit -m "Initial Expressa setup for Klutch.sh"
    git remote add origin https://github.com/yourusername/expressa-deploy.git
    git push -u origin main
  4. Create a New Project

    Log in to the Klutch.sh dashboard and create a new project for your Expressa deployment.

  5. Connect Your GitHub Repository

    In your Klutch.sh project:

    1. Navigate to the Apps section
    2. Click “Create New App”
    3. Select GitHub as your source
    4. Choose the repository containing your Expressa Dockerfile
    5. Select the branch you want to deploy (typically main or master)
  6. Configure Build Settings

    Klutch.sh will automatically detect your Dockerfile in the repository root. No additional build configuration is needed.

  7. Set Traffic Type

    In the deployment settings:

    1. Select HTTP as the traffic type
    2. Set the internal port to 3000
    3. Klutch.sh will automatically route traffic to your Expressa application
  8. Configure Environment Variables

    In your Klutch.sh app settings, add the following environment variables:

    Required Variables:

    Terminal window
    NODE_ENV=production
    PORT=3000
    MONGODB_URI=mongodb://expressa:your_password@your-mongodb-app.klutch.sh:8000/expressa?authSource=admin
    JWT_SECRET=your_generated_jwt_secret_64_bytes
    ADMIN_EMAIL=admin@example.com
    ADMIN_PASSWORD=your_admin_password

    Optional Variables (Email):

    Terminal window
    SMTP_HOST=smtp.gmail.com
    SMTP_PORT=587
    SMTP_USER=your-email@gmail.com
    SMTP_PASSWORD=your_app_password
    SMTP_FROM=Expressa <noreply@example.com>

    Optional Variables (Configuration):

    Terminal window
    API_PREFIX=/api
    CORS_ORIGIN=https://yourfrontend.com
    ENFORCE_PERMISSIONS=true
    ENABLE_FILE_UPLOADS=true
    MAX_UPLOAD_SIZE=10485760

    Mark all sensitive values like MONGODB_URI, JWT_SECRET, ADMIN_PASSWORD, and SMTP_PASSWORD as secrets in the Klutch.sh dashboard.

  9. Attach Persistent Volumes

    Expressa needs persistent storage for uploaded files and collection definitions:

    Upload Volume:

    1. In your Klutch.sh app settings, navigate to Volumes
    2. Click “Add Volume”
    3. Set the mount path to /app/uploads
    4. Choose an appropriate size (start with 5GB for file storage)
    5. Save the volume configuration

    Data Volume:

    1. Click “Add Volume” again
    2. Set the mount path to /app/data
    3. Choose an appropriate size (start with 2GB for application data)
    4. Save the volume configuration

    Collections Volume:

    1. Click “Add Volume” again
    2. Set the mount path to /app/collections
    3. Choose an appropriate size (start with 1GB for schema definitions)
    4. Save the volume configuration
  10. Deploy the Application

    Click “Deploy” to start the deployment process. Klutch.sh will:

    1. Pull your code from GitHub
    2. Build the Docker image using the Dockerfile
    3. Configure networking and volumes
    4. Start your Expressa server
    5. Initialize the admin user on first run

    The initial deployment typically takes 2-4 minutes.

  11. Verify Deployment

    Once deployed, verify your Expressa instance:

    Terminal window
    # Check health endpoint
    curl https://example-app.klutch.sh/health
    # Expected response
    {
    "status": "healthy",
    "timestamp": "2025-12-19T...",
    "uptime": 123
    }

    Access the admin interface at https://example-app.klutch.sh/admin and log in with your admin credentials.

Getting Started with Expressa

Accessing the Admin Interface

    1. Navigate to https://example-app.klutch.sh/admin
    2. Log in with your admin credentials (from ADMIN_EMAIL and ADMIN_PASSWORD)
    3. You’ll see the Expressa dashboard with the default collections

Creating Your First Collection

    1. Click “Collections” in the admin sidebar
    2. Click “Create Collection”
    3. Define your collection schema:
    {
    "name": "blog_posts",
    "schema": {
    "type": "object",
    "properties": {
    "title": {
    "type": "string",
    "minLength": 1,
    "maxLength": 200
    },
    "slug": {
    "type": "string",
    "pattern": "^[a-z0-9-]+$"
    },
    "content": {
    "type": "string"
    },
    "author": {
    "type": "string"
    },
    "published": {
    "type": "boolean",
    "default": false
    },
    "tags": {
    "type": "array",
    "items": {
    "type": "string"
    }
    },
    "created_at": {
    "type": "string",
    "format": "date-time"
    }
    },
    "required": ["title", "content", "author"]
    }
    }
    1. Click “Save”
    2. The collection and its API endpoints are now available

Adding Data

    1. Navigate to your collection in the admin interface
    2. Click “Add Item”
    3. Fill in the form fields based on your schema
    4. Click “Save”

Configuring Permissions

    1. Click on your collection
    2. Click “Permissions”
    3. Set role-based access:
    {
    "create": ["Admin", "Author"],
    "read": ["*"],
    "update": ["Admin", "Author"],
    "delete": ["Admin"]
    }
    1. "*" means public access
    2. Named roles require authentication

Using the Expressa API

Authentication

Register a New User:

Terminal window
curl -X POST https://example-app.klutch.sh/api/user \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepassword",
"role": "User"
}'

Login:

Terminal window
curl -X POST https://example-app.klutch.sh/api/user/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepassword"
}'
# Response includes JWT token
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": { ... }
}

CRUD Operations

Create (POST):

Terminal window
curl -X POST https://example-app.klutch.sh/api/blog_posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"title": "Getting Started with Expressa",
"slug": "getting-started",
"content": "Expressa makes API development fast...",
"author": "John Doe",
"published": true,
"tags": ["tutorial", "api"],
"created_at": "2025-12-19T10:00:00Z"
}'

Read (GET):

Terminal window
# Get all items
curl https://example-app.klutch.sh/api/blog_posts
# Get single item by ID
curl https://example-app.klutch.sh/api/blog_posts/507f1f77bcf86cd799439011
# Filter with query parameters
curl "https://example-app.klutch.sh/api/blog_posts?filter[published]=true&sort=-created_at&limit=10"
# Search
curl "https://example-app.klutch.sh/api/blog_posts?filter[title][\$regex]=Expressa"

Update (PUT):

Terminal window
curl -X PUT https://example-app.klutch.sh/api/blog_posts/507f1f77bcf86cd799439011 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"title": "Updated Title",
"published": true
}'

Delete (DELETE):

Terminal window
curl -X DELETE https://example-app.klutch.sh/api/blog_posts/507f1f77bcf86cd799439011 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

Sample Code for Common Tasks

Node.js Client

const fetch = require('node-fetch');
const API_URL = 'https://example-app.klutch.sh/api';
let authToken = null;
async function login(email, password) {
const response = await fetch(`${API_URL}/user/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
authToken = data.token;
return data;
}
async function createPost(postData) {
const response = await fetch(`${API_URL}/blog_posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
body: JSON.stringify(postData)
});
return await response.json();
}
async function getPosts(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(`${API_URL}/blog_posts?${params}`);
return await response.json();
}
async function updatePost(id, updates) {
const response = await fetch(`${API_URL}/blog_posts/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
body: JSON.stringify(updates)
});
return await response.json();
}
async function deletePost(id) {
const response = await fetch(`${API_URL}/blog_posts/${id}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${authToken}` }
});
return response.ok;
}
// Usage
(async () => {
await login('user@example.com', 'password');
const post = await createPost({
title: 'My First Post',
content: 'Hello Expressa!',
author: 'User Name',
published: true
});
console.log('Created post:', post);
const posts = await getPosts({ 'filter[published]': true });
console.log('All published posts:', posts);
})();

Python Client

import requests
from typing import Dict, List, Optional
class ExpressaClient:
def __init__(self, base_url: str):
self.base_url = base_url.rstrip('/')
self.api_url = f"{self.base_url}/api"
self.token: Optional[str] = None
def login(self, email: str, password: str) -> Dict:
"""Authenticate and get JWT token"""
response = requests.post(
f"{self.api_url}/user/login",
json={"email": email, "password": password}
)
response.raise_for_status()
data = response.json()
self.token = data['token']
return data
def _headers(self) -> Dict[str, str]:
"""Get headers with authentication"""
headers = {"Content-Type": "application/json"}
if self.token:
headers["Authorization"] = f"Bearer {self.token}"
return headers
def create(self, collection: str, data: Dict) -> Dict:
"""Create a new item in a collection"""
response = requests.post(
f"{self.api_url}/{collection}",
json=data,
headers=self._headers()
)
response.raise_for_status()
return response.json()
def read(self, collection: str, item_id: Optional[str] = None,
filters: Optional[Dict] = None) -> Dict:
"""Read items from a collection"""
url = f"{self.api_url}/{collection}"
if item_id:
url += f"/{item_id}"
response = requests.get(url, params=filters or {})
response.raise_for_status()
return response.json()
def update(self, collection: str, item_id: str, data: Dict) -> Dict:
"""Update an item in a collection"""
response = requests.put(
f"{self.api_url}/{collection}/{item_id}",
json=data,
headers=self._headers()
)
response.raise_for_status()
return response.json()
def delete(self, collection: str, item_id: str) -> bool:
"""Delete an item from a collection"""
response = requests.delete(
f"{self.api_url}/{collection}/{item_id}",
headers=self._headers()
)
return response.ok
# Usage
client = ExpressaClient("https://example-app.klutch.sh")
client.login("user@example.com", "password")
# Create a post
post = client.create("blog_posts", {
"title": "Python Integration",
"content": "Using Expressa with Python",
"author": "Python Developer",
"published": True
})
print(f"Created post: {post['_id']}")
# Get all published posts
posts = client.read("blog_posts", filters={"filter[published]": "true"})
print(f"Found {len(posts)} posts")
# Update a post
updated = client.update("blog_posts", post['_id'], {
"title": "Updated Python Integration"
})
# Delete a post
# client.delete("blog_posts", post['_id'])

React Frontend Example

import React, { useState, useEffect } from 'react';
const API_URL = 'https://example-app.klutch.sh/api';
function BlogApp() {
const [posts, setPosts] = useState([]);
const [token, setToken] = useState(localStorage.getItem('token'));
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
const response = await fetch(`${API_URL}/blog_posts?filter[published]=true&sort=-created_at`);
const data = await response.json();
setPosts(data);
} catch (error) {
console.error('Error fetching posts:', error);
}
};
const login = async (email, password) => {
try {
const response = await fetch(`${API_URL}/user/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
setToken(data.token);
localStorage.setItem('token', data.token);
} catch (error) {
console.error('Login error:', error);
}
};
const createPost = async (postData) => {
try {
const response = await fetch(`${API_URL}/blog_posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(postData)
});
const newPost = await response.json();
setPosts([newPost, ...posts]);
} catch (error) {
console.error('Error creating post:', error);
}
};
return (
<div>
<h1>Blog Posts</h1>
{posts.map(post => (
<article key={post._id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<small>By {post.author}</small>
</article>
))}
</div>
);
}
export default BlogApp;

Advanced Configuration

Custom Domain Setup

Use your own domain for your Expressa API:

    1. Add Custom Domain in Klutch.sh:

      • Navigate to your app in the Klutch.sh dashboard
      • Go to Domains section
      • Click “Add Custom Domain”
      • Enter your domain (e.g., api.example.com)
    2. Configure DNS:

      • Add a CNAME record in your DNS provider:

        Type: CNAME
        Name: api
        Value: example-app.klutch.sh
        TTL: 3600
    3. Update CORS Configuration:

      Terminal window
      CORS_ORIGIN=https://yourfrontend.com
    4. Redeploy:

      • Redeploy your application for changes to take effect
      • Wait for DNS propagation (typically 5-30 minutes)

File Upload Configuration

Configure file uploads for user-generated content:

Local Storage (Default):

Terminal window
FILE_STORAGE_TYPE=local
FILE_STORAGE_PATH=/app/uploads
MAX_UPLOAD_SIZE=10485760

Collection Schema with File Field:

{
"name": "products",
"schema": {
"properties": {
"name": { "type": "string" },
"image": {
"type": "string",
"format": "file"
}
}
}
}

Upload File via API:

Terminal window
curl -X POST https://example-app.klutch.sh/api/file \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@product-image.jpg"
# Response
{
"filename": "abc123.jpg",
"url": "/api/file/abc123.jpg"
}

Email Notification Setup

Configure email for user registration and notifications:

Gmail Configuration:

Terminal window
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-specific-password
SMTP_FROM=Expressa <noreply@example.com>

SendGrid Configuration:

Terminal window
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASSWORD=your-sendgrid-api-key
SMTP_FROM=Expressa <noreply@example.com>

Database Backup Strategy

Set up automated MongoDB backups:

backup-expressa.sh
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
MONGODB_URI="mongodb://expressa:password@your-mongodb-app.klutch.sh:8000/expressa?authSource=admin"
# Backup database
mongodump --uri="${MONGODB_URI}" --out="${BACKUP_DIR}/backup_${DATE}"
# Compress backup
tar -czf "${BACKUP_DIR}/backup_${DATE}.tar.gz" "${BACKUP_DIR}/backup_${DATE}"
rm -rf "${BACKUP_DIR}/backup_${DATE}"
# Upload to S3 or backup service
# aws s3 cp "${BACKUP_DIR}/backup_${DATE}.tar.gz" s3://your-bucket/expressa/
# Clean up old backups (keep last 30 days)
find "${BACKUP_DIR}" -name "backup_*.tar.gz" -mtime +30 -delete
echo "Backup completed: backup_${DATE}.tar.gz"

Production Best Practices

Security Hardening

Environment Variables:

  • Never commit .env files to version control
  • Use Klutch.sh’s secret management for all sensitive values
  • Rotate JWT secret quarterly
  • Use strong admin passwords (minimum 16 characters)

Generate Secure Secrets:

Terminal window
# JWT secret
openssl rand -base64 64
# Admin password
openssl rand -base64 32

API Security:

  • Enable CORS only for trusted origins
  • Use HTTPS only (handled by Klutch.sh)
  • Implement rate limiting for public endpoints
  • Validate all input data with JSON Schema
  • Use JWT tokens with reasonable expiration times
  • Enable permission enforcement in production

Performance Optimization

Database Indexing:

// In MongoDB shell or through Expressa hooks
db.blog_posts.createIndex({ published: 1, created_at: -1 });
db.blog_posts.createIndex({ author: 1 });
db.blog_posts.createIndex({ slug: 1 }, { unique: true });
db.blog_posts.createIndex({ tags: 1 });

Application Caching:

Consider adding Redis caching for frequently accessed data:

const redis = require('redis');
const client = redis.createClient({
url: process.env.REDIS_URL
});
// Cache middleware
app.get('/api/blog_posts', async (req, res, next) => {
const cacheKey = `posts:${JSON.stringify(req.query)}`;
const cached = await client.get(cacheKey);
if (cached) {
return res.json(JSON.parse(cached));
}
// Continue to Expressa handler
next();
});

Response Compression:

Enabled by default in our server.js with the compression middleware.

Monitoring and Logging

Health Monitoring:

monitor-expressa.sh
#!/bin/bash
HEALTH_URL="https://example-app.klutch.sh/health"
check_health() {
response=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL")
if [ "$response" != "200" ]; then
echo "Expressa health check failed: HTTP $response"
# Send alert
return 1
fi
echo "Expressa is healthy"
return 0
}
check_health

Key Metrics to Monitor:

  • API response times
  • Database connection pool usage
  • Error rates by endpoint
  • Upload storage usage
  • Memory and CPU utilization
  • Active user sessions
  • API request rate

Log Aggregation:

Access logs through Klutch.sh dashboard or set up centralized logging:

// Add structured logging
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Log API requests
app.use((req, res, next) => {
logger.info('API Request', {
method: req.method,
path: req.path,
user: req.user?.email
});
next();
});

Scaling Considerations

Vertical Scaling:

  • Small (< 1000 requests/day): 512MB RAM, 0.5 CPU
  • Medium (1000-10000 requests/day): 1GB RAM, 1 CPU
  • Large (10000-100000 requests/day): 2GB RAM, 2 CPU
  • Enterprise (> 100000 requests/day): 4GB RAM, 4+ CPU

Horizontal Scaling:

  • Deploy multiple instances behind a load balancer
  • Use sticky sessions for file uploads
  • Share MongoDB connection across instances
  • Consider read replicas for heavy read workloads

Storage Scaling:

  • Monitor volume usage regularly
  • Scale upload volume as user content grows
  • Consider S3 for long-term file storage
  • Implement file cleanup policies for temporary uploads

Troubleshooting

Application Won’t Start

Check MongoDB Connection:

Terminal window
# Test MongoDB connectivity
mongosh "mongodb://expressa:password@your-mongodb-app.klutch.sh:8000/expressa?authSource=admin"

Verify Environment Variables:

  • Ensure MONGODB_URI is correctly formatted
  • Check that JWT_SECRET is set
  • Verify admin credentials are configured
  • Confirm PORT is set to 3000

Check Logs:

Review deployment logs in Klutch.sh dashboard for:

  • MongoDB connection errors
  • Missing environment variables
  • Port binding issues
  • Node.js runtime errors

Cannot Access Admin Interface

Common Issues:

  1. Wrong URL:

    • Admin interface is at /admin not /
    • Correct URL: https://example-app.klutch.sh/admin
  2. Invalid Credentials:

    • Verify ADMIN_EMAIL and ADMIN_PASSWORD in environment variables
    • Check for typos in credentials
    • Admin user is created on first startup
  3. CORS Issues:

    • If accessing from different domain, configure CORS_ORIGIN
    • Check browser console for CORS errors

API Requests Failing

Authentication Errors:

Terminal window
# Verify JWT token is valid
curl https://example-app.klutch.sh/api/user/me \
-H "Authorization: Bearer YOUR_TOKEN"

Permission Errors:

  • Check collection permissions in admin interface
  • Verify user role matches required permissions
  • Review ENFORCE_PERMISSIONS setting

Validation Errors:

  • Ensure request body matches collection schema
  • Check required fields are present
  • Verify data types match schema definitions

File Upload Issues

Check Volume Mount:

Ensure /app/uploads volume is properly mounted:

Terminal window
# Inside container (for debugging)
ls -la /app/uploads
# Should be writable

Check File Size Limits:

Terminal window
MAX_UPLOAD_SIZE=10485760 # 10MB in bytes

Verify File Types:

Configure allowed file types in collection schema:

{
"image": {
"type": "string",
"format": "file",
"pattern": "\\.(jpg|jpeg|png|gif)$"
}
}

Database Performance Issues

Slow Queries:

// Enable MongoDB slow query logging
db.setProfilingLevel(1, { slowms: 100 });
// View slow queries
db.system.profile.find().sort({ ts: -1 }).limit(10);

Connection Pool:

Terminal window
# Increase pool size if needed
MONGODB_POOL_SIZE=20

Missing Indexes:

// Check index usage
db.blog_posts.explain().find({ published: true });
// Add missing indexes
db.blog_posts.createIndex({ field: 1 });

Additional Resources

Conclusion

Deploying Expressa on Klutch.sh provides you with a powerful, flexible backend that turns your database into a full-featured API with minimal code. Whether you’re building a mobile app, a content management system, or prototyping a new product, Expressa accelerates development by eliminating boilerplate API code while providing enterprise features like authentication, permissions, and file uploads. With Klutch.sh’s managed infrastructure, persistent storage, and MongoDB integration, your Expressa backend is production-ready, scalable, and always available.

Start building your next API in minutes by deploying Expressa on Klutch.sh today and experience the power of schema-driven development.