API Reference
Automatic interactive API documentation generated from your OpenAPI spec. A pluggable UI provider renders it - Scalar by default, or classic Swagger UI.
Renamed from SwaggerComponent
Swagger UI is one of the pluggable UI providers, not the only one, so the component carries a vendor-neutral name. The deprecated Swagger* aliases are removed: use ApiReferenceComponent, IApiReferenceOptions and ApiReferenceBindingKeys.API_REFERENCE_OPTIONS.
Quick Reference
| Item | Value |
|---|---|
| Package | @venizia/ignis |
| Class | ApiReferenceComponent |
| UI Factory | UIProviderFactory |
| Runtimes | Both |
| Provider | Value | When to use |
|---|---|---|
| Scalar | 'scalar' | Modern, clean UI (default) |
| Swagger UI | 'swagger' | Classic Swagger interface |
Import Paths
import { ApiReferenceComponent, ApiReferenceBindingKeys, UIProviderFactory } from '@venizia/ignis';
import type { IApiReferenceOptions, IUIProvider, IUIConfig, IGetProviderParams } from '@venizia/ignis';In one example
Register the component - no configuration required. The docs UI comes up at <app base path>/doc/explorer, the raw spec at <app base path>/doc/openapi.json. With the /api base path used throughout the getting-started guide, that's /api/doc/explorer.
// src/application.ts
import { ApiReferenceComponent, BaseApplication, ValueOrPromise } from '@venizia/ignis';
export class Application extends BaseApplication {
preConfigure(): ValueOrPromise<void> {
this.component(ApiReferenceComponent);
}
}Define routes with Zod schemas so they show up in the generated spec:
// src/controllers/hello.controller.ts
import { z } from '@hono/zod-openapi';
import { BaseRestController, controller, jsonContent, ValueOrPromise } from '@venizia/ignis';
import { HTTP } from '@venizia/ignis-helpers';
@controller({ path: '/hello' })
export class HelloController extends BaseRestController {
constructor() {
super({ scope: HelloController.name, path: '/hello' });
}
override binding(): ValueOrPromise<void> {
this.defineRoute({
configs: {
path: '/',
method: 'get',
responses: {
[HTTP.ResultCodes.RS_2.Ok]: jsonContent({
description: 'A simple hello message',
schema: z.object({ message: z.string() }),
}),
},
},
handler: (c) => {
return c.json({ message: 'Hello, `IGNIS`!' }, HTTP.ResultCodes.RS_2.Ok);
},
});
}
}TIP
Only routes registered through defineRoute, bindRoute, or @api() with @hono/zod-openapi schemas appear in the generated spec.
How it works
- Options merge group by group.
binding()reads the boundIApiReferenceOptions, then shallow-mergesbase,doc, anduieach against their own defaults. Overridingui.typealone still keepsui.pathand everybase/docfield. explorer.infois always overwritten. The component reads yourpackage.jsonviaapplication.getAppInfo(). It replacesexplorer.infowith{ title, version, description, contact }- anyexplorer.infoyou bind is discarded. Editpackage.jsoninstead.explorer.serversfills in only when empty. A supplied server entry is kept as-is. Otherwise, the component builds one fromapplication.getServerAddress()plus the base path.- UI type resolution uses
??, not||. The source isrestOptions.ui.type ?? DocumentUITypes.SWAGGER. Onlynull/undefinedfalls back, and it falls back to'swagger'- not the configured default'scalar'.- An explicit empty string is NOT repaired by this fallback: it fails
DocumentUITypes.isValid()and the component throwsInvalid document UI Typeimmediately.
- An explicit empty string is NOT repaired by this fallback: it fails
- UI libraries load lazily.
SwaggerUIProvider/ScalarUIProvidereachawait import()their rendering library insiderender(). That happens on the first request to the docs UI, not at application startup. Only the configured provider's library is ever loaded. ScalarUIProviderrenamestitletopageTitle. Scalar's own render API takespageTitle, nottitle- worth knowing if you inspect the rendered output or write a custom UI provider.- Security schemes are always registered. JWT (
bearer) and Basic security schemes are added to the OpenAPI registry unconditionally. Routes usingauthenticate: { strategies: ['jwt'] }or['basic']render the correct auth UI as a result.
Common tasks
Switch to Swagger UI
this.bind<IApiReferenceOptions>({
key: ApiReferenceBindingKeys.API_REFERENCE_OPTIONS,
}).toValue({ restOptions: { ui: { type: 'swagger' } } });Move the docs under a different base path
this.bind<IApiReferenceOptions>({
key: ApiReferenceBindingKeys.API_REFERENCE_OPTIONS,
}).toValue({ restOptions: { base: { path: '/api-docs' } } });Result: UI at <app base path>/api-docs/explorer, spec at <app base path>/api-docs/openapi.json. The group merge keeps doc.path/ui.path defaults.
Set the info block shown in the UI
explorer.info always comes from package.json. Update name, version, description, and author there - binding explorer.info directly has no effect.
Register a custom UI provider
UIProviderFactory.register() only understands 'swagger'/'scalar'. Register a custom provider directly on the factory before ApiReferenceComponent.binding() runs:
UIProviderFactory.getInstance().set('my-ui', new MyCustomUIProvider());Reference
Options
export interface IApiReferenceOptions {
restOptions?: {
base?: { path?: string };
doc?: { path?: string };
ui?: { path?: string; type?: TDocumentUIType };
};
explorer?: {
openapi?: string;
info?: {
title: string;
version: string;
description: string;
contact?: { name: string; email: string };
};
servers?: Array<{ url: string; description?: string }>;
};
uiConfig?: Record<string, any>;
}| Option | Type | Default | Description |
|---|---|---|---|
restOptions.base.path | string | '/doc' | Base path for all documentation routes |
restOptions.doc.path | string | '/openapi.json' | Path to the raw OpenAPI spec (relative to base) |
restOptions.ui.path | string | '/explorer' | Path to the documentation UI (relative to base) |
restOptions.ui.type | 'swagger' | 'scalar' | 'scalar' | UI provider type |
explorer.openapi | string | '3.0.0' | OpenAPI specification version |
explorer.info.title / .version / .description / .contact | - | Always sourced from package.json | Overwritten at runtime - binding values are discarded |
explorer.servers | Array<{ url, description? }> | Auto-detected when empty | Server URLs |
uiConfig | Record<string, any> | undefined | Custom config passed through to the UI provider |
Binding keys
| Key | Constant | Type | Required | Default |
|---|---|---|---|---|
@app/api-reference/options | ApiReferenceBindingKeys.API_REFERENCE_OPTIONS | IApiReferenceOptions | No | See Options table |
SwaggerBindingKeys.SWAGGER_OPTIONS is removed. It was only ever an alias for the key above - there was never a separate binding under the literal '@app/swagger/options', so nothing needs rebinding.
Default value:
const DEFAULT_API_REFERENCE_OPTIONS: IApiReferenceOptions = {
restOptions: {
base: { path: '/doc' },
doc: { path: '/openapi.json' },
ui: { path: '/explorer', type: 'scalar' },
},
explorer: {
openapi: '3.0.0',
info: {
title: 'API Documentation',
version: '1.0.0',
description: 'API documentation for your service',
},
},
};NOTE
The explorer.info values above are never used at runtime - binding() unconditionally overwrites explorer.info from package.json. They exist only as structural defaults.
API endpoints
| Method | Path (default, relative to app base) | Description |
|---|---|---|
GET | /doc/explorer | Documentation UI (Scalar by default) |
GET | /doc/openapi.json | Raw OpenAPI specification |
These paths are mounted under your application's own base path - path.base in IApplicationConfigs. With the /api base path from the getting-started guide, that's GET /api/doc/explorer. They also shift with restOptions.base.path, restOptions.ui.path, and restOptions.doc.path.
UIProviderFactory
| Method | Signature | Description |
|---|---|---|
getInstance() | static () => UIProviderFactory | Returns the singleton instance |
register() | (opts: { type: string }) => void | Instantiates and registers a built-in provider; idempotent - warns and returns if the type is already bound |
getProvider() | (opts: IGetProviderParams) => IUIProvider | Returns the registered provider or throws Unknown UI Provider |
getRegisteredProviders() | () => string[] | Lists all registered provider type keys |
Extends MemoryStorageHelper<{ [key: string | symbol]: IUIProvider }>, using isBound() / get() / set() / keys() for lightweight, type-safe storage without the full DI container.
Type definitions
type TDocumentUIType = TConstValue<typeof DocumentUITypes>;
class DocumentUITypes {
static readonly SWAGGER = 'swagger';
static readonly SCALAR = 'scalar';
static readonly SCHEME_SET: Set<string>;
static isValid(input: string): boolean;
}TDocumentUIType is derived via TConstValue, which extracts the union of every static readonly string on DocumentUITypes. The type stays in sync with the constants automatically.
Component lifecycle (binding())
- Resolve options - reads
ApiReferenceBindingKeys.API_REFERENCE_OPTIONSwithisOptional: true, then mergesbase/doc/uieach againstDEFAULT_API_REFERENCE_OPTIONS - Overwrite info - reads
package.jsonviaapplication.getAppInfo()and replacesexplorer.info - Auto-detect servers - builds one entry from
application.getServerAddress()whenexplorer.serversis empty - Normalize paths - ensures every path segment (
base.path,doc.path,ui.path) has a leading/ - Register the OpenAPI doc route -
rootRouter.doc(docPath, explorer) - Resolve
uiType-restOptions.ui.type ?? DocumentUITypes.SWAGGER - Validate and register the UI provider - via
UIProviderFactory, unless a provider with that type is already bound - Register the UI route -
GEThandler atuiPathcallinguiProvider.render() - Register JWT and Basic security schemes on the OpenAPI registry
Tech stack
| Library | Purpose |
|---|---|
@hono/zod-openapi | OpenAPI generation from Zod schemas |
@hono/swagger-ui | Swagger UI rendering |
@scalar/hono-api-reference | Scalar UI rendering |
zod | Schema validation and type generation |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Invalid document UI Type | restOptions.ui.type is not 'swagger' or 'scalar' - an explicit empty string is NOT repaired by the ?? fallback | Use 'scalar' or 'swagger' explicitly |
| Documentation UI shows no routes | Controllers aren't defining routes with Zod schemas via defineRoute, bindRoute, or @api() | Add Zod response schemas to your route configs |
Unknown UI Provider | UIProviderFactory.getProvider() was called with a type that was never registered - usually a failed binding() | Ensure ApiReferenceComponent is registered in preConfigure(); check logs for warnings during binding |
| OpenAPI spec missing authentication schemes | AuthenticationComponent isn't registered, so auth strategies aren't available when schemes are added | Register AuthenticationComponent before ApiReferenceComponent in preConfigure() |
explorer.info doesn't match my binding | explorer.info is always overwritten from package.json during binding() | Update package.json fields (name, version, description, author) instead |
See also
Guides:
- Components Overview - Component system basics
- Controllers - Defining OpenAPI routes
Components:
- All Components - Built-in components list
- Authentication - JWT/Basic auth for secured endpoints
Utilities:
- Schema Utilities - Response schema helpers
- JSX Utilities - HTML response schemas
External Resources:
- OpenAPI Specification - OpenAPI standard
- Scalar Documentation - Scalar API documentation UI
- @hono/zod-openapi - Hono OpenAPI integration
Files:
packages/core-server/src/components/api-reference/component.ts-ApiReferenceComponentpackages/core-server/src/components/api-reference/ui/-UIProviderFactory,SwaggerUIProvider,ScalarUIProviderpackages/core-server/src/components/api-reference/common/types.ts-IApiReferenceOptions,IUIProvider,IUIConfigpackages/core-server/src/components/api-reference/common/keys.ts-ApiReferenceBindingKeyspackages/core-server/src/components/api-reference/common/constants.ts-DocumentUITypes