Production-ready Express.js application starter kit
Sequelize · Prometheus metrics · Structured logging · CLI scaffolding · Redis · Swagger
|
🛡️ Security First
|
📊 Observability
|
|
🗄️ Database & Cache
|
🔧 Developer Experience
|
npm install express-starter-proconst { createApp } = require('express-starter-pro');
async function main() {
const { app, ready } = await createApp({
port: 3000,
database: { dialect: 'sqlite', storage: './data.db' },
});
await ready;
const server = app.listen(3000, () => console.log('🚀 Server running on port 3000'));
// Wire server into shutdown handler for graceful shutdown
const { setServer } = await createApp({ ... });
setServer(server);
}
main().catch(console.error);| Option | Type | Default | Description |
|---|---|---|---|
port |
number |
3000 |
Server port |
nodeEnv |
string |
'development' |
Environment (development, production, test) |
timeout |
boolean|number |
30000 |
Request timeout in ms |
shutdownTimeout |
number |
30000 |
Graceful shutdown timeout in ms |
jsonLimit |
string |
'1mb' |
JSON body size limit |
urlencodedLimit |
string |
'1mb' |
URL-encoded body size limit |
staticDir |
boolean|string |
false |
Static files directory |
sessionSecret |
string|null |
null |
Secret for sessions & CSRF |
| Option | Type | Default | Description |
|---|---|---|---|
helmet |
boolean|object |
true |
Security headers |
cors |
boolean|object |
true |
Cross-origin requests |
compression |
boolean|object |
{ level: 6 } |
Response compression |
morgan |
boolean|string |
'dev' |
HTTP request logging format |
cookieParser |
boolean|string |
true |
Cookie parsing (string = secret) |
rateLimit |
boolean|object |
{ windowMs: 900000, max: 100 } |
Rate limiting |
session |
boolean |
false |
Session support (Redis store when Redis enabled) |
csrf |
boolean |
false |
CSRF protection with replay prevention |
requestId |
boolean |
true |
Adds X-Request-Id header |
cache |
boolean|object |
{ ttl: 60, enabled: false, maxSize: 500 } |
Response caching (LRU + optional Redis) |
xssClean |
boolean |
true |
XSS sanitization |
hpp |
boolean |
true |
HTTP parameter pollution protection |
noCacheAuthRoutes |
boolean |
true |
Disable caching on auth routes |
| Option | Type | Default | Description |
|---|---|---|---|
database.url |
string|null |
null |
Database URL (overrides other settings) |
database.dialect |
string |
'sqlite' |
postgres, mysql, sqlite, mariadb, mssql |
database.storage |
string |
':memory:' |
SQLite storage path |
database.host |
string |
— | Database host |
database.port |
number |
— | Database port |
database.pool.max |
number |
10 |
Max pool size |
database.pool.min |
number |
0 |
Min pool size |
database.retry.max |
number |
3 |
Connection retry count |
database.sync |
boolean|object |
false |
Auto-sync models ({ alter: true } or { force: true }) |
| Option | Type | Default | Description |
|---|---|---|---|
redis.url |
string|null |
null |
Redis connection URL (redis://localhost:6379) |
redis.enabled |
boolean |
false |
Enable Redis integration |
When Redis is enabled, it automatically powers:
- Session store — persistent sessions via
connect-redis - Rate-limit store — distributed rate limiting via
rate-limit-redis - Cache backend — shared response cache across instances
- CSRF token tracking — replay prevention across restarts
| Option | Type | Default | Description |
|---|---|---|---|
healthCheck.path |
string |
'/health' |
Custom health check endpoint path |
healthCheck.readyPath |
string |
'/ready' |
Custom readiness endpoint path |
swagger |
boolean|object |
false |
Enable OpenAPI docs at /api-docs |
swagger.title |
string |
'Express Starter API' |
API title |
swagger.version |
string |
'1.0.0' |
API version |
validation.enabled |
boolean |
false |
Enable Zod validation middleware |
beforeRoutes |
function |
— | Hook to add routes before built-in routes |
afterRoutes |
function |
— | Hook to add routes after built-in routes |
models |
string |
'models/**/*.js' |
Glob pattern for auto-loading models |
transactionMiddleware |
boolean |
false |
Enable per-request transactions |
Returns Promise<CreateAppResult>:
| Property | Type | Description |
|---|---|---|
app |
Express |
Configured Express application instance |
ready |
Promise<void> |
Resolves when database connection is established |
shutdown |
() => Promise<void> |
Gracefully shut down server, DB, and Redis |
sequelize |
Sequelize |
Sequelize ORM instance |
metrics |
MetricsInterface |
Prometheus metrics registry |
redisClient |
RedisClientType|null |
Redis client (if enabled) |
setServer |
(server) => void |
Wire HTTP server into shutdown handler |
| Endpoint | Method | Description |
|---|---|---|
/health (or custom) |
GET | { status: 'ok', uptime, timestamp } |
/ready (or custom) |
GET | { status: 'ready' } or 503 if DB is down |
/metrics |
GET | Prometheus metrics in text/plain format |
/api-docs |
GET | OpenAPI 3.0.3 spec (JSON) or Swagger UI (HTML) |
const { app } = await createApp({
beforeRoutes: (app) => {
// These routes run BEFORE built-in health/metrics endpoints
app.get('/api/public', (req, res) => res.json({ public: true }));
},
afterRoutes: (app) => {
// These routes run AFTER built-in endpoints but BEFORE 404 handler
app.get('/api/catchall', (req, res) => res.json({ caught: true }));
},
});const { z } = require('zod');
const { createValidationMiddleware } = require('express-starter-pro');
app.post('/users', createValidationMiddleware({
body: z.object({
email: z.string().email(),
age: z.number().min(0).max(150),
}),
query: z.object({
include: z.string().optional(),
}),
}), (req, res) => {
// req.body is validated and typed
res.json(req.body);
});const { app, ready, setServer } = await createApp(config);
await ready;
const server = app.listen(3000);
setServer(server); // Ensures HTTP server closes on shutdown
// Or manually:
await appInstance.shutdown();npx express-starter-pro init <projectName> [options]| Option | Description | Default |
|---|---|---|
--template |
Template: basic, auth, websocket, full |
basic |
--db |
Database: postgres, mysql, sqlite |
postgres |
--redis |
Include Redis support | false |
--force |
Overwrite existing directory | false |
# Basic project
npx express-starter-pro init my-app
# Auth template with MySQL
npx express-starter-pro init my-app --template auth --db mysql
# Full stack with Redis
npx express-starter-pro init my-app --template full --redisEach scaffolded project includes:
src/index.js— entry point withcreateAppmodels/User.js— example Sequelize modelroutes/auth.js— auth routes (auth/full templates)__tests__/app.test.js— basic integration testsDockerfile+docker-compose.yml.env.example+.gitignorepackage.jsonwith all dependencies
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3000 |
NODE_ENV |
Environment | development |
DATABASE_URL |
Database connection string | — |
SESSION_SECRET |
Session secret | — |
REDIS_URL |
Redis connection string | — |
LOG_LEVEL |
Log level (error/warn/info/debug) |
debug |
// models/User.js
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const User = sequelize.define('User', {
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
email: { type: DataTypes.STRING, allowNull: false, unique: true },
name: { type: DataTypes.STRING, allowNull: false },
});
User.associate = (models) => {
User.hasMany(models.Post);
};
return User;
};Models are auto-loaded from the models/ directory. Each file exports a function (sequelize) => Model.
Docker
docker build -t my-app .
docker run -p 3000:3000 my-appDocker Compose
docker-compose upPM2
npm install -g pm2
pm2 start index.js --name my-appKubernetes
kubectl apply -f k8s/deployment.yaml| Step | From Vanilla Express | To express-starter-pro |
|---|---|---|
| 1 | npm install express |
npm install express-starter-pro |
| 2 | const app = express() |
const { app, ready } = await createApp(config) |
| 3 | Manual helmet/cors/morgan setup | Auto-configured via config |
| 4 | app.listen(3000) |
await ready; app.listen(3000) |
| 5 | console.log |
req.log.info(...) |
| 6 | Manual error handling | Built-in error handler |
| 7 | Manual shutdown | setServer(server) + auto shutdown on SIGTERM/SIGINT |
| Issue | Solution |
|---|---|
| Database connection fails | Check DATABASE_URL, ensure DB is running, verify credentials |
| Session not working | Set SESSION_SECRET and enable session: true in config |
| Rate limiting too strict | Adjust rateLimit.windowMs and rateLimit.max |
| CSRF errors on POST | Include X-CSRF-Token header from the csrf-token cookie |
| Redis connection refused | Ensure Redis is running, or set redis.enabled: false to fall back gracefully |
| Cache not working | Enable with cache: { enabled: true, ttl: 60 } |
| Metrics not showing | Hit /metrics endpoint after some requests have been made |
| Swagger UI not loading | Enable with swagger: true and visit /api-docs in a browser |
| Validation errors | Use createValidationMiddleware({ body: zodSchema }) on your routes |
Contributions are welcome! See CONTRIBUTING.md for guidelines.
MIT © Jpkoech30