#
Bootstrap
The bootstrap process creates the ServerApp object that gets passed throughout the application. This happens once at startup and provides every component with access to shared services like the database, logger, and WebSocket manager.
#
How Startup Works
When the server starts, src/server/index.ts checks the WORKER_ID environment variable. If set, it runs as a worker process. Otherwise, it starts the main HTTP server.
The main server path calls createApp() in start-server.ts, which initializes services in order:
- Sentry — Error tracking and performance monitoring (if configured)
- SocketManager — WebSocket connection handling
- WorkerManager — Spawns worker child processes
- ServerApp — Created via
createServerApp()inbootstrap.ts - ElysiaJS — HTTP server with GraphQL, health checks, static files
Worker processes follow a simpler path. They create a ServerApp without the worker manager (to avoid circular spawning) and run the job polling loop.
#
The createServerApp Function
The core bootstrap logic lives in src/server/bootstrap.ts. It connects to the database, creates blockchain clients if Web3 is enabled, and assembles everything into the ServerApp object:
export async function createServerApp(options: {
includeWorkerManager?: boolean
workerCountOverride?: number
socketManager: ISocketManager
rootLogger: Logger
}): Promise<ServerApp> {
const db = await dbManager.connect()
// Web3 clients only when enabled
const { publicClient, walletClient } = createBlockchainClients(rootLogger)
// Notification helper that persists to DB and sends via WebSocket
const createNotification = async (userId: number, data: NotificationData) => {
// Inserts to DB and sends via WebSocket
}
const baseServerApp = { db, rootLogger, createLogger, startSpan, ... }
if (includeWorkerManager) {
return { ...baseServerApp, workerManager: await createWorkerManager(...) }
}
return baseServerApp
}
#
Using ServerApp
GraphQL resolvers receive ServerApp through their context. The GraphQL handler builds the context for each request:
// In src/server/graphql/index.ts
const context = {
serverApp,
user: authenticatedUser, // null if not authenticated
operationName,
requiresAuth
}
Resolvers then access services through this context:
const resolvers = {
Query: {
getMyNotifications: async (_, { limit, offset }, context) => {
const { serverApp, user } = context
return getNotifications(serverApp.db, user.id, limit, offset)
}
}
}
Worker jobs receive ServerApp as their first parameter along with job data:
export const run: JobRunner = async ({ serverApp, log, job }) => {
// Full access to database, blockchain clients, etc.
const result = await serverApp.db.select().from(settings)
log.info("Job completed", { result })
}
#
Configuration
The bootstrap process loads configuration through a layered system:
.env— Base configuration, committed to git.env.{NODE_ENV}— Environment-specific overrides.env.local— Local developer overrides, gitignored
Access configuration through the typed serverConfig object. Never read process.env directly in application code—this ensures type safety and consistent defaults.
#
Testing
Tests create their own ServerApp with the worker manager disabled:
const serverApp = await createServerApp({
socketManager: createTestSocketManager(),
workerManager: undefined
})
The test helpers in tests/helpers/ provide utilities for creating test servers, managing database state, and cleaning up between tests.
See src/server/bootstrap.ts for the complete implementation and src/server/types.ts for the ServerApp type definition.