Skip to content

Deep Dive: Application

Technical reference for AbstractApplication and BaseApplication - the foundation classes for every Ignis application.

Files:

  • packages/core/src/base/applications/abstract.ts
  • packages/core/src/base/applications/base.ts
  • packages/core/src/base/applications/types.ts

Quick Reference

ClassPurposeKey Methods
AbstractApplicationBase class with lifecycle management, server start/stopstart(), stop(), init(), validateEnvs()
BaseApplicationConcrete implementation with resource registration and boot supportcomponent(), controller(), service(), repository(), dataSource(), boot()

AbstractApplication

Base class responsible for core lifecycle and server management. Extends Container (IoC container) and implements IApplication.

typescript
abstract class AbstractApplication<
  AppEnv extends Env = Env,
  AppSchema extends Schema = {},
  BasePath extends string = '/',
> extends Container implements IApplication<AppEnv, AppSchema, BasePath>

Constructor

typescript
constructor(opts: { scope: string; config: IApplicationConfigs })

The constructor:

  1. Merges the provided config with defaults (host from HOST or APP_ENV_SERVER_HOST env, port from PORT or APP_ENV_SERVER_PORT env, defaults to localhost:3000)
  2. Enables asyncContext by default ({ enable: true })
  3. Sets strictPath to true by default for the Hono instance
  4. Creates two OpenAPIHono instances: the main server and a rootRouter
  5. Auto-detects the runtime (Bun or Node.js)

Key Features

FeatureDescription
Hono InstanceCreates and holds two OpenAPIHono instances — a main server and a root router
Runtime DetectionAuto-detects Bun or Node.js via RuntimeModules.detect() and uses the appropriate server implementation
Core BindingsRegisters CoreBindings.APPLICATION_INSTANCE, CoreBindings.APPLICATION_SERVER, and CoreBindings.APPLICATION_ROOT_ROUTER
Lifecycle ManagementDefines abstract methods (preConfigure, postConfigure, setupMiddlewares, staticConfigure, initialize, getAppInfo)
Environment ValidationValidates all registered applicationEnvironment keys are non-empty (unless ALLOW_EMPTY_ENV_VALUE is set)
Post-Start HooksSupports registering hooks that execute after the server starts

Abstract Methods

These must be implemented by subclasses:

MethodSignaturePurpose
getAppInfo()() => ValueOrPromise<IApplicationInfo>Return application metadata (name, version, description)
preConfigure()() => ValueOrPromise<void>Register resources before framework auto-configuration
postConfigure()() => ValueOrPromise<void>Logic after all resources are configured
staticConfigure()() => voidPre-DI static setup (synchronous)
setupMiddlewares(opts?)(opts?: { middlewares?: Record<string | symbol, any> }) => ValueOrPromise<void>Register Hono middlewares
initialize()() => Promise<void>Full initialization sequence

Public Methods

MethodReturn TypeDescription
getProjectConfigs()IApplicationConfigsReturns the merged application config
getProjectRoot()stringReturns process.cwd() and binds it to CoreBindings.APPLICATION_PROJECT_ROOT
getRootRouter()OpenAPIHonoReturns the root router instance
getServerHost()stringReturns the configured host
getServerPort()numberReturns the configured port
getServerAddress()stringReturns host:port string
getServer()OpenAPIHonoReturns the main Hono server instance
getServerInstance()TBunServerInstance | TNodeServerInstance | undefinedReturns the underlying runtime server instance
registerPostStartHook(opts)voidRegister a hook to run after server start
init()voidCalls registerCoreBindings()
start()Promise<void>Runs initialize(), setupMiddlewares(), mounts root router, starts the server, then runs post-start hooks
stop()voidStops the server (calls .stop() for Bun, .close() for Node.js)

start() Method Flow

Server Types

typescript
// Bun server instance
type TBunServerInstance = ReturnType<typeof Bun.serve>;

// Node.js server instance (from @hono/node-server)
type TNodeServerInstance = any;

The server is stored as a discriminated union based on runtime:

typescript
protected server:
  | { hono: OpenAPIHono; runtime: 'bun'; instance?: TBunServerInstance }
  | { hono: OpenAPIHono; runtime: 'node'; instance?: TNodeServerInstance };

BaseApplication

Extends AbstractApplication with concrete lifecycle implementations, resource registration, and boot support. Implements IRestApplication and IBootableApplication.

typescript
abstract class BaseApplication
  extends AbstractApplication
  implements IRestApplication, IBootableApplication

Resource Registration Methods

BaseApplication provides a set of convenient methods for registering your application's building blocks. These methods bind the provided classes to the DI container with conventional keys.

MethodDI Binding Key ConventionScope
component(ctor, opts?)components.{Name}Singleton
controller(ctor, opts?)controllers.{Name}Transient
service(ctor, opts?)services.{Name}Transient
repository(ctor, opts?)repositories.{Name}Transient
dataSource(ctor, opts?)datasources.{Name}Singleton
booter(ctor, opts?)booters.{Name}Tagged with 'booter'

TIP

All registration methods accept an optional opts.binding parameter to override the default namespace-based key:

typescript
this.controller(UserController, {
  binding: { namespace: 'controllers', key: 'CustomUserController' },
});

Method Signatures

typescript
component<Base extends BaseComponent, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

controller<Base, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

service<Base extends IService, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

repository<Base extends IRepository<TTableSchemaWithId>, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

dataSource<Base extends IDataSource, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

booter<Base extends IBooter, Args extends AnyObject = any>(
  ctor: TClass<Base>,
  opts?: TMixinOpts<Args>,
): Binding<Base>

Where TMixinOpts is:

typescript
type TMixinOpts<Args extends AnyObject = any> = {
  binding: { namespace: string; key: string };
  args?: Args;
};

Static File Serving

typescript
static(opts: { restPath?: string; folderPath: string }): this

Serves static files using the appropriate runtime handler (hono/bun for Bun, @hono/node-server/serve-static for Node.js). The restPath defaults to '*'.

typescript
this.static({ restPath: '/public/*', folderPath: './public' });

Boot Support

typescript
boot(): Promise<IBootReport>

Registers default booters (DatasourceBooter, RepositoryBooter, ServiceBooter, ControllerBooter) and runs the bootstrapper. The boot options come from this.configs.bootOptions.

typescript
async registerBooters(): Promise<void>

Registers the Bootstrapper singleton and all four default booters with the 'booter' tag.

registerDynamicBindings

Protected method for handling late-registration and circular dependency patterns. Iterates bindings in a namespace, configuring each instance and re-fetching to pick up dynamically added bindings.

typescript
protected async registerDynamicBindings<T extends IConfigurable>(opts: {
  namespace: TBindingNamespace;
  onBeforeConfigure?: (opts: { binding: Binding<T> }) => Promise<void>;
  onAfterConfigure?: (opts: { binding: Binding<T>; instance: T }) => Promise<void>;
}): Promise<void>
ParameterTypeDescription
namespaceTBindingNamespaceBinding namespace to scan (e.g., 'components', 'datasources')
onBeforeConfigurecallbackCalled before each binding's configure()
onAfterConfigurecallbackCalled after configure()

The method tracks already-configured bindings to prevent duplicates and re-fetches after each configuration to handle bindings registered during the configure phase.

initialize() Method Flow

Startup sequence executed by the initialize() method:

HookWhen to UseNotes
staticConfigure()Pre-DI static setup (static files, etc.)Synchronous, called before preConfigure
preConfigure()Register all resources (datasources, services, controllers)Nothing instantiated yet - order doesn't matter
register...()Framework iterates bindings and instantiates classesDataSources initialized first (other layers depend on them)
postConfigure()Logic after all resources configuredDo not register new datasources/components/controllers here - they won't auto-configure

registerDefaultMiddlewares

Automatically registers these default middlewares during initialize():

  1. Error handler (appErrorHandler) - with optional rootKey from configs.error.rootKey
  2. Async context storage (contextStorage) - enabled by default via configs.asyncContext.enable
  3. Not-found handler (notFoundHandler)
  4. RequestTrackerComponent - assigns x-request-id to every request, includes request body parsing
  5. Emoji favicon - defaults to the flame emoji, configurable via configs.favicon

registerControllers and Transport Support

The registerControllers() method supports multiple transport protocols via the transports config:

typescript
// In your application config
{
  transports: ['rest'],        // Default: REST only
  transports: ['rest', 'grpc'], // Enable both REST and gRPC
  transports: ['grpc'],        // gRPC only
}

For each transport in the array, the corresponding component (RestComponent or GrpcComponent) is instantiated and configured. If gRPC controllers are discovered but the 'grpc' transport is not enabled, a warning is logged.

registerComponents

When registering components, after each component's configure() completes, the framework also re-registers any datasources that the component may have added. This handles the pattern where components bring their own datasources.

IApplicationConfigs

typescript
interface IApplicationConfigs {
  host?: string;                          // Server host (default: process.env.HOST || 'localhost')
  port?: number;                          // Server port (default: process.env.PORT || 3000)
  path: { base: string; isStrict: boolean }; // Base path config (required)
  requestId?: { isStrict: boolean };      // Request ID validation
  favicon?: string;                       // Favicon emoji (default: '🔥')
  error?: { rootKey: string };            // Error response root key
  asyncContext?: { enable: boolean };     // Hono async context storage (default: true)
  bootOptions?: IBootOptions;             // Boot system configuration
  debug?: { shouldShowRoutes?: boolean }; // Show registered routes on startup
  transports?: TControllerTransport[];    // Controller transports: 'rest' | 'grpc' (default: ['rest'])
  strictPath?: boolean;                   // Hono strict path matching (default: true)
  [key: string]: any;                     // Extensible
}

IBootOptions

typescript
interface IBootOptions {
  controllers?: IArtifactOptions;
  services?: IArtifactOptions;
  repositories?: IArtifactOptions;
  datasources?: IArtifactOptions;
  [artifactType: string]: IArtifactOptions | undefined;
}

interface IArtifactOptions {
  dirs?: string[];
  extensions?: string[];
  isNested?: boolean;
  glob?: string;
}

TControllerTransport

typescript
class ControllerTransports {
  static readonly REST = 'rest';
  static readonly GRPC = 'grpc';
}

type TControllerTransport = 'rest' | 'grpc';

IApplicationInfo

typescript
interface IApplicationInfo {
  name: string;
  version: string;
  description: string;
  author?: { name: string; email: string; url?: string };
  [extra: string | symbol]: any;
}

CoreBindings

Core binding keys used for fundamental application components:

KeyValueDescription
APPLICATION_INSTANCE'@app/instance'The application instance itself
APPLICATION_SERVER'@app/server'The server object (hono + runtime + instance)
APPLICATION_CONFIG'@app/config'Application configuration
APPLICATION_PROJECT_ROOT'@app/project_root'Project root directory (process.cwd())
APPLICATION_ROOT_ROUTER'@app/router/root'The root OpenAPIHono router
APPLICATION_ENVIRONMENTS'@app/environments'Application environment variables
APPLICATION_MIDDLEWARE_OPTIONS'@app/middleware_options'Middleware configuration options

BindingNamespaces

Standard namespaces for organizing DI bindings:

NamespaceValueUsed By
COMPONENT'components'component()
DATASOURCE'datasources'dataSource()
REPOSITORY'repositories'repository()
MODEL'models'Model bindings
SERVICE'services'service()
MIDDLEWARE'middlewares'Middleware bindings
PROVIDER'providers'Provider bindings
CONTROLLER'controllers'controller()
BOOTERS'booters'booter()

Mixin Interfaces

BaseApplication implements several mixin interfaces that define its capabilities:

InterfaceMethodsDescription
IComponentMixincomponent(), registerComponents()Component registration and lifecycle
IControllerMixincontroller(), registerControllers()Controller registration and route mounting
IRepositoryMixindataSource(), repository()DataSource and repository registration
IServiceMixinservice()Service registration
IStaticServeMixinstatic()Static file serving

NOTE

There is also an IServerConfigMixin interface defined in the mixins types that declares staticConfigure(), preConfigure(), postConfigure(), and getApplicationVersion(), though BaseApplication inherits the first three from AbstractApplication.

Middleware Configuration Types

These types are used when configuring middlewares via setupMiddlewares():

typescript
interface IMiddlewareConfigs {
  requestId?: IRequestIdOptions;
  compress?: ICompressOptions;
  cors?: ICORSOptions;
  csrf?: ICSRFOptions;
  bodyLimit?: IBodyLimitOptions;
  ipRestriction?: IBaseMiddlewareOptions & IIPRestrictionRules;
  [extra: string | symbol]: any;
}

interface IBaseMiddlewareOptions {
  enable: boolean;
  path?: string;
  [extra: string | symbol]: any;
}

See Also