SpaceNode
SpaceNode is built for production. Here's what's included out of the box.
const app = await createApp({
timeout: 30000, // request timeout (default: 30s)
keepAliveTimeout: 5000, // keep-alive timeout (default: 5s)
})
The headersTimeout is automatically set to timeout + 1000ms to prevent a known Node.js bug.
app.close() // stops accepting new connections
The shutdown sequence runs in this order:
onDestroy hooks — all registered hooks are called (async-safe)1001 (Going Away)events.destroy() removes all listenersshutdownTimeout (default 5s), all remaining connections are forcefully terminatedSpaceNode also registers SIGTERM and SIGINT handlers automatically, so Ctrl+C and docker stop trigger a clean shutdown.
If an error occurs inside the error handler itself, SpaceNode catches it and sends a generic 500 response. The server stays alive.
If a handler doesn't call send(), the framework sends 204 No Content. No hanging connections.
Request bodies limited to 1 MB by default. Configure with bodyLimit:
const app = await createApp({
bodyLimit: 5 * 1024 * 1024, // 5 MB
})
Larger bodies trigger 413, connection destroyed.
SpaceNode automatically parses JSON, URL-encoded forms, and multipart/form-data requests. See Body Parsing for details.
When static file serving is enabled, files are cached in-memory via an LRU cache with ETag support and 304 Not Modified responses:
const app = await createApp({
static: './public',
staticCacheMax: 500, // max cached files (default: 500)
staticCacheFileSize: 256 * 1024, // max file size to cache (default: 256 KB)
})
Files exceeding staticCacheFileSize are streamed from disk. See Static Files for details.
SpaceNode includes a built-in Logger with levels and custom transports:
import { Logger } from 'SpaceNode'
const logger = new Logger({ level: 'info' })
logger.info('Server started')
logger.error('Something failed', { code: 500 })
Pass logger options to createApp config for framework-level logging:
const app = await createApp({
logger: { level: 'warn' },
})
Create prefixed child loggers for per-module or per-service context:
const logger = new Logger({ level: 'info' })
const dbLog = logger.child('db')
const authLog = logger.child('auth')
dbLog.info('Connected') // INFO [db] Connected
authLog.warn('Token expired') // WARN [auth] Token expired
Child loggers inherit the parent’s log level and timestamps. Useful for filtering logs by subsystem in production.
watch: true in production. It is a development convenience (like nodemon) that restarts the server on file changes. In production, use a process manager (PM2, systemd, Docker) instead.
import { createApp, defineAuth } from 'SpaceNode'
defineAuth(async (token) => { /* verify */ })
const app = await createApp({
db: dbConnection,
timeout: 15000,
keepAliveTimeout: 5000,
shutdownTimeout: 10000,
bodyLimit: 5 * 1024 * 1024, // 5 MB
pipe: ['cors:https://mysite.com', 'logger', 'rateLimit:500', 'security', 'compress'],
openapi: { title: 'My API', version: '1.0.0' },
})
app.onError((err, req) => {
// Send to monitoring service
})
app.listen(process.env.PORT || 3000)
process.on('SIGINT', () => app.close(() => process.exit(0)))
process.on('SIGTERM', () => app.close(() => process.exit(0)))