Nile Server
Type: Reference / Specification
Path: nile/
1. Purpose
The Nile Server module provides the top-level factory for bootstrapping a Nile application. createNileServer is the single entry point developers use to wire together the Action Engine, shared context, and interface layers (REST, and later WebSocket/RPC).
1.1 Responsibilities
- Bootstrapping: Create and connect the Action Engine,
NileContext, and REST interface from a singleServerConfig - Context ownership: Create a single
NileContextinstance shared across all interfaces - Context access: Export
getContext()to retrieve the runtime context from anywhere within a request scope - Lifecycle: Execute
onBoothooks after initialization with crash safety - Diagnostics: Route diagnostic output through
createDiagnosticsLogfromutils/diagnostics-log.ts, which checksresources.loggerfirst and falls back toconsole.log. Seedocs/internals/logging.mdsection 7.
1.2 Non-Goals
- Transport logic: The server module does not handle HTTP routing, CORS, or request parsing. That is the REST layer's responsibility.
- Engine internals: The server does not manage action execution or hook pipelines. It delegates to the engine.
2. createNileServer
Path: nile/server.ts
2.1 Initialization Sequence
- Validate: Throws immediately if
config.servicesis empty - Create
NileContext: Single instance withconfig.resourcesattached - Create Engine: Passes
services,diagnostics, and global hook handlers - Log services table: When
config.logServicesistrue, prints aconsole.tableof registered services (name, description, actions). Always prints, not gated bydiagnostics - Run
onBoot: If configured, awaits the boot callback inside aPromise.racewith a configurable timeout (maxWaitTime, default 10s). On success, logsBooted in Xms. On failure or timeout, logs via diagnostics logger and callsprocess.exit(1). The function does not return until boot completes. Boot timing is always visible viaconsole.log - Create REST app: Only if
config.restis provided. Passes engine, context,serverName, andruntime(defaults to"bun") - Print REST endpoint URLs: When REST is configured, prints
POST http://host:port/baseUrl/servicesand optionallyGET http://host:port/statusviaconsole.log. Usesrest.host(default"localhost") andrest.port(default8000)
2.2 Return Value (NileServer)
restis only present whenconfig.restwas providedengineprovides direct access togetServices,getServiceActions,getAction,executeActioncontextis the sharedNileContextpassed to all layersaddMiddlewareregisters middleware that runs before the services POST handler. Middleware is executed in registration order via a dynamic runner. A middleware can return aResponseto short-circuit the request (skipping downstream middleware and the handler).
3. ServerConfig
runtimelives only onServerConfigand is piped tocreateRestAppas a parameter. It is not duplicated ontoRestConfig.servicesis required. An empty array throws at initialization.diagnosticsdefaults tofalse. When enabled, internal modules emit diagnostic output throughcreateDiagnosticsLog.logServicesdefaults totrue. Prints aconsole.tableof registered services (Service, Description, Actions count). Not gated bydiagnostics. SetlogServices: falseto suppress.- When REST is configured, endpoint URLs are always printed via
console.logusingrest.host(default"localhost") andrest.port(default8000). forceNewInstancedefaults tofalse. Whenfalse, a secondcreateNileServercall returns the existing server instance with a warning logged. Set totrueto explicitly create a new instance (useful in tests).
4. NileContext
Path: nile/nile.ts
Factory: createNileContext(params?)
The context is a singleton per server. It carries interface-specific data, hook execution state, session storage, and a general-purpose key-value store. It supports an optional TDB generic to provide type safety for the database resource.
4.1 Key-Value Store
_store is a Map<string, unknown> exposed as readonly. Use get/set methods for access.
4.2 Sessions
Each NileContext owns its own session store. Multiple server instances do not share session state.
Session keys are "rest" | "ws" | "rpc", matching the interface types.
4.3 Hook Context
hookContext tracks the lifecycle of a single action execution. It is reset at the start of each executeAction call via resetHookContext(actionName, input).
Mutation methods: updateHookState, addHookLog, setHookError, setHookOutput.
4.4 Request-Scoped Contexts (AsyncLocalStorage)
Interface-specific data (rest, ws, rpc, sessions) is isolated per-request via AsyncLocalStorage. Concurrent requests never see each other's state.
The REST layer wraps each incoming request in runInRequestScope(), which creates an isolated RequestStore for that request's lifetime. All async continuations within the request see the same store.
Key exports:
RequestStore: interface for per-request state (rest,ws,rpc,sessions)runInRequestScope(store, fn): runs a callback within an isolated request scopegetRequestStore(): retrieves the current request's store (undefined outside a request)
4.5 Resources
Extensible via index signature. Passed through from ServerConfig.resources. The database field is typed as TDB (defaulting to unknown).
5. Key Types
5.1 BeforeActionHandler
Global hook that runs before every action. Returns a Result. Err halts the pipeline.
5.2 AfterActionHandler
Global hook that runs after every action. Receives the action result and can transform it.
5.3 Sessions
5.4 Resources
The logger field accepts a NileLogger. The return type of createLogger from the logging module enables handleError and createDiagnosticsLog to log through the same logger instance.
6. Constraints
- One context per server:
createNileContextis called once increateNileServer. All interfaces share this instance. - Generic Database Support: To avoid generic leakage into the core engine, the database type
TDBis only present inNileContextandResources. High-level components (Engine, REST) useunknown. createNileServeris async: The function returns aPromise<NileServer>. Useawaitor.then(). Requires ES modules (top-level await) or wrapping in an async function.onBootcrashes on failure: TheonBootcallback is awaited inside aPromise.racewith a configurable timeout (maxWaitTime, default 10s). If it fails or times out,process.exit(1)is called. The server never returns in a degraded state.- Singleton by default: A second
createNileServercall returns the existing instance unlessforceNewInstance: trueis passed. A warning is logged when the cached instance is returned. - Runtime default: If
config.runtimeis omitted, it defaults to"bun". This affects static file serving and runtime-specific behavior. - No dynamic service injection: Services are fixed at boot time. Adding services after initialization is not supported.
7. Failure Modes
- Empty services:
createNileServerthrows immediately with a descriptive error onBootcrash or timeout: Logged via diagnostics logger, thenprocess.exit(1). The server does not start in a degraded state. IfmaxWaitTimeis exceeded, the error message includes the configured timeout value.- Missing resources:
resourcesis optional. Diagnostics fall back toconsole.logwhenresources.loggeris absent (handled bycreateDiagnosticsLog) - Double initialization: Returns cached instance with a warning unless
forceNewInstance: true
8. getContext
Path: nile/server.ts
Exported function that retrieves the runtime NileContext from anywhere within a request scope. It accepts an optional TDB generic for type-safe database access. The context is stored in a module-level variable set during createNileServer initialization.
8.1 Usage Pattern
getContext is designed to be called from action handlers or utility functions that need access to the context but don't receive it as a parameter:
8.2 Constraints
- Must be called after server initialization:
getContextthrows if called beforecreateNileServerhas run - Must be called within a request scope: The context singleton is set at server boot. Per-request data (interface contexts, sessions) is isolated via AsyncLocalStorage. Use
context.get("rest")orcontext.getSession("rest")within request handlers
8.3 Failure Modes
- Called before server boot: Throws
"getContext: Server not initialized. Call createNileServer first."