@zyno-io/dk-server-foundation
v26.225.339
Published
TypeScript server foundation built on the Deepkit framework
Readme
@zyno-io/dk-server-foundation
A TypeScript foundation library built on top of the Deepkit framework for building server applications. Provides opinionated abstractions and utilities for database management, HTTP handling, authentication, background jobs, RPC, distributed systems, and observability.
📚 Documentation Site | 🚀 Getting Started
Install
npm install @zyno-io/dk-server-foundation
# or
yarn add @zyno-io/dk-server-foundationThe postinstall script runs deepkit-type-install and patch-package automatically.
Requirement: TypeScript with
experimentalDecorators: trueand Deepkit's"reflection": truein tsconfig.json.
Quick Start
import { createApp, BaseAppConfig, createMySQLDatabase } from '@zyno-io/dk-server-foundation';
class AppConfig extends BaseAppConfig {
MY_SETTING!: string;
}
class MyDB extends createMySQLDatabase({}, [UserEntity]) {}
const app = createApp({
config: AppConfig,
db: MyDB,
cors: config => ({ hosts: ['https://example.com'], credentials: true }),
controllers: [UserController],
providers: [UserService]
});
app.run();This gives you an HTTP server on port 3000 with health checks, CORS, database, logging, and OpenAPI docs (in development).
Modules
| Module | Description | Docs | | --------------------------------------- | ---------------------------------------------------------- | -------------------------------------------- | | Application | App factory, config, DI resolution | Getting Started | | Graceful Shutdown | Pre-shutdown event for async cleanup | | | Configuration | Environment-based config with secrets support | Configuration | | Database | MySQL/MariaDB ORM with transactions, hooks, and migrations | Database | | HTTP | Custom HTTP kernel, middleware, uploads, CORS | HTTP | | Authentication | JWT and Basic Auth with entity resolution | Authentication | | Workers | BullMQ background job processing | Workers | | SRPC | Bidirectional WebSocket RPC with binary streams | SRPC | | Leader Election | Redis-based distributed leader election | Leader Service | | Mesh Networking | Typed RPC between distributed instances | Mesh Service | | Mail | Email via Postmark or SMTP with templates | Mail | | Telemetry | OpenTelemetry and Sentry integration | Telemetry | | Health Checks | Liveness endpoint with pluggable checks | Health | | Helpers | Async context, Redis cache/mutex, crypto, and more | Helpers | | Testing | Test facades, fixtures, and request mocking | Testing | | Types | Phone numbers, dates, coordinates, and utility types | Types | | Logging | Scoped Pino logger with context tracking | Logging | | DevConsole | Built-in web dashboard for development monitoring | DevConsole | | CLI Tools | REPL, provider invoke, migrations, proto generation | CLI |
Application
Applications are created with createApp(), which sets up a Deepkit app with opinionated defaults: custom HTTP kernel, health checks, config loading from environment variables, CORS, and optional worker/RPC support.
const app = createApp({
config: MyConfig,
db: MyDB,
enableWorker: true,
cors: config => ({ hosts: [config.CORS_ORIGIN], credentials: true })
});Services decorated with @AutoStart() are instantiated at startup for establishing connections or background processes.
Use resolve() (or its alias r()) to access any provider from the DI container outside of constructor injection:
import { r } from '@zyno-io/dk-server-foundation';
const db = r(MyDatabase);Graceful Shutdown
createApp() automatically installs a ShutdownListener that intercepts SIGTERM/SIGINT and dispatches an onServerShutdownRequested event before forwarding to Deepkit's built-in shutdown. This lets you perform async cleanup (drain connections, finish in-flight work) before the framework tears down.
import { eventDispatcher } from '@deepkit/event';
import { onServerShutdownRequested } from '@zyno-io/dk-server-foundation';
class MyShutdownHandler {
@eventDispatcher.listen(onServerShutdownRequested)
async onShutdownRequested() {
// Async cleanup here — this handler is awaited before
// Deepkit's onServerShutdown fires.
}
}Register your handler as a listener in createApp():
const app = createApp({
config: AppConfig,
listeners: [MyShutdownHandler]
});Configuration
Extend BaseAppConfig to define application settings. All properties are loaded from environment variables via @zyno-io/config. Properties ending in _SECRET are treated as secrets.
class AppConfig extends BaseAppConfig {
STRIPE_API_KEY_SECRET!: string;
MAX_UPLOAD_SIZE: number = 10_000_000;
}See Configuration for the full list of built-in settings.
Database
Extends Deepkit's ORM with support for both MySQL and PostgreSQL: transaction hooks, session locks, raw query helpers, and entity creation utilities.
class MyDB extends createMySQLDatabase({}, [User, Post]) {}
// or
class MyDB extends createPostgresDatabase({}, [User, Post]) {}
// Entity creation
const user = await createPersistedEntity(User, { email: '[email protected]', name: 'Alice' }, session);
// Transaction with hooks
await db.transaction(async session => {
session.addPostCommitHook(async () => {
await notifyUser(user);
});
await session.flush();
});
// Session locks (MySQL: _locks table, PostgreSQL: pg_advisory_xact_lock)
await db.transaction(async session => {
await session.acquireSessionLock(['wallet', walletId]);
// Lock held until commit/rollback
});Migrations
Generate migrations by diffing entity definitions against the live database:
# Interactive mode (prompts for column renames)
ts-node app.ts migration:create
# Non-interactive (CI-safe, treats ambiguous changes as drop+add)
ts-node app.ts migration:create --non-interactive
# Run pending migrations
ts-node app.ts migration:runThe migration:create command reads entity metadata via Deepkit reflection, introspects the database schema, and generates dialect-appropriate DDL covering: table creation/removal, column additions/removals/modifications/renames, index and foreign key changes, primary key changes, and PostgreSQL enum type management. Migration files use the createMigration() format:
import { createMigration } from '@zyno-io/dk-server-foundation';
export default createMigration(async db => {
await db.rawExecute(`ALTER TABLE \`users\` ADD COLUMN \`bio\` varchar(500) NULL AFTER \`email\``);
});HTTP
Custom HTTP kernel with configurable request logging, middleware that preserves HTTP error codes, file uploads, and multi-origin CORS support.
// Custom middleware
class RateLimitMiddleware extends HttpMiddleware {
async handle(request: HttpRequest, response: HttpResponse) {
// Rate limiting logic; throw HttpError to reject
}
}Response type helpers (OkResponse, RedirectResponse, EmptyResponse, AnyResponse) are used as return type annotations on controller methods.
Authentication
JWT (HS256/EdDSA) and HTTP Basic Auth with request-scoped caching and entity resolution.
// Create auth middleware that validates JWT and checks entity existence
const authMiddleware = createAuthMiddleware(User);
// Use as route middleware
@http.GET('/me').use(authMiddleware)
async getMe(/* ... */) {
// JWT is validated; use getEntityFromRequestJwt to load the entity
}Workers
BullMQ-based background job processing. Define jobs with @WorkerJob() and queue them via WorkerService.
@WorkerJob()
class SendEmailJob extends BaseJob<{ to: string; body: string }, void> {
async handle(data: { to: string; body: string }) {
await sendEmail(data.to, data.body);
}
}
// Queue a job
await workerService.queueJob(SendEmailJob, { to: '[email protected]', body: 'Hello' });Enable with enableWorker: true in createApp(). Jobs are automatically discovered and registered.
SRPC
Bidirectional RPC over WebSocket with HMAC authentication, ts-proto code generation, and multiplexed binary streams.
// Server
const server = new SrpcServer({ logger, clientMessage, serverMessage, wsPath: '/rpc' });
server.registerMessageHandler('uEcho', async (stream, data) => {
return { message: data.message };
});
// Client
const client = new SrpcClient(logger, 'wss://host/rpc', clientMessage, serverMessage, 'client-1');
const result = client.invoke('uEcho', { message: 'hello' });Generate TypeScript types from .proto files:
dksf-gen-proto input.proto output/Leader Election
Distributed leader election using Redis. Exactly one instance holds leadership at a time, with automatic renewal and failover.
const leader = new LeaderService('my-task');
leader.setBecameLeaderCallback(async () => {
/* start leader-only work */
});
leader.start();See Leader Service for full API documentation.
Mesh Networking
Typed RPC between distributed application instances. Nodes get unique IDs and can invoke handlers on any other node with full type safety.
type Messages = {
getStatus: { request: {}; response: { status: string } };
};
const mesh = new MeshService<Messages>('my-app');
mesh.registerHandler('getStatus', async () => ({ status: 'ok' }));
await mesh.start();See Mesh Service for full API documentation.
Email sending via Postmark or SMTP with a template system.
class WelcomeEmail extends MailTemplate<{ name: string }> {
subject = 'Welcome!';
generateHtml() {
return `<h1>Hello ${this.data.name}</h1>`;
}
}
await mailService.sendFromTemplate({
to: { address: '[email protected]' },
template: WelcomeEmail,
data: { name: 'Alice' }
});Telemetry
OpenTelemetry auto-instrumentation for HTTP, database, Redis, DNS, and BullMQ. Optional Sentry integration.
// Call before other imports
import { init } from '@zyno-io/dk-server-foundation/telemetry/otel';
init();
// Manual spans
import { withSpan } from '@zyno-io/dk-server-foundation';
await withSpan('processOrder', async () => {
// traced operation
});Health Checks
A /healthz endpoint is automatically registered by createApp(). Register additional checks via HealthcheckService.register(). When a database is configured, a connectivity check is added automatically.
class MyService {
constructor(private hcSvc: HealthcheckService) {
hcSvc.register(async () => {
// Throw to indicate unhealthy
await checkExternalDependency();
}, 'External API');
}
}Use checkIndividual() for per-check status results (used by DevConsole's Health view).
Helpers
Organized utility functions for common patterns:
- Async:
AsyncContext, semaphores, child process execution - Redis: Cache with TTL, distributed mutex, pub/sub broadcast channels
- Crypto: AES-256-GCM encryption, random string generation
- Data: Array/object manipulation, chainable
Transformerpipelines - Framework: Deepkit decorator utilities, event handler inheritance
// Distributed mutex
await withMutex({
key: 'user:123',
fn: async () => {
/* critical section */
}
});
// Redis cache
await Cache.setObj('key', data, 3600);
const data = await Cache.getObj<MyType>('key');
// Broadcast
const channel = createBroadcastChannel<MyEvent>('events');
channel.subscribe(data => handleEvent(data));
channel.publish({ type: 'update' });Testing
Test facades with per-test database isolation, entity fixtures, and request mocking.
const tf = TestingHelpers.createTestingFacade(app, {
enableDatabase: true,
seedData: async facade => {
await loadEntityFixtures([fixtures.user1, fixtures.user2]);
}
});
TestingHelpers.installStandardHooks(tf);
it('should return user', async () => {
const res = await makeMockRequest(tf, 'GET', '/api/users/1', {});
expect(res.statusCode).toBe(200);
});Types
Custom validated types for common patterns:
DateString-- MySQL DATE field (YYYY-MM-DD)PhoneNumber/PhoneNumberNANP-- Validated phone numbers via libphonenumberCoordinate-- MySQL POINT geometryEmailAddress-- Regex-validated emailTrimmedString/NonEmptyTrimmedString-- Auto-trimmed during deserializationValidDate-- Date that rejectsInvalid Date
Logging
Scoped Pino logger with async context tracking and error reporting.
const logger = createLogger(this); // or createLogger('MyService')
logger.info('Processing order', { orderId: 123 });
logger.error('Failed to process', err);CLI Tools
| Command | Description |
| -------------------------------------------- | -------------------------------------------------------- |
| dksf-dev <cmd> | Dev workflow: clean, build, run, migrate, repl, test |
| dksf-gen-proto <input> <output> | Generate TypeScript types from .proto files |
| dksf-install | Postinstall setup (patch-package + deepkit-type-install) |
| dksf-update | Update utility |
| repl | Interactive REPL with access to all providers |
| provider:invoke <provider> <method> [args] | Invoke any provider method from CLI |
| worker:start | Start the job runner (with leader-elected recorder) |
| worker:queue <jobName> [data] | Queue a job by name |
| migration:create | Generate migration from entity/DB schema diff |
| migration:run | Run pending database migrations |
| migration:reset | Reset migrations to a single base migration |
| migration:charset [charset] [collation] | Standardize database character set |
DevConsole
A built-in web dashboard for development-time monitoring and debugging, automatically enabled when APP_ENV !== 'production'. Access it at http://localhost:{PORT}/_devconsole/ — no setup required.
Features include an HTTP request inspector, SRPC connection monitor, database entity browser with SQL editor, worker job inspector, Redis mutex monitor, health check viewer, environment config display, OpenAPI schema viewer, and a live REPL with access to the DI container.
DevConsole is localhost-only and communicates over SRPC/WebSocket for real-time updates. See DevConsole for details.
A demo app showcasing all features is included:
yarn demoapp
# then open http://localhost:3000/_devconsole/Important Notes
- Timezone: The server enforces UTC. The entry point throws if
TZ !== UTC. - Identity Maps: Disabled by default in database sessions for predictable behavior.
- Development Mode: When
APP_ENV !== 'production', DevConsole is enabled at/_devconsole/, connection pools are smaller, and the worker runner auto-starts.
Commands
yarn build # Clean build
yarn build:dirty # Quick rebuild
yarn dev # Watch mode
yarn test # Run all tests
yarn format # Lint with oxlint + format with PrettierVersioning
This library uses calendar versioning in the format YY.MMDD.HHmm (e.g. 25.0214.1830). Versions are generated automatically from the CI pipeline timestamp. There are no stability guarantees between releases — pin to a specific version if you need predictability.
License
MIT. See LICENSE for details.
