Controller Discovery
Configure how Girouette discovers your controllers
Controller Discovery
By default, Girouette expects controllers in app/controllers. However, you can configure it to work with any project structure: feature-based, hexagonal architecture, domain-driven design, or any custom layout.
Configuration Overview
To use a custom file structure, you need to configure three things:
indexControllers()hook inadonisrc.ts- tells Girouette where to find controllersimportsinpackage.json- defines the TypeScript/Node.js path aliaseshotHook.boundariesinpackage.json- enables HMR for your controller paths
The indexControllers Hook
The indexControllers() hook accepts three options:
import { indexControllers } from '@adonisjs-community/girouette'
export default defineConfig({
hooks: {
init: [
indexControllers({
source: './app', // Base directory to scan
glob: ['**/*_controller.ts'], // File patterns to match
importAlias: '#app', // Import alias prefix
}),
],
},
})| Option | Default | Description |
|---|---|---|
source | 'app/controllers' | Base directory containing your controllers |
glob | ['**/*_controller.ts'] | Glob patterns to match controller files |
importAlias | '#controllers' | The import alias that maps to your source directory |
The importAlias must match an entry in your package.json imports field. This is how Node.js resolves the generated imports in start/routes.girouette.ts.
Feature-Based Architecture
A feature-based structure groups all related files (controllers, models, services) by feature rather than by type.
Project Structure
app/
├── features/
│ ├── users/
│ │ ├── users_controller.ts
│ │ ├── user.ts
│ │ └── users_service.ts
│ ├── posts/
│ │ ├── posts_controller.ts
│ │ ├── post.ts
│ │ └── posts_service.ts
│ └── comments/
│ ├── comments_controller.ts
│ └── comment.ts
└── middleware/
└── auth_middleware.tsConfigure adonisrc.ts
import { indexControllers } from '@adonisjs-community/girouette'
export default defineConfig({
hooks: {
init: [
indexControllers({
source: './app/features',
glob: ['**/*_controller.ts'],
importAlias: '#features',
}),
],
},
})Configure package.json
Add the import alias and HMR boundaries:
{
"imports": {
"#features/*": "./app/features/*.js"
},
"hotHook": {
"boundaries": [
"./app/features/**/*_controller.ts",
"./app/middleware/*.ts"
]
}
}Hexagonal Architecture
Hexagonal (ports & adapters) architecture separates your domain from infrastructure. Controllers typically live in the infrastructure or adapters layer.
Project Structure
app/
├── domain/
│ ├── users/
│ │ ├── user.ts
│ │ └── user_repository.ts
│ └── orders/
│ ├── order.ts
│ └── order_repository.ts
├── application/
│ ├── users/
│ │ └── create_user_use_case.ts
│ └── orders/
│ └── place_order_use_case.ts
└── infrastructure/
├── http/
│ ├── users_controller.ts
│ └── orders_controller.ts
└── persistence/
├── lucid_user_repository.ts
└── lucid_order_repository.tsConfigure adonisrc.ts
import { indexControllers } from '@adonisjs-community/girouette'
export default defineConfig({
hooks: {
init: [
indexControllers({
source: './app/infrastructure/http',
glob: ['**/*_controller.ts'],
importAlias: '#infrastructure/http',
}),
],
},
})Configure package.json
{
"imports": {
"#infrastructure/*": "./app/infrastructure/*.js",
"#domain/*": "./app/domain/*.js",
"#application/*": "./app/application/*.js"
},
"hotHook": {
"boundaries": [
"./app/infrastructure/http/**/*_controller.ts"
]
}
}Scanning the Entire App Directory
If your controllers are scattered across multiple directories (mixed architecture), you can scan the entire app folder:
import { indexControllers } from '@adonisjs-community/girouette'
export default defineConfig({
hooks: {
init: [
indexControllers({
source: './app', // Scan entire app directory
glob: ['**/*_controller.ts'], // Match all controllers
importAlias: '#app', // Use #app alias
}),
],
},
}){
"imports": {
"#app/*": "./app/*.js"
},
"hotHook": {
"boundaries": [
"./app/**/*_controller.ts",
"./app/middleware/*.ts"
]
}
}When scanning a broad directory like ./app, ensure your glob pattern is specific enough to avoid matching unintended files.
Understanding HMR Boundaries
The hotHook.boundaries configuration tells AdonisJS which files can be hot-reloaded without a full server restart. For Girouette to work with HMR:
{
"hotHook": {
"boundaries": [
"./app/**/*_controller.ts"
]
}
}Key points:
- Boundaries must include all controller file paths
- The pattern should match your
source+globconfiguration - Include middleware if you want HMR for middleware changes too
Troubleshooting
Import Errors
If you see errors like Cannot find module '#features/users/users_controller':
- Verify your
package.jsonimports match theimportAliasinindexControllers() - Check that the path pattern uses
.jsextension (Node.js ESM resolution) - Start the dev server to regenerate the routes file
HMR Not Working
If changes require a full restart:
- Check that
hotHook.boundariesincludes your controller paths - Ensure the glob pattern matches your actual file locations
- Verify you're running with
node ace serve --hmr
Controllers Not Found
If Girouette doesn't detect your controllers:
- Verify controllers end with
_controller.ts(or update theglobpattern) - Check that
sourcepoints to the correct directory - Ensure controllers have a default export
Next Steps
- HMR Support - Learn more about hot module replacement
- Installation - Review the default configuration
- Basic Routing - Start defining routes