AdonisJS v7 relies heavily on code generation. We use it to create barrel files , emit types consumed by the router, and allow Tuyau to generate a type-safe client that brings end-to-end type safety to AdonisJS applications.
Until now, the development server has owned the complete codegen process. It watches your application, keeps generated types in sync, and updates barrel indexes as you add, remove, or rename files. This is exactly what we need during active development.
However, many workflows need up-to-date generated files without running the development server. AI coding agents are a good example. An agent may implement a feature and run the typechecker and tests before it ever attempts to start the development server. It should be able to refresh generated files first.
You can now do that using the codegen command.
node ace codegen
The command runs codegen once and exits. It is also useful after switching branches, resolving merge conflicts, or before typechecking an application in CI.
Codegen needs more than static analysis
Implementing the command was not as straightforward as scanning a few directories. Some generated files can be created from the files on disk, but everything derived from routes needs the assembled application.
Routes may be registered inside start/routes.ts, preload files, service providers, installed packages, or loops. AdonisJS must load all this code and collect the final list of routes before it can generate accurate route types.
Starting the application, however, can trigger side effects. The ORM may connect to the database, a queue provider may start pulling jobs, or a scheduler may execute its next task. You wanted to refresh generated types but accidentally started running the application.
Introducing the warmup phase
The new warmup phase assembles an application without making it operational. During warmup, AdonisJS runs provider start hooks, executes application starting hooks, and imports preload files.
At this point, routes, middleware, and event listeners have been registered, but the application has not started accepting requests or running background work. The codegen command uses this state to inspect the application and generate route-derived types safely.
The application lifecycle can now be understood as three steps.
- Boot prepares container bindings and framework services.
- Warmup assembles the application by running provider
starthooks and preload files. - Ready begins work that belongs to a running application.
Warmup is not exclusive to codegen. Every application now goes through this phase when starting. A regular application continues into the ready phase and begins processing requests or background work, whereas the codegen command stops after warmup and exits.
Handling side effects in service providers
Most service providers need no changes. Bindings, routes, middleware, macros, and event listeners must be registered during warmup so that codegen sees the same application you run in production.
Operational work should ideally live inside the provider's ready method. This method does not run during warmup, making it the right place to start queue workers, register recurring schedules, or connect to services needed by a running process.
If a side effect must remain inside start, guard it using the application mode. For example, the following provider avoids creating its recurring schedule during codegen.
import CleanupExpiredSessions from '#jobs/cleanup_expired_sessions'
import type { ApplicationService } from '@adonisjs/core/types'
export default class SchedulerProvider {
constructor(protected app: ApplicationService) {}
async start() {
if (this.app.getMode() !== 'run') {
return
}
await CleanupExpiredSessions.schedule({ retentionDays: 30 })
.id('cleanup-expired-sessions')
.cron('0 0 * * *')
.timezone('UTC')
.run()
}
}
Only guard operational work. Do not use the mode to conditionally register routes, bindings, or listeners. Doing so would make codegen inspect a different application from the one you run.
Provider shutdown hooks are also skipped during warmup. These hooks exist to close long-lived connections that would otherwise prevent a running process from exiting. An application should not create such connections during warmup. If it does, that is a defect in the provider logic rather than something for a shutdown hook to clean up.
How the command generates files
The command performs its work in two steps.
First, it generates files that can be derived from the filesystem, including barrel indexes. These files must be written first because preload files are allowed to import them.
Next, it warms up the application and runs generators that depend on the final list of registered routes. This includes the route types used by the router and packages such as Tuyau.
The development server continues to run the same codegen steps as files change. The command simply makes them available as an explicit, one-time operation.
Commit generated files
The .adonisjs directory contains generated source, not disposable cache files. Some files are imported by your application, while others provide type safety during development and CI. Therefore, you should continue committing .adonisjs to Git.
The codegen command refreshes committed output. It does not replace committing it. TanStack Router similarly
recommends committing its generated route tree
, while Rails
recommends committing its generated schema snapshot
.
When generated files have merge conflicts, resolve the source files and run codegen again instead of editing generated entries by hand.
Running codegen in CI
Run codegen before typechecking or building your application.
npm ci
node ace codegen
npm run typecheck
npm test
The command relies on @adonisjs/assembler, so development dependencies must be installed when it runs.
You can learn more in the codegen , application lifecycle , and service providers guides.
For implementation details, see the pull requests for
application warmup
,
standalone codegen in Assembler
, and the
codegen command in core
.