Deploy a Node.js API
This tutorial walks you through deploying a Node.js backend — Express, Fastify, or NestJS — on Temps. By the end, you will have a production API with environment variables, health checks, and automatic HTTPS.
What you will build
- A Node.js API deployed from a Git repository
- Environment variables for database connections and secrets
- A health check endpoint for zero-downtime deployments
- Automatic HTTPS and deployment on every push
Time required: 10 minutes.
Prerequisites:
- A running Temps instance (install guide)
- A Node.js API in a Git repository
- (Optional) A database connection string
Step 1: Prepare your API
Your Node.js API needs two things to work on Temps: listen on the PORT environment variable and bind to 0.0.0.0.
import express from 'express';
const app = express();
app.use(express.json());
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.get('/api/users', async (req, res) => {
// Your business logic
res.json([]);
});
const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on port ${port}`);
});
package.json
Make sure you have start and build scripts (build is needed for TypeScript projects):
package.json
{
"name": "my-api",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"express": "^5.0.0"
},
"devDependencies": {
"typescript": "^5.7.0",
"tsx": "^4.0.0"
}
}
Temps detects express, fastify, or @nestjs/core in your dependencies and generates an appropriate Dockerfile. For NestJS, the dist/ output directory is used automatically. For plain Express or Fastify, Temps runs npm start.
Add a health check endpoint
Temps checks / by default to verify your app is healthy. Adding a dedicated /health endpoint is better practice — it responds quickly and does not trigger business logic:
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
Configure it in .temps.yaml:
.temps.yaml
health:
path: /health
Step 2: Create a project
- Open the Temps dashboard
- Click New Project
- Select your Git provider and repository
- Choose the branch to deploy
Temps detects your Node.js framework automatically. You will see it listed in the project configuration.
Step 3: Configure environment variables
Add environment variables in the project settings. Common ones for APIs:
# Database
DATABASE_URL=postgres://user:password@db-host:5432/mydb
# Secrets
JWT_SECRET=your-jwt-secret
API_KEY=your-api-key
# Application
NODE_ENV=production
LOG_LEVEL=info
All variables are available at runtime via process.env. They are injected when the container starts — not baked into the image.
Step 4: Deploy
Push a commit or click Deploy in the dashboard. Temps:
- Clones your repository
- Detects Node.js and your framework
- Installs dependencies (detected from your lockfile)
- Runs
npm run buildif a build script exists - Starts the container with
npm start - Sends health check requests until the API responds
A typical Node.js API builds in under a minute.
Step 5: Test your API
Once deployed, test your endpoints:
# Health check
curl https://your-api.your-domain.com/health
# API endpoint
curl https://your-api.your-domain.com/api/users
# POST request
curl -X POST https://your-api.your-domain.com/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
Connecting to a database
Using a Temps-managed database
If you added a PostgreSQL or MySQL service through Temps, the connection details are available as environment variables. Use them in your database client:
Database connection
import pg from 'pg';
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
Using an external database
Set the connection string as an environment variable in the dashboard. If the database requires SSL, most clients accept an ?sslmode=require query parameter:
DATABASE_URL=postgres://user:pass@external-host:5432/db?sslmode=require
Connection pooling
Always configure connection pool limits. A single container with 20-50 connections is typical. Set max in your pool configuration to avoid exhausting database connections.
TypeScript projects
For TypeScript APIs, Temps runs npm run build during the build step and npm start to run the compiled output.
Make sure your tsconfig.json outputs to a directory that your start script references:
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
package.json scripts
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
Production best practices
CORS
If your API is called from a different domain (e.g. a frontend on a different subdomain), configure CORS:
import cors from 'cors';
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://your-frontend.com',
credentials: true,
}));
Error handling
Add a global error handler to prevent unhandled errors from crashing the process:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal server error' });
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
});
Graceful shutdown
Handle SIGTERM to close connections cleanly during deployments:
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down gracefully');
await pool.end(); // Close database connections
server.close(() => {
process.exit(0);
});
});
Troubleshooting
"Cannot find module" at startup
Your start script points to a file that does not exist. For TypeScript projects, verify that npm run build produces output in the expected directory. Check that outDir in tsconfig.json matches the path in your start script.
Health check times out
Your API is starting but Temps cannot reach it. Common causes:
- Binding to
localhostinstead of0.0.0.0 - The port in your code does not match what Temps expects — use
process.env.PORT - A database migration or seed step is blocking startup — move these to a build script or separate endpoint
Database connection refused
The database host is unreachable from the container. For Temps-managed databases, use the service name as the hostname (visible in the dashboard). For external databases, ensure the database allows connections from your server's IP.