This sample is the Koa-flavored reference application for the Node-Boot monorepo. It demonstrates the same broad feature set as the other server samples, but boots on @nodeboot/koa-server by calling NodeBoot.run(KoaServer) from src/app.ts.
The app exposes versioned REST controllers under /api, enables OpenAPI + Swagger UI, wires authorization and current-user resolution, uses TypeORM repositories and migrations with SQLite, registers schedulers and an HTTP client, and shows custom Koa middleware/error handling.
- Bootstrapping a Node-Boot app on
@nodeboot/koa-server - Authorization + current-user hooks with
@nodeboot/authorization - Repositories, migrations, subscribers, transactions, and naming strategies with
@nodeboot/starter-persistence - Cron-style scheduled jobs with
@nodeboot/starter-scheduler - Declarative outbound HTTP clients with
@nodeboot/starter-http - OpenAPI generation + Swagger UI with
@nodeboot/starter-openapi - DTO validation with
@nodeboot/starter-validation - Operational endpoints with
@nodeboot/starter-actuator
It also shows these application-level decorators in src/app.ts:
@EnableDI(Container)— enables TypeDI-backed dependency injection.@EnableOpenApi()— generates an OpenAPI spec for the controllers.@EnableSwaggerUI()— serves Swagger UI.@EnableAuthorization(LoggedInUserResolver, DefaultAuthorizationChecker)— plugs in current-user resolution and route authorization.@EnableActuator()— exposes actuator endpoints.@EnableRepositories()— enables persistence repositories, migrations, and subscribers.@EnableScheduling()— enables cron-based scheduled methods.@EnableHttpClients()— enables the declarative HTTP client stub.@EnableValidations()— enables request-body validation.@EnableComponentScan()— enables AOT component scanning.@NodeBootApplication()— marks the application bootstrap class.
- Node.js
pnpm- No external database is required; the sample uses
better-sqlite3with a localkoa-sample.dbfile
From the monorepo root:
pnpm install
pnpm --filter @nodeboot/koa-sample devOther useful commands:
pnpm --filter @nodeboot/koa-sample start— clean build, AOT postbuild, then rundist/server.jspnpm --filter @nodeboot/koa-sample start:prod— production startpnpm --filter @nodeboot/koa-sample rebuild:sqlite— rebuildbetter-sqlite3if your local environment needs it
Runtime/configuration highlights from app-config.yaml:
app:
name: facts-service
platform: tech-insights
environment: development
defaultErrorHandler: false
port: 3000
api:
routePrefix: /api
validations:
enableDebugMessages: true
server:
cors: ...
multipart: ...
openapi:
info: ...
servers:
- url: http://localhost:3000
securitySchemes:
basicAuth:
scheme: basic
type: http
persistence:
type: better-sqlite3
synchronize: false
migrationsRun: true
better-sqlite3:
database: koa-sample.dbWith OpenAPI enabled, the starter docs indicate these docs endpoints are available:
GET /api-docs/swagger.jsonGET /api-docs/GET /docs→ redirect to Swagger UI
With the actuator starter enabled, operational routes are exposed under /actuator/*.
src/app.ts— application bootstrap and feature-enabling decoratorssrc/server.ts— createsFactsServiceAppand starts the Koa serversrc/controllers/hello.controller.ts— simple hello routepaging.controller.ts— paginated and cursor-paginated user queriesusers.controller.ts— CRUD-style user endpoints plus external/custom-query demos
src/exceptions/httpException.ts— simpleHttpErrorsubclass for custom HTTP exceptionssrc/interfaces/users.interface.ts— minimal user shape used by the auth resolversrc/auth/DefaultAuthorizationChecker.ts— authorization checker implementationLoggedInUserResolver.ts— current-user resolver implementation
src/middlewares/LoggingMiddleware.ts— before-request logging middlewareCustomErrorHandler.ts— JSON error handler
src/config/AppConfigProperties.ts— bindsapp.*config intoapp-configServerConfiguration.ts— maps Koa server options from configSecurityConfiguration.ts— registerskoa-helmetand@koa/corsClassTransformConfiguration.ts— configures class-transform behaviorMultipleConfigurations.ts— groups configuration classes
src/persistence/entities/User.ts— TypeORM user entityrepositories/— standard and paging repositoriesmigrations/— createsnb-userand later addsnamelisteners/— entity lifecycle and transaction subscribersCustomNamingStrategy.ts— prefixes table names withnb-DatasourceOverridesConfiguration.ts— annotation-based datasource overrideusers.init.ts— seed data used when the database is empty
src/models/— DTOs and OpenAPI response modelsrc/clients/MicroserviceHttpClient.ts— external HTTP client stubsrc/services/— user service, greeting service, and schedulers
All controller routes are versioned with v1 and prefixed by /api.
| Method | Path | Notes |
|---|---|---|
GET |
/api/v1/hello/ |
Returns "Hello, World!" |
| Method | Path | Notes |
|---|---|---|
GET |
/api/v1/paging/paginated |
Returns Page<UserModel> using PagingRequest query params |
GET |
/api/v1/paging/cursor |
Returns CursorPage<UserModel> using CursorRequest query params |
GET |
/api/v1/paging/paginated/filter |
Same as paginated, but filtered to email = "example3@email.com" |
GET |
/api/v1/paging/cursor/filter |
Same as cursor pagination, but filtered to email = "example3@email.com" |
| Method | Path | Notes |
|---|---|---|
GET |
/api/v1/users/ |
Returns all users from SQLite |
GET |
/api/v1/users/external/ |
Calls the external JSONPlaceholder /users API through MicroserviceHttpClient |
GET |
/api/v1/users/query/ |
Runs the repository custom query (id IN (1, 2)) |
GET |
/api/v1/users/:id |
Returns one user by id |
POST |
/api/v1/users/ |
Creates a user, returns 201, and is protected with @Authorized("ADMIN") |
PUT |
/api/v1/users/:id |
Updates a user using UpdateUserDto |
DELETE |
/api/v1/users/:id |
Demonstrates transactional rollback: the service deletes, then throws an error to force rollback |
Validation rules used by the DTOs:
CreateUserDto:emailmust be an email;namemust be a string;passwordmust be a non-empty string with length9..32UpdateUserDto:passwordmust be a non-empty string with length9..32UserModel: OpenAPI response model withid,email, and optionalname
Authorization is enabled in src/app.ts with:
LoggedInUserResolverDefaultAuthorizationChecker
In this Koa sample, both classes are typed against Koa request/response types from koa:
LoggedInUserResolver implements CurrentUserChecker<Request, Response>DefaultAuthorizationChecker implements AuthorizationChecker<Request, Response>
The real filename in this sample is src/auth/DefaultAuthorizationChecker.ts.
The demo authorization flow is intentionally simple:
LoggedInUserResolverlogs and returns a hard-coded current user objectDefaultAuthorizationCheckerlogs and evaluates requested roles against a hard-coded["USER", "ADMIN"]role set@Authorized("ADMIN")is applied toPOST /api/v1/users/
For the framework-specific integration details, see the Authorization and current user integration section in ../../servers/koa-server/README.md and the package docs in ../../packages/authorization/README.md.
LoggingMiddlewareis decorated with@Middleware({type: "before"})and logs every incoming request before controller execution.CustomErrorHandleris decorated with@ErrorHandler()and returns JSON{message}responses using theHttpErrorstatus code. This matters becauseapp-config.yamlsetsapp.defaultErrorHandler: false.SecurityConfigurationregisters:koa-helmetwithcontentSecurityPolicy: false(the code comments that this is needed when Swagger UI is enabled)@koa/cors
The sample uses the persistence starter with SQLite (better-sqlite3) and migrations enabled.
DatasourceOverridesConfiguration.tshard-codes the datasource as:type: "better-sqlite3"database: "koa-sample.db"synchronize: falsemigrationsRun: true
CustomNamingStrategy.tsprefixes table names withnb-, so theUserentity maps tonb-user- Migrations:
1701774002463-migration.tscreatesnb-userwithid,email, andpassword1701786331338-migration.tsadds the nullablenamecolumn
users.init.tscontains four seed users;UserServiceinserts them when the repository is emptyUserRepositorydemonstrates a custom query builder method:findByQueryIn()PagingUserRepositoryextendsPagingAndSortingRepository<User>for page/cursor APIs- Entity subscribers:
UserEntityEventListenerlogs before/after user insertions and callsGreetingService.sayHello(...)GlobalEntityEventListenerlogs entity lifecycle events and transaction start/commit/rollback hooks
UserServicealso demonstrates@Transactional(),runOnTransactionCommit(...), andrunOnTransactionRollback(...)
src/services/schedulers.component.ts registers three scheduled methods:
@Scheduler("*/1 * * * *")— every minute@Scheduler("*/5 * * * *")— every 5 minutes@Scheduler("0 9 * * *")— every day at 9:00 AM
src/clients/MicroserviceHttpClient.ts demonstrates the HTTP starter with:
baseURL: "https://jsonplaceholder.typicode.com"timeout: 5000httpLogging: true
UserService.findExternalUsers() uses that client to call GET /users.
| Script | Purpose |
|---|---|
pnpm start |
Clean build, run TypeScript build + AOT postbuild, then start the compiled server |
pnpm start:prod |
Build and start with NODE_ENV=production |
pnpm dev |
Run with nodemon in development mode |
pnpm nodeboot:update |
Update @nodeboot/* dependencies |
pnpm build |
Compile with tsc -p tsconfig.build.json |
pnpm postbuild |
Run npx @nodeboot/aot node-boot-aot |
pnpm clean:build |
Remove dist/ |
pnpm lint |
Run ESLint |
pnpm lint:fix |
Run ESLint with --fix |
pnpm format |
Check formatting with Prettier |
pnpm format:fix |
Rewrite formatting with Prettier |
pnpm pretest |
Clean + build before tests |
pnpm test |
Run Node test runner tests through ts-node/register |
pnpm test:coverage |
Run tests with experimental coverage |
pnpm tsc |
Run plain TypeScript compilation |
pnpm rebuild:sqlite |
Rebuild better-sqlite3 |
pnpm create:migration |
Create a TypeORM migration file under src/persistence/migrations/ |
MIT. See LICENSE.