### Full Example of Async Initialization with onReady Hook Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/07-lifecycle-hooks.md Illustrates a complete Fastify application setup where dependencies are initialized asynchronously using the onReady hook. This example shows the flow from application startup to dependency initialization and completion. ```javascript class Database { async init() { console.log('Connecting...') await this.connect() console.log('Connected') } } const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: true }) diContainer.register({ db: asClass(Database, { asyncInit: 'init' }) }) console.log('Before ready') await app.ready() console.log('After ready (db initialized)') // Output: // Before ready // Connecting... // Connected // After ready (db initialized) ``` -------------------------------- ### Complete Fastify Awilix TypeScript Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/05-types.md A full example demonstrating Fastify setup with Awilix for dependency injection. It includes defining application dependencies, registering them, and creating type-safe routes. ```typescript import fastify from 'fastify' import { asClass, asValue, Lifetime } from 'awilix' import { fastifyAwilixPlugin, Cradle, RequestCradle, FastifyAwilixOptions } from '@fastify/awilix' // Define application dependencies class UserRepository { async getUser(id: string) { return { id, name: 'John' } } } class UserService { constructor(userRepository: UserRepository) { this.userRepository = userRepository } getUserById(id: string) { return this.userRepository.getUser(id) } } interface CurrentUser { id: string name: string } // Extend Cradle with app-level dependencies declare module '@fastify/awilix' { interface Cradle { userRepository: UserRepository userService: UserService } interface RequestCradle { currentUser: CurrentUser } } const app = fastify() const options: FastifyAwilixOptions = { asyncInit: false, disposeOnClose: true } await app.register(fastifyAwilixPlugin, options) // Register dependencies const { diContainer } = await import('@fastify/awilix') diContainer.register({ userRepository: asClass(UserRepository, { lifetime: Lifetime.SINGLETON }), userService: asClass(UserService, { lifetime: Lifetime.SINGLETON }) }) // Register request-scoped dependencies in hook app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ currentUser: asValue({ id: request.headers['user-id'] || 'guest', name: request.headers['user-name'] || 'Guest' }) }) done() }) // Type-safe routes app.get('/profile', (request) => { // All resolutions are type-safe const userService = request.diScope.resolve('userService') const currentUser = request.diScope.resolve('currentUser') return { user: currentUser, message: `Hello ${currentUser.name}` } }) app.get('/user/:id', (request) => { const userService = app.diContainer.resolve('userService') const userId = request.params.id return userService.getUserById(userId) }) await app.listen({ port: 3000 }) ``` -------------------------------- ### Install @fastify/awilix and Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Install the necessary packages using npm. ```bash npm i @fastify/awilix awilix ``` -------------------------------- ### Install Dependencies Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Install the necessary packages for Fastify Awilix integration. ```bash npm install @fastify/awilix awilix ``` -------------------------------- ### Debug Logging Output Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/02-fastifyAwilixPlugin.md Shows example output when debug logging is enabled, illustrating the messages logged during dependency initialization. ```text [DI] Initializing dependency: database [DI] Dependency 'database' initialized in 234ms [DI] Initializing dependency: cache [DI] Dependency 'cache' initialized in 45ms ``` -------------------------------- ### Async Lifecycle Example with Fastify Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md This example shows how to register classes with asynchronous init and dispose methods. Ensure `asyncInit`, `asyncDispose`, and `disposeOnClose` are set to true for proper lifecycle management. ```javascript const fastify = require('fastify') const { asClass, Lifetime } = require('awilix') const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') class Database { async init () { console.log('DB: Connecting...') await new Promise(r => setTimeout(r, 200)) console.log('DB: Connected') } async dispose () { console.log('DB: Closing...') await new Promise(r => setTimeout(r, 100)) console.log('DB: Closed') } } class Cache { async init () { console.log('Cache: Warming up...') await new Promise(r => setTimeout(r, 150)) console.log('Cache: Ready') } async dispose () { console.log('Cache: Flushing...') await new Promise(r => setTimeout(r, 50)) console.log('Cache: Flushed') } } const app = fastify({ logger: false }) app.register(fastifyAwilixPlugin, { asyncInit: true, asyncDispose: true, disposeOnClose: true, enableDebugLogging: true }) diContainer.register({ database: asClass(Database, { lifetime: Lifetime.SINGLETON, asyncInit: 'init', asyncDispose: 'dispose' }), cache: asClass(Cache, { lifetime: Lifetime.SINGLETON, asyncInit: 'init', asyncDispose: 'dispose' }) }) app.get('/status', async (req, res) => { return { status: 'ok' } }) console.log('Starting app...') await app.ready() console.log('App ready!') await app.listen({ port: 3000 }) console.log('Server listening on port 3000') // Later, during shutdown console.log('Closing app...') await app.close() console.log('App closed!') // Output: // Starting app... // DB: Connecting... // Cache: Warming up... // DB: Connected // Cache: Ready // App ready! // Server listening on port 3000 // Closing app... // DB: Closing... // Cache: Flushing... // DB: Closed // Cache: Flushed // App closed! ``` -------------------------------- ### Basic Fastify Awilix Setup (PROXY Mode) Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/02-fastifyAwilixPlugin.md Sets up Fastify with the Awilix plugin using default settings and registers an app-level singleton dependency. ```javascript const fastify = require('fastify') const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') const { asClass, Lifetime } = require('awilix') const app = fastify() // Register plugin with defaults app.register(fastifyAwilixPlugin) // Register app-level singleton diContainer.register({ database: asClass(Database, { lifetime: Lifetime.SINGLETON, dispose: (db) => db.close() }) }) // Use in route app.get('/', async (req, res) => { const db = req.diScope.resolve('database') return db.query('SELECT 1') }) await app.ready() ``` -------------------------------- ### Compare asyncInit and eagerInject Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Illustrates the difference between asyncInit for asynchronous setup and eagerInject for synchronous, immediate instantiation and validation. ```javascript // Service with asyncInit class Database { async init () { // This method is called await connectToDatabase() } } diContainer.register({ db: asClass(Database, { asyncInit: 'init' }) }) // Service with eagerInject class Config { constructor () { // Constructor is called, no async if (!process.env.API_KEY) throw new Error('Missing API_KEY') } } diContainer.register({ config: asClass(Config, { eagerInject: true }) }) ``` -------------------------------- ### Full Example with Async Disposal on Close Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/07-lifecycle-hooks.md An example showing a Fastify application with Awilix plugin configured for asynchronous disposal on close. It includes a `Database` service with an async `dispose` method. ```javascript const { diContainer, fastifyAwilixPlugin } = require('@fastify/awilix') const { asClass } = require('awilix') class Database { async dispose() { console.log('Closing database...') await this.connection.close() } } const app = fastify() app.register(fastifyAwilixPlugin, { disposeOnClose: true, asyncDispose: true }) diContainer.register({ db: asClass(Database, { asyncDispose: 'dispose' }) }) await app.ready() console.log('Closing app...') await app.close() // Logs: "Closing database..." console.log('App closed') ``` -------------------------------- ### TypeScript CLASSIC Mode Setup Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Demonstrates CLASSIC mode with TypeScript, highlighting the necessity of type annotations for constructor parameters. Manual typing is required for resolve. ```typescript import { diContainerClassic } from '@fastify/awilix' import { asClass } from 'awilix' class UserRepository {} class UserService { constructor( userRepository: UserRepository, // Type annotation required logger: Logger // Type annotation required ) { this.userRepository = userRepository this.logger = logger } } diContainerClassic.register({ userRepository: asClass(UserRepository), logger: asClass(Logger) }) // Type-safe resolve requires manual typing const service = diContainerClassic.resolve('userService') ``` -------------------------------- ### Handle Asynchronous Initialization Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Ensure the Fastify application is ready before proceeding, especially when dealing with asynchronous setup. ```javascript await app.ready() ``` -------------------------------- ### TypeScript PROXY Mode Setup Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Illustrates how to set up PROXY mode with TypeScript, including defining the Cradle interface and registering classes. Type-safe resolution is demonstrated. ```typescript import { diContainer, Cradle } from '@fastify/awilix' import { asClass } from 'awilix' class UserRepository {} class UserService { constructor({ userRepository }: { userRepository: UserRepository }) { this.userRepository = userRepository } } declare module '@fastify/awilix' { interface Cradle { userRepository: UserRepository userService: UserService } } diContainer.register({ userRepository: asClass(UserRepository), userService: asClass(UserService) }) // Type-safe resolve const service = diContainer.resolve('userService') // Type: UserService ``` -------------------------------- ### Custom Logger Implementation Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/05-types.md Shows how to implement the `Logger` type for custom logging. This logger can then be passed to the `fastifyAwilixPlugin` options. ```typescript const myLogger: Logger = (message: string) => { console.log(`[DI] ${message}`) } app.register(fastifyAwilixPlugin, { enableDebugLogging: true, loggerFn: myLogger }) ``` -------------------------------- ### Registering Plugin with Configuration Error Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/02-fastifyAwilixPlugin.md This example demonstrates a configuration error where both a pre-created container and an injection mode are specified, which is not allowed. ```javascript app.register(fastifyAwilixPlugin, { container: diContainer, injectionMode: 'CLASSIC' }) ``` -------------------------------- ### PROXY Mode: Optional Dependencies Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Illustrates how to handle optional dependencies in PROXY mode using default parameter values during object destructuring. If an optional dependency is not provided, it defaults to null. ```javascript const { diContainer } = require('@fastify/awilix') const { asFunction } = require('awilix') const createService = ({ required, optional = null }) => { return { required, optional } } diContainer.register({ required: asFunction(() => 'value'), service: asFunction(createService) }) const service = diContainer.resolve('service') // { required: 'value', optional: null } ``` -------------------------------- ### Registering Fastify Awilix Plugin with Options Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/05-types.md Example of how to register the Fastify Awilix plugin with custom options. Ensure imports are correct. ```typescript import { FastifyAwilixOptions, fastifyAwilixPlugin } from '@fastify/awilix' import fastify from 'fastify' const options: FastifyAwilixOptions = { asyncInit: true, disposeOnClose: true } const app = fastify() app.register(fastifyAwilixPlugin, options) ``` -------------------------------- ### Handle Initialization Errors Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md If a dependency's `asyncInit` method throws an error, `app.ready()` will reject. The application will not start, and no requests can be handled. The error message from the failing `init` method will be propagated. ```javascript class FailingService { async init () { throw new Error('Initialization failed') } } diContainer.register({ service: asClass(FailingService, { asyncInit: 'init' }) }) try { await app.ready() } catch (err) { console.error(err.message) // "Initialization failed" // App did not start // No requests can be handled } ``` -------------------------------- ### Example: Request Scope Disposal with onResponse Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/07-lifecycle-hooks.md Demonstrates setting up Fastify Awilix with `disposeOnResponse: true` and registering a scoped service that is disposed when the response is sent. Ensure the `onRequest` hook runs before the route handler. ```javascript const app = fastify() app.register(fastifyAwilixPlugin, { disposeOnResponse: true }) class RequestLogger { dispose() { console.log('Disposed request logger') } } app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ logger: asClass(RequestLogger, { lifetime: Lifetime.SCOPED, dispose: (instance) => instance.dispose() }) }) done() }) app.get('/', async (request) => { return { ok: true } }) // When response is sent: // 1. Route handler completes // 2. Response sent to client // 3. onResponse hook runs // 4. request.diScope.dispose() called // Output: "Disposed request logger" ``` -------------------------------- ### Set Environment Variables (Fix) Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/11-error-handling.md This bash script demonstrates how to set the required environment variables before starting the application to resolve configuration errors. ```bash export DATABASE_URL=postgres://localhost/mydb export API_KEY=secret123 npm start ``` -------------------------------- ### Observe Async Initialization Timing Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md When `asyncInit` is enabled, dependency initialization methods are called during `app.ready()`. This example demonstrates that `app.ready()` completes only after all async initialization tasks are finished. ```javascript const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: true }) class Service { async init () { console.log('Service init started') await new Promise(r => setTimeout(r, 100)) console.log('Service init finished') } } diContainer.register({ service: asClass(Service, { asyncInit: 'init' }) }) console.log('1. Before ready') await app.ready() console.log('2. After ready') // Output: // 1. Before ready // Service init started // Service init finished // 2. After ready ``` -------------------------------- ### Register Modules with Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Register modules for dependency injection using Awilix's `asClass`, `asFunction`, and `Lifetime` options. This example shows registering a singleton `UserRepository` with a custom dispose function. ```javascript const { diContainer, diContainerClassic, diContainerProxy } = require('@fastify/awilix') const { asClass, asFunction, Lifetime } = require('awilix') diContainer.register({ userRepository: asClass(UserRepository, { lifetime: Lifetime.SINGLETON, dispose: (module) => module.dispose(), }), }) ``` -------------------------------- ### Error Handling for Failed Async Initialization Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/07-lifecycle-hooks.md Shows how Fastify Awilix handles errors during asynchronous initialization. If an 'asyncInit' method throws an error, app.ready() will reject, preventing the application from starting. ```javascript class BrokenService { async init() { throw new Error('Failed to initialize') } } diContainer.register({ service: asClass(BrokenService, { asyncInit: 'init' }) }) try { await app.ready() } catch (err) { console.error('Startup failed:', err.message) // "Failed to initialize" // app did not start } ``` -------------------------------- ### Fastify Awilix Scope Inheritance Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/06-fastify-decorations.md Illustrates how dependencies registered at the application level (`appConfig`) can be resolved from the request scope, and how request-specific dependencies (`requestConfig`) are registered and resolved within a request. ```javascript const { diContainer } = require('@fastify/awilix') const { asValue } = require('awilix') diContainer.register({ appConfig: asValue({ appName: 'MyApp' }) }) ``` ```javascript app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ requestConfig: asValue({ requestId: request.id }) }) done() }) ``` ```javascript app.get('/', (request) => { // Can resolve app-level dependency from request scope const appConfig = request.diScope.resolve('appConfig') // Can resolve request-level dependency const requestConfig = request.diScope.resolve('requestConfig') // Cannot resolve from app-level if not inherited // Each request gets separate instance of request-scoped deps return { appConfig, requestConfig } }) ``` -------------------------------- ### Create Fastify App Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Initialize a new Fastify application instance. ```javascript const app = fastify() ``` -------------------------------- ### Handle Async Init Failure in Fastify Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/11-error-handling.md Demonstrates how to catch errors when a dependency's async initialization fails. Ensure `app.ready()` is wrapped in a try-catch block to handle startup errors gracefully. ```javascript const { diContainer, fastifyAwilixPlugin } = require('@fastify/awilix') const { asClass } = require('awilix') class Database { async init () { // Simulating connection failure throw new Error('Failed to connect to database') } } const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: true }) diContainer.register({ database: asClass(Database, { asyncInit: 'init' }) }) try { await app.ready() console.log('App ready') } catch (err) { console.error('App startup failed:', err.message) // Error: Failed to connect to database process.exit(1) } ``` -------------------------------- ### Access App Scope Dependencies Outside Requests Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/10-scope-management.md Before starting the server, you can resolve application-scoped dependencies directly from `app.diContainer`. ```javascript // Before app.listen() const logger = app.diContainer.resolve('logger') logger.info('App starting') ``` -------------------------------- ### Register, Resolve, and Dispose Dependencies with Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/03-containers.md Demonstrates how to register a singleton dependency with a dispose callback, check its existence, resolve it multiple times to confirm singleton behavior, access it via the cradle, and finally dispose of all registered dependencies. ```javascript const { diContainer } = require('@fastify/awilix') const { asClass, Lifetime } = require('awilix') class UserRepository { getUser(id) { return { id, name: 'John' } } dispose() { console.log('Cleanup repository') } } diContainer.register({ userRepository: asClass(UserRepository, { lifetime: Lifetime.SINGLETON, dispose: (instance) => instance.dispose() }) }) // Check if registered console.log(diContainer.has('userRepository')) // true // Resolve (will return same instance due to SINGLETON lifetime) const repo1 = diContainer.resolve('userRepository') const repo2 = diContainer.resolve('userRepository') console.log(repo1 === repo2) // true // PROXY mode: access via cradle const repo3 = diContainer.cradle.userRepository console.log(repo1 === repo3) // true // Clean up await diContainer.dispose() // Logs: "Cleanup repository" ``` -------------------------------- ### CLASSIC Mode: Registering Dependencies Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Demonstrates how to register dependencies in CLASSIC mode, where the order of registration must match the constructor parameter order. This mode uses direct function calls and has no proxy overhead. ```javascript const { diContainerClassic } = require('@fastify/awilix') const { asClass, asFunction } = require('awilix') // With class (parameters are positional) class UserService { constructor (userRepository, logger, config) { // Parameters must match registration order this.userRepository = userRepository this.logger = logger this.config = config } } // With function const createUserService = (userRepository, logger, config) => { return new UserService(userRepository, logger, config) } // Register in order (MUST MATCH CONSTRUCTOR PARAMETER ORDER) diContainerClassic.register({ userRepository: asClass(UserRepository), logger: asClass(Logger), config: asValue(configObject) }) ``` -------------------------------- ### Create Default and Proxy Containers Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/03-containers.md Demonstrates the creation of default and proxy injection mode containers. These are module-level singletons. ```javascript const diContainerProxy = awilix.createContainer({ injectionMode: 'PROXY' }) const diContainerClassic = awilix.createContainer({ injectionMode: 'CLASSIC' }) ``` -------------------------------- ### Register Scoped Dependencies with Hooks Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Register request-scoped dependencies using `app.addHook`. This example registers a `userService` that depends on `userRepository` and uses request parameters. ```javascript app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ userService: asFunction( ({ userRepository }) => { return new UserService(userRepository, request.params.countryId) }, { lifetime: Lifetime.SCOPED, dispose: (module) => module.dispose(), } ), }) done() }) ``` -------------------------------- ### Registering Dependencies with Async Initialization Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/07-lifecycle-hooks.md Demonstrates two ways to register dependencies for asynchronous initialization: using the 'asyncInit' flag to call a specific method, or using 'eagerInject' to instantiate the dependency immediately. ```javascript // Option 1: asyncInit flag diContainer.register({ database: asClass(Database, { asyncInit: 'init' // calls db.init() during onReady }) }) // Option 2: eagerInject flag diContainer.register({ cache: asClass(Cache, { eagerInject: true // instantiates at onReady }) }) ``` -------------------------------- ### Custom Logger Function for Debug Output Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Provide a custom `loggerFn` to control how debug messages are logged. This example logs messages with a timestamp and a custom prefix. ```javascript app.register(fastifyAwilixPlugin, { asyncInit: true, enableDebugLogging: true, loggerFn: (message) => { // Custom logging const timestamp = new Date().toISOString() console.log(`[${timestamp}] [DI] ${message}`) } }) ``` -------------------------------- ### Async Initialization with Fastify Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Configure Fastify Awilix for asynchronous initialization of dependencies. Use the `asyncInit` option when registering a class and `await app.ready()` to ensure initialization is complete. ```javascript class Database { async init () { await this.connect() } } app.register(fastifyAwilixPlugin, { asyncInit: true }) diContainer.register({ database: asClass(Database, { asyncInit: 'init' }) }) await app.ready() // Waits for database to connect ``` -------------------------------- ### Handle Errors in asyncDispose Method Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md If an `asyncDispose` method rejects, `app.close()` will fail. This example shows how to register a service with a failing dispose method and catch the error. ```javascript class FailingService { async dispose () { throw new Error('Cleanup failed') } } diContainer.register({ service: asClass(FailingService, { asyncDispose: 'dispose' }) }) try { await app.close() } catch (err) { console.error('Shutdown error:', err.message) // "Cleanup failed" } ``` -------------------------------- ### Fastify Awilix Plugin: Debug Slow Startup Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/04-configuration.md Enable debug logging for slow dependency initialization during startup. Customize the logger function to output startup messages. ```javascript const app = fastify({ logger: { level: 'debug' } }) app.register(fastifyAwilixPlugin, { asyncInit: true, enableDebugLogging: true, loggerFn: (message) => { console.log(`[Startup] ${message}`) } }) // Output during startup: // [Startup] Initializing dependency: database // [Startup] Dependency 'database' initialized in 1234ms // [Startup] Initializing dependency: cache // [Startup] Dependency 'cache' initialized in 567ms ``` -------------------------------- ### Initialize Fastify with Awilix Plugin Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/10-scope-management.md Register the fastifyAwilixPlugin to enable dependency injection. `app.diContainer` becomes available after registration. ```javascript const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') const app = fastify() app.register(fastifyAwilixPlugin) // app.diContainer is the application scope // It's the same instance for the entire app lifetime ``` -------------------------------- ### Configure Eager Injection for a Singleton Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Register a class as a singleton with eager injection enabled. This ensures the constructor runs during app.ready(), allowing for immediate validation or setup. ```javascript const { diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') class Config { constructor () { if (!process.env.API_KEY) { throw new Error('API_KEY is required') } this.apiKey = process.env.API_KEY } } diContainer.register({ config: asClass(Config, { lifetime: Lifetime.SINGLETON, eagerInject: true // Instantiate during app.ready() }) }) ``` -------------------------------- ### Register @fastify/awilix Plugin Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Set up the @fastify/awilix plugin with Fastify, configuring options like `disposeOnClose` and `disposeOnResponse`. ```javascript const { fastifyAwilixPlugin } = require('@fastify/awilix') const fastify = require('fastify') app = fastify({ logger: true }) app.register(fastifyAwilixPlugin, { disposeOnClose: true, disposeOnResponse: true, strictBooleanEnforced: true }) ``` -------------------------------- ### Import Core APIs from @fastify/awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Import the main plugin function and container constructors. This is the primary entry point for using the library. ```javascript const { fastifyAwilixPlugin, diContainer, diContainerProxy, diContainerClassic } = require('@fastify/awilix') ``` -------------------------------- ### Race Condition in Tests Cleanup Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/11-error-handling.md This example shows how to prevent race conditions in tests by ensuring the dependency injection container is disposed of after each test. This is crucial for maintaining isolated test environments. ```javascript const { describe, it, afterEach } = require('node:test') const { diContainer } = require('@fastify/awilix') describe('UserService', () => { afterEach(async () => { // ✅ Always cleanup await diContainer.dispose() }) it('test 1', async () => { diContainer.register({ userService: asClass(UserService) }) // test }) it('test 2', async () => { // Previous registration is gone diContainer.register({ userService: asClass(AnotherUserService) }) // test }) }) ``` -------------------------------- ### Module Structure Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Illustrates the directory layout of the @fastify/awilix package. ```tree @fastify/awilix/ ├── index.js # Main entry point ├── lib/ │ └── fastifyAwilixPlugin.js # Plugin implementation ├── types/ │ ├── index.d.ts # TypeScript definitions │ └── index.tst.ts # Type tests ├── test/ # Test suite └── package.json ``` -------------------------------- ### Missing Environment Variable Error Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/11-error-handling.md This scenario demonstrates how to catch errors caused by missing environment variables during application configuration. Ensure all required environment variables are set before starting the application. ```javascript class AppConfig { constructor () { const required = ['DATABASE_URL', 'API_KEY'] for (const key of required) { if (!process.env[key]) { throw new Error(`Missing required env var: ${key}`) } } this.databaseUrl = process.env.DATABASE_URL this.apiKey = process.env.API_KEY } } const app = fastify() app.register(fastifyAwilixPlugin, { eagerInject: true // Fails early }) diContainer.register({ config: asClass(AppConfig, { eagerInject: true }) }) // Missing DATABASE_URL try { await app.ready() } catch (err) { console.error('Config error:', err.message) // Missing required env var: DATABASE_URL process.exit(1) } ``` -------------------------------- ### Registering onReady Hook for Async Initialization Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/02-fastifyAwilixPlugin.md Adds an `onReady` hook to execute asynchronous initialization of Awilix dependencies if enabled. Logs timing information for debugging and handles potential errors during initialization. ```javascript if (asyncInitEnabled || eagerInjectEnabled) { fastify.addHook('onReady', function awilixOnReady (done) { const startTime = Date.now() fastify.log.debug('Start async awilix init') awilixManager.executeInit().then(() => { const endTime = Date.now() fastify.log.debug(`Finished async awilix init in ${endTime - startTime} msecs`) done() }, (err) => { if (err) { fastify.log.error('Error during async awilix init') return done(err) } done() }) }) } ``` -------------------------------- ### Fail-Fast Configuration with Eager Injection Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Implement a fail-fast configuration strategy by registering a configuration class with eager injection. This ensures all required environment variables are validated upon application startup. ```javascript const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') class AppConfig { constructor () { // Validate immediately const required = ['DATABASE_URL', 'API_KEY', 'SECRET'] for (const key of required) { if (!process.env[key]) { throw new Error(`Missing required env var: ${key}`) } } this.databaseUrl = process.env.DATABASE_URL this.apiKey = process.env.API_KEY this.secret = process.env.SECRET } } const app = fastify() app.register(fastifyAwilixPlugin, { eagerInject: true }) diContainer.register({ config: asClass(AppConfig, { lifetime: Lifetime.SINGLETON, eagerInject: true }) }) try { await app.ready() // If we reach here, all env vars are present } catch (err) { console.error('Missing configuration:', err.message) process.exit(1) } ``` -------------------------------- ### Configuration Options Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md Details on the various options available for configuring the @fastify/awilix plugin. ```APIDOC ## Configuration Options ### Description This section outlines the available options for configuring the @fastify/awilix plugin. | Option | Type | Default | Purpose | |------------------------|-------------------------------------|---------|---------------------------------------------| | `disposeOnResponse` | boolean | true | Clean up request scope after response | | `disposeOnClose` | boolean | true | Clean up app scope on app close | | `injectionMode` | 'PROXY' \| 'CLASSIC' | 'PROXY' | Dependency passing method | | `container` | AwilixContainer | undefined | Use pre-created container | | `asyncInit` | boolean | true | Process asyncInit methods | | `asyncDispose` | boolean | true | Process asyncDispose methods | | `eagerInject` | boolean | true | Eagerly instantiate marked dependencies | | `strictBooleanEnforced`| boolean | true | Validate boolean resolver fields | | `enableDebugLogging` | boolean | false | Log initialization timing | | `loggerFn` | (msg: string) => void | console.log | Custom logger for debug messages | See: [04-configuration.md](04-configuration.md) ``` -------------------------------- ### Fastify Awilix Plugin: Strict Boolean Validation Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/04-configuration.md Demonstrates strict boolean validation for the 'enabled' field in resolver configurations. With `strictBooleanEnforced: true` (default), invalid values like strings will throw an error. ```javascript const { diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') const app = fastify() app.register(fastifyAwilixPlugin, { strictBooleanEnforced: true // default }) // ❌ Throws error diContainer.register({ myService: asClass(MyService, { enabled: 'yes' // Invalid: not a boolean }) }) // ✅ Works diContainer.register({ myService: asClass(MyService, { enabled: true // Valid }) }) ``` -------------------------------- ### PROXY Mode: Refactoring Example Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Shows how PROXY mode simplifies refactoring by allowing reordering or addition of dependencies in the constructor's destructured object without affecting the registration. This enhances maintainability and IDE support. ```javascript class PaymentService { constructor ({ stripeClient, database }) { this.stripe = stripeClient this.db = database } } // Refactored to add new dependency (order doesn't matter): class PaymentService { constructor ({ database, logger, stripeClient }) { // Reordered, renamed some properties this.logger = logger this.db = database this.stripe = stripeClient } } // No changes needed to registration! ``` -------------------------------- ### Plugin Entry Point Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/00-index.md The main function to register the @fastify/awilix plugin with your Fastify application. It accepts an options object for configuration. ```APIDOC ## Plugin Entry Point ### Description This section details the main function for registering the @fastify/awilix plugin. ### Usage ```javascript const { fastifyAwilixPlugin } = require('@fastify/awilix') // Register the plugin with Fastify app.register(fastifyAwilixPlugin, options) ``` ### Main Function `fastifyAwilixPlugin` ### Type `FastifyPluginCallback>` ### Registration `app.register(fastifyAwilixPlugin, options)` See: [02-fastifyAwilixPlugin.md](02-fastifyAwilixPlugin.md) ``` -------------------------------- ### Enable Async Features in Fastify Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Register the @fastify/awilix plugin with configuration flags to enable asynchronous initialization, disposal, and eager injection of dependencies. Ensure `app.ready()` is called to trigger initialization. ```javascript const { fastifyAwilixPlugin } = require('@fastify/awilix') const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: true, // Default: true asyncDispose: true, // Default: true eagerInject: true, // Default: true enableDebugLogging: false // Default: false }) await app.ready() // All asyncInit and eagerInject dependencies are initialized ``` -------------------------------- ### CLASSIC Mode: Registration Order Matters Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/08-injection-modes.md Illustrates the critical importance of registration order in CLASSIC mode. Incorrect order leads to dependencies being injected into the wrong parameters, causing runtime errors. ```javascript const { diContainerClassic } = require('@fastify/awilix') const { asClass } = require('awilix') class UserService { constructor (userRepository, logger) { this.userRepository = userRepository this.logger = logger } } // ✅ Correct order diContainerClassic.register({ userRepository: asClass(UserRepository), logger: asClass(Logger) }) // ❌ Wrong order - will pass logger to userRepository and vice versa diContainerClassic.register({ logger: asClass(Logger), userRepository: asClass(UserRepository) }) ``` -------------------------------- ### Configuring Asynchronous Initialization and Disposal Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Enable async lifecycle hooks and eager injection by configuring the plugin and individual resolvers. ```javascript const { diContainer, fastifyAwilixPlugin } = '@fastify/awilix' const { asClass } = require('awilix') diContainer.register( 'dependency1', asClass(AsyncInitSetClass, { lifetime: 'SINGLETON', asyncInit: 'init', asyncDispose: 'dispose', eagerInject: true, }) ) app = fastify() await app.register(fastifyAwilixPlugin, { asyncInit: true, asyncDispose: true, eagerInject: true }) await app.ready() ``` -------------------------------- ### Registering Classes with Dependency Injection Source: https://github.com/fastify/fastify-awilix/blob/main/README.md Use asClass to register services and repositories with constructor injection and lifecycle management. ```javascript class UserService { constructor({ userRepository }) { this.userRepository = userRepository } dispose() { // Disposal logic goes here } } class UserRepository { constructor() { // Constructor logic goes here } dispose() { // Disposal logic goes here } } diContainer.register({ userService: asClass(UserService, { lifetime: Lifetime.SINGLETON, dispose: (module) => module.dispose(), }), userRepository: asClass(UserRepository, { lifetime: Lifetime.SINGLETON, dispose: (module) => module.dispose(), }), }) ``` -------------------------------- ### Registering Services with diContainer (PROXY) Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/03-containers.md Demonstrates how to register a service using the default diContainer, which utilizes PROXY injection mode. Dependencies are accessed as object properties. ```javascript const { diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') class UserService { constructor ({ userRepository, logger }) { // Access dependencies as object properties this.userRepository = userRepository this.logger = logger } } diContainer.register({ userService: asClass(UserService) }) ``` -------------------------------- ### Async Initialization and Disposal with Fastify Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/02-fastifyAwilixPlugin.md Enables asynchronous initialization and disposal for dependencies, ensuring resources are properly managed during application startup and shutdown. ```javascript const { fastifyAwilixPlugin, diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: true, asyncDispose: true, disposeOnClose: true }) class Database { async init() { console.log('Connecting to database...') await this.connect() } async dispose() { console.log('Closing database connection...') await this.close() } } diContainer.register({ db: asClass(Database, { lifetime: 'SINGLETON', asyncInit: 'init', asyncDispose: 'dispose' }) }) await app.ready() // At this point, Database.init() has been called await app.close() // At this point, Database.dispose() has been called ``` -------------------------------- ### Import Main APIs Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/01-overview.md All public APIs are re-exported from the main entry point of the @fastify/awilix package. ```javascript // From index.js const { diContainer, diContainerProxy, diContainerClassic, fastifyAwilixPlugin } = require('@fastify/awilix') ``` -------------------------------- ### Fastify Awilix Plugin: Fast Startup (Disable Async Features) Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/04-configuration.md Disable asynchronous initialization and eager injection for faster application startup. This requires manual dependency initialization. ```javascript const app = fastify() app.register(fastifyAwilixPlugin, { asyncInit: false, eagerInject: false, asyncDispose: false }) // App starts immediately without initializing dependencies // Faster startup, but requires manual dependency initialization ``` -------------------------------- ### Initialize Fastify Awilix and Access Request Scope Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/10-scope-management.md Register the Fastify Awilix plugin and demonstrate how each request receives a unique scope instance, which is a child of the application scope. ```javascript const { fastifyAwilixPlugin } = require('@fastify/awilix') const app = fastify() app.register(fastifyAwilixPlugin) app.get('/', (request) => { // request.diScope is created in onRequest hook // Each request gets a unique scope instance // It's a child of app.diContainer return { scopeId: request.diScope } }) ``` -------------------------------- ### Enabling `asyncInit` or `eagerInject` Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/04-configuration.md When `asyncInit` or `eagerInject` is enabled, the plugin registers an `onReady` hook to execute `awilixManager.executeInit()`. This ensures that dependencies marked for asynchronous initialization or eager injection are completed before `app.ready()` resolves. ```javascript app.register(fastifyAwilixPlugin, { asyncInit: true, // or eagerInject: true asyncDispose: false // can be independent }) ``` -------------------------------- ### Demonstrate Disposal Order Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/10-scope-management.md Illustrates the order of disposal for request-scoped dependencies. Dependencies are disposed in the reverse order of their registration. ```javascript class RequestLogger { dispose () { console.log('RequestLogger disposed') } } class RequestCache { dispose () { console.log('RequestCache disposed') } } app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ logger: asClass(RequestLogger, { lifetime: Lifetime.SCOPED, dispose: (instance) => instance.dispose() }), cache: asClass(RequestCache, { lifetime: Lifetime.SCOPED, dispose: (instance) => instance.dispose() }) }) done() }) // After response: // RequestCache disposed (last registered, first disposed) // RequestLogger disposed ``` -------------------------------- ### Multi-Database Support with Fastify and Awilix Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/12-common-patterns.md Manage multiple database connections (e.g., primary and replica) and inject the appropriate one based on the operation (read or write). This pattern optimizes database performance. ```javascript const app = fastify() app.register(fastifyAwilixPlugin) diContainer.register({ primaryDb: asClass(Database, { lifetime: Lifetime.SINGLETON }), replicaDb: asClass(Database, { lifetime: Lifetime.SINGLETON }) }) app.addHook('onRequest', (request, reply, done) => { request.diScope.register({ // Use replica for reads db: asFunction(({ replicaDb }) => { return replicaDb }), // Use primary for writes writeDb: asFunction(({ primaryDb }) => { return primaryDb }) }) done() }) app.get('/users/:id', async (request) => { const { db } = request.diScope const user = await db.query('SELECT * FROM users WHERE id = ?', [request.params.id]) return user }) app.post('/users', async (request) => { const { writeDb } = request.diScope const result = await writeDb.query( 'INSERT INTO users (name, email) VALUES (?, ?)', [request.body.name, request.body.email] ) return { id: result.insertId } }) ``` -------------------------------- ### Registering Services with diContainerClassic (CLASSIC) Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/03-containers.md Illustrates registering services with diContainerClassic, which uses CLASSIC injection mode. Dependencies are passed as function parameters in the order they are registered or declared. ```javascript const { diContainerClassic } = require('@fastify/awilix') const { asClass, asFunction } = require('awilix') class UserService { constructor (userRepository, logger) { // Receive dependencies as parameters this.userRepository = userRepository this.logger = logger } } // Register parameters in order (must match constructor parameter order) diContainerClassic.register({ userRepository: asClass(UserRepository), logger: asClass(Logger) }) // Or with a factory function diContainerClassic.register({ userService: asFunction((userRepository, logger) => { return new UserService(userRepository, logger) }) }) ``` -------------------------------- ### Handle Async Initialization Errors Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/12-common-patterns.md Configure `asyncInit: true` and wrap `app.ready()` in a try-catch block to handle potential errors during asynchronous initialization. ```javascript app.register(fastifyAwilixPlugin, { asyncInit: true, enableDebugLogging: true }) try { await app.ready() } catch (err) { console.error('Startup failed:', err) process.exit(1) } ``` -------------------------------- ### Register Dependency with Async Initialization Source: https://github.com/fastify/fastify-awilix/blob/main/_autodocs/09-async-lifecycle.md Define a class with an `init` method and register it with `asClass` in the `diContainer`. Specify `asyncInit: 'init'` in the options to ensure the `init` method is called during application startup. ```javascript const { diContainer } = require('@fastify/awilix') const { asClass } = require('awilix') class Database { constructor () { this.connection = null } async init () { console.log('Connecting to database...') this.connection = await mysql.createConnection({ host: 'localhost', user: 'root' }) console.log('Connected!') } async query (sql) { return this.connection.query(sql) } } diContainer.register({ database: asClass(Database, { lifetime: Lifetime.SINGLETON, asyncInit: 'init' // Call db.init() during app.ready() }) }) ```