Common questions about installation, TypeScript configuration, PostgreSQL, WebSockets, and deployment.
Street is a production-grade TypeScript backend framework built entirely on Node.js core modules. It provides an HTTP server, router, dependency injection container, PostgreSQL driver, WebSocket server, security primitives, and a CLI — all without Express, pg, Prisma, or other heavy abstractions.
Most Node.js frameworks layer abstractions on top of abstractions. Street takes the opposite approach: implement each component directly on Node.js core, enforce strict memory bounds, and expose a clean TypeScript API. The result is a framework where you can read and understand every line of the runtime.
Yes. Street is designed for production from the ground up — bounded memory, parameterized queries, SCRAM-SHA-256 PostgreSQL auth, AES-256-GCM sessions, and a comprehensive test suite including memory leak, wire protocol, load, fuzz, chaos, and security tests.
reflect-metadata — enables TypeScript’s emitDecoratorMetadata for constructor injectionws — WebSocket framing protocol (Node.js http.Server handles upgrades but not framing)Everything else — HTTP, TLS, streams, crypto, cluster — ships with Node.js.
Node.js 20 or higher. Street uses node:test, top-level await, crypto.randomUUID(), and other Node 20 APIs.
TypeScript 5.0 or higher with NodeNext module resolution.
reflect-metadata separately?Yes. Add it to your project:
1
npm install reflect-metadata
And import it as the first line of your entry point:
1
2
import 'reflect-metadata'; // must be first
import { streetApp } from '@streetjs/core';
Yes. Install @streetjs/core directly and set up your project manually. The CLI (@streetjs/cli) is optional tooling.
NodeNext module resolution?NodeNext is the correct module resolution mode for Node.js ESM. It requires explicit .js extensions on imports, which matches how Node.js resolves modules at runtime. Other modes (bundler, node16) have subtle differences that cause issues in production.
.js extensions in .ts files?This is required by NodeNext module resolution. TypeScript resolves .ts files when it encounters .js imports during compilation, but the compiled output uses .js — which is what Node.js needs at runtime.
1
2
// Correct — TypeScript resolves this to user.service.ts during compilation
import { UserService } from './user.service.js';
error TS1240: Unable to resolve signature of class decoratorAdd these to your tsconfig.json:
1
2
3
4
5
6
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
The built-in driver is PostgreSQL-only (wire protocol v3). For other databases, use the database’s official Node.js driver and register it manually with the container.
pg?No. Street implements the PostgreSQL wire protocol directly over node:net. There is no pg dependency.
1
2
3
4
5
6
# Using the CLI
street migrate:create create_users_table
street migrate:run
# Or directly
node dist/main.js migrate
Yes. PgPool manages a bounded pool of connections with idle timeout, acquire timeout, and automatic dead connection replacement.
Yes, when you use parameterized queries:
1
2
3
4
5
// Safe — parameterized
await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
// Unsafe — never do this
await pool.query(`SELECT * FROM users WHERE id = '${userId}'`);
Pass maxConnections to StreetWebSocketServer:
1
const wss = new StreetWebSocketServer({ maxConnections: 10_000 });
Connections beyond the limit are rejected with close code 1013 (Try Again Later).
Pass a JWT token as a query parameter and validate it in the connection handler:
1
2
3
4
5
6
wss.on('connection', (socket, req) => {
const url = new URL(req.url!, 'http://localhost');
const token = url.searchParams.get('token');
if (!token) { socket.close(4001, 'Unauthorized'); return; }
// verify token...
});
Every component has explicit bounds:
| Component | Bound |
|---|---|
| HTTP body | 1 MB default (configurable) |
| File uploads | Disk only — ≤128 KB heap |
| DB results | 256 rows buffered |
| LRU cache | maxEntries cap |
| Rate limiter | 100K IPs, 1K timestamps/IP |
| WebSocket connections | maxConnections |
Yes. Use ClusterCoordinator to spawn worker processes:
1
2
3
4
import { ClusterCoordinator } from '@streetjs/core';
const coordinator = new ClusterCoordinator({ workers: 4 });
coordinator.start(() => bootstrap());
Yes. The generated Dockerfile uses a multi-stage build:
1
2
docker build -t my-api .
docker run -p 3000:3000 --env-file .env my-api
Yes. Set HOST=0.0.0.0 and let the proxy handle TLS termination. Trust the X-Forwarded-For header for real IP detection in the rate limiter.
At minimum:
NODE_ENV=productionJWT_SECRET — at least 32 random charactersSESSION_KEY — 64-character hex stringPG_HOST, PG_PORT, PG_DATABASE, PG_USER, PG_PASSWORDOpen an issue at github.com/hassanmubiru/issues with a minimal reproduction.
See the Contributing Guide.
See the Roadmap.