Application Base Classes
Overview
@nx/core provides one abstract base class, BaseApplication, that holds the infrastructure every BANA backend service shares: CORS and body-limit middleware, the health check endpoint, Swagger/OpenAPI documentation, static file serving, IdentityNetworkService registration, and the preConfigure() initialization flow.
Services do not extend BaseApplication directly. They extend one of two concrete subclasses based on their role in the JWT trust chain:
IssuerApplication- extended only by@nx/identity. Signs JWTs (JWKS issuer) and serves the public key set at/jw-certs.VerifierApplication- extended by the other 13 IGNIS services. Verifies JWTs against identity's JWKS endpoint.
A third subclass, DefaultApplication, is legacy and unused - nothing extends it. It predates the Issuer/Verifier split and uses a symmetric JWS token with no authorization.
Source:
| File | Class |
|---|---|
packages/core/src/application/base.ts | BaseApplication (abstract, shared) |
packages/core/src/application/issuer.ts | IssuerApplication |
packages/core/src/application/verifier.ts | VerifierApplication |
packages/core/src/application/default.ts | DefaultApplication (legacy) |
See the IGNIS Application reference for details on the IGNIS
BaseApplicationthat@nx/core's base extends.
Class Hierarchy
| Base class | Extended by | Authentication | Authorization |
|---|---|---|---|
BaseApplication (abstract) | - (shared by all) | declares configureAuthentication() | declares configureAuthorization() |
IssuerApplication | @nx/identity only | JWKS issuer (signs, serves /jw-certs) | Casbin (scoped) |
VerifierApplication | commerce, finance, inventory, sale, signal, payment, pricing, ledger, invoice, licensing, outreach, taxation, helpdesk | JWKS verifier (validates against identity JWKS) | Casbin (scoped) |
DefaultApplication | nothing (legacy) | symmetric JWS | none |
Class Definition
BaseApplication is abstract: it implements the shared lifecycle and leaves authentication/authorization to its subclasses.
import { BaseApplication as IgnisBaseApplication, IApplicationInfo } from '@venizia/ignis';
import { ValueOrPromise } from '@venizia/ignis-helpers';
export abstract class BaseApplication extends IgnisBaseApplication {
protected applicationRoles: string[] = [];
// Implemented per subclass (Issuer / Verifier / Default)
protected abstract configureAuthentication(): ValueOrPromise<void>;
protected abstract configureAuthorization(): ValueOrPromise<void>;
// Shared lifecycle
override getAppInfo(): IApplicationInfo;
preConfigure(): void;
async postConfigure(): Promise<void>;
override async setupMiddlewares(): Promise<void>;
staticConfigure(): void;
// Registration methods (override in child packages)
configureDatasources(): void;
configureRepositories(): void;
configureServices(): void; // registers IdentityNetworkService
configureComponents(): void; // HealthCheck + Swagger
configureSecurity(): void; // -> configureAuthentication() + configureAuthorization()
configureControllers(): void;
}Lifecycle and Boot Sequence
preConfigure() Call Order
preConfigure() runs the registration methods in a fixed order. In MIGRATE run mode it short-circuits after repositories (a migration only needs database access); otherwise it continues through services, components, security, and controllers.
preConfigure() {
this.applicationRoles = this.getApplicationRoles();
this.configureDatasources(); // 1. Data sources
this.configureRepositories(); // 2. Repositories
if (this.getApplicationRunMode() !== ApplicationRunModes.MIGRATE) {
this.configureServices(); // 3. Services (IdentityNetworkService)
this.configureComponents(); // 4. Components (HealthCheck, Swagger)
this.configureSecurity(); // 5. Authentication + Authorization
this.configureControllers(); // 6. Controllers
}
}Full Boot Sequence
Middleware Configuration
setupMiddlewares() lives in the shared base. It optionally wires an HTTP tracing middleware (only when telemetry was initialized by bootstrapApplication), then registers CORS and a body limit.
CORS
cors: {
enable: true,
path: '*',
origin: '*',
allowMethods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'],
allowHeaders: [/* see table below */],
exposeHeaders: [/* see table below */],
maxAge: 86_400, // 24 hours
credentials: true,
}Allowed Request Headers:
| Header | IGNIS Constant |
|---|---|
Authorization | HTTP.Headers.AUTHORIZATION |
Content-Type | HTTP.Headers.CONTENT_TYPE |
X-Request-Channel | HTTP.Headers.REQUEST_CHANNEL |
X-Count-Data | HTTP.Headers.REQUEST_COUNT_DATA |
X-Device-Info | HTTP.Headers.REQUEST_DEVICE_INFO |
X-Tracing-Id | HTTP.Headers.REQUEST_TRACING_ID |
timezone | Custom |
timezone-offset | Custom |
x-auth-provider | Custom |
x-forward-for | Custom |
x-locale | Custom |
x-merchant-id | Custom (active-merchant scope) |
x-real-ip | Custom |
Exposed Response Headers:
| Header | IGNIS Constant |
|---|---|
Content-Disposition | HTTP.Headers.CONTENT_DISPOSITION |
Content-Length | HTTP.Headers.CONTENT_LENGTH |
Content-Range | HTTP.Headers.CONTENT_RANGE |
Content-Type | HTTP.Headers.CONTENT_TYPE |
X-Count-Data | HTTP.Headers.RESPONSE_COUNT_DATA |
X-Response-Format | HTTP.Headers.RESPONSE_FORMAT |
x-custom-header | Custom |
Body Limit
bodyLimit: {
enable: true,
path: '*',
maxSize: 100 * 1024 * 1024, // 100 MB
onError: c => c.json({}, HTTP.ResultCodes.RS_4.ContentTooLarge), // HTTP 413
}Component Setup
Health Check
Registered at the /health endpoint. Returns application status information.
this.bind<IHealthCheckOptions>({
key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
}).toValue({
restOptions: { path: '/health' },
});
this.component(HealthCheckComponent);See the IGNIS Health Check reference for response format and customization.
Swagger / OpenAPI
Provides three endpoints for API documentation, using the Swagger UI renderer (ui.type: 'swagger'):
| Path | Description |
|---|---|
/doc | Documentation base path |
/openapi.json | OpenAPI 3.0 specification (JSON) |
/explorer | Swagger UI |
this.bind<ISwaggerOptions>({ key: SwaggerBindingKeys.SWAGGER_OPTIONS }).toValue({
restOptions: {
base: { path: '/doc' },
doc: { path: '/openapi.json' },
ui: { path: '/explorer', type: 'swagger' },
},
explorer: {
openapi: '3.0.0',
info: {
title: 'API Documentation',
version: this.getAppInfo().version,
description: 'NEXPANDO - Seller API documentation',
},
servers: [
{
url: applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_APPLICATION_EXPLORER_URL),
description: 'Application Server URL',
},
],
},
});
this.component(SwaggerComponent);See the IGNIS Swagger reference for full options.
Security Setup
configureSecurity() is shared by the base and simply delegates to the two abstract methods, which each subclass implements:
configureSecurity(): void {
this.configureAuthentication();
this.configureAuthorization();
}Authentication per Subclass
| Subclass | JWT strategy | JOSEStandards | Notes |
|---|---|---|---|
IssuerApplication | JWKSIssuerAuthenticationStrategy | JWKS (mode ISSUER) | Signs with a PEM private key; publishes keys at /jw-certs |
VerifierApplication | JWKSVerifierAuthenticationStrategy | JWKS (mode VERIFIER) | Validates against APP_ENV_IDENTITY_SERVICE_BASE_URL + /jw-certs |
DefaultApplication | JWSAuthenticationStrategy | JWS | Legacy symmetric secret; not used by any service |
All three call the shared bindBasicAuthentication() helper, register AuthenticateComponent, and add a Basic strategy that delegates to the Identity service.
Basic Authentication (shared)
bindBasicAuthentication() (defined on the base) delegates credential verification to the Identity service via IdentityNetworkService.signIn():
protected bindBasicAuthentication(): void {
this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
verifyCredentials: ({ credentials }) => {
const identityNetworkService = this.get<IdentityNetworkService>({
key: BindingKeys.build({
namespace: BindingNamespaces.SERVICE,
key: IdentityNetworkService.name,
}),
});
return identityNetworkService.signIn({
identifier: { scheme: 'username', value: credentials.username },
credential: { scheme: 'basic', value: credentials.password },
});
},
});
}Authorization (Casbin)
IssuerApplication and VerifierApplication both register the IGNIS AuthorizeComponent with a scoped Casbin enforcer (ScopedCasbinAdapter, domain-scoped model, defaultDecision: DENY, alwaysAllowRoles = SUPER_ADMIN / ADMIN / OPERATOR) and a merchant-scoped domain resolver driven by the x-merchant-id header. DefaultApplication.configureAuthorization() is empty.
Authentication Flow
See the IGNIS Authentication reference for strategy details.
Static File Serving
staticConfigure() serves files from the public/ directory of the @nx/core package:
staticConfigure(): void {
this.static({ folderPath: path.join(__dirname, '../../public') });
}How to Extend in Child Packages
Every service declares an Application class that extends VerifierApplication (or IssuerApplication for @nx/identity) and overrides the registration methods, calling super first:
// packages/sale/src/application.ts
import { VerifierApplication, PostgresCoreDataSource } from '@nx/core';
export class Application extends VerifierApplication {
override configureDatasources(): void {
super.configureDatasources();
this.dataSource(PostgresCoreDataSource);
}
override configureRepositories(): void {
super.configureRepositories();
this.repository(SaleOrderRepository);
this.repository(SaleOrderItemRepository);
}
override configureServices(): void {
super.configureServices(); // IdentityNetworkService
this.service(SaleOrderService);
this.service(CheckoutService);
}
override async postConfigure(): Promise<void> {
await super.postConfigure();
// Run after all components are bound
}
}Always call super
When overriding any registration method, call super.<method>() first to preserve the default registrations (IdentityNetworkService, HealthCheck, Swagger, JWKS/Basic auth).
Environment Variables
| Variable | Used by | Required | Description |
|---|---|---|---|
APP_ENV_APPLICATION_SECRET | All | Yes | Application-level secret |
APP_ENV_JWKS_ALGORITHM | Issuer | No | Signing algorithm (default ES256) |
APP_ENV_JWKS_PRIVATE_KEY | Issuer | Yes | PEM private signing key |
APP_ENV_JWKS_PUBLIC_KEY | Issuer | Yes | PEM public key |
APP_ENV_JWKS_REST_PATH | Issuer + Verifier | No | JWKS endpoint path (default /jw-certs) |
APP_ENV_IDENTITY_SERVICE_BASE_URL | Verifier (+ Basic) | Yes | Identity service base URL (JWKS source + Basic delegation) |
APP_ENV_JWT_EXPIRES_IN | Issuer | Yes | Token expiration in seconds |
APP_ENV_APPLICATION_ROLES | All | No | Comma-separated role identifiers |
APP_ENV_APPLICATION_EXPLORER_URL | All | No | Server URL shown in Swagger explorer |
Legacy: DefaultApplication
DefaultApplication (application/default.ts) is retained only for reference. It configures a symmetric JWS token (APP_ENV_JWT_SECRET) via JWSAuthenticationStrategy and leaves configureAuthorization() empty. No service extends it - every service uses IssuerApplication or VerifierApplication instead. Do not use it for new packages.