VelaVela
Fundamentals

Modules

Modules organize the application into cohesive blocks of functionality.

Modules are the building blocks of a Vela application. Each module groups related controllers and providers together, and can import or export functionality to other modules.

Defining a Module

The @Module() decorator accepts an options object with four properties:

PropertyDescription
providersProviders to be instantiated and available within this module
controllersControllers that belong to this module
importsOther modules whose exported providers are needed
exportsProviders to make available to importing modules
import { Module } from '@velajs/vela';

@Module({
  providers: [SpiritsService],
  controllers: [SpiritsController],
  exports: [SpiritsService],
})
class SpiritsModule {}

Root Module

Every application has a root module that serves as the entry point. Bootstrap it with VelaFactory.create():

import { VelaFactory } from '@velajs/vela';

@Module({
  imports: [SpiritsModule, UsersModule],
})
class AppModule {}

const app = await VelaFactory.create(AppModule);

Feature Modules

Split your application into feature modules to keep related functionality together:

@Module({
  providers: [UsersService],
  controllers: [UsersController],
  exports: [UsersService],
})
class UsersModule {}

@Module({
  imports: [UsersModule],
  providers: [AuthService],
  controllers: [AuthController],
})
class AuthModule {}

The AuthModule imports UsersModule, which means UsersService (because it is exported) becomes available for injection inside AuthModule.

Imports and Exports

  • Imports — bring in exported providers from other modules
  • Exports — make providers available to modules that import this one

Only exported providers are shared. Providers that are registered but not exported remain private to their module.

Dynamic Modules

Dynamic modules allow you to create configurable modules at runtime. A dynamic module returns a DynamicModule object:

interface DynamicModule {
  module: Type;
  providers?: Provider[];
  controllers?: Type[];
  exports?: (Type | InjectionToken)[];
}

forRoot / forFeature Pattern

Use static methods to create configurable modules:

@Module({})
class DatabaseModule {
  static forRoot(url: string): DynamicModule {
    return {
      module: DatabaseModule,
      providers: [
        { provide: 'DB_URL', useValue: url },
        DatabaseService,
      ],
      exports: [DatabaseService],
    };
  }

  static forFeature(entities: Type[]): DynamicModule {
    const repositories = entities.map((entity) => ({
      provide: `${entity.name}Repository`,
      useFactory: (db: DatabaseService) => db.getRepository(entity),
      inject: [DatabaseService],
    }));

    return {
      module: DatabaseModule,
      providers: repositories,
      exports: repositories.map((r) => r.provide),
    };
  }
}

Use it in your root module:

@Module({
  imports: [
    DatabaseModule.forRoot('postgres://localhost/mydb'),
    DatabaseModule.forFeature([User, Post]),
  ],
})
class AppModule {}

Circular Dependencies

Vela detects circular module dependencies at startup and throws an error. If module A imports module B and module B imports module A, the application will fail to start with a clear error message. Restructure your modules or extract shared providers into a separate module to resolve this.

Edit on GitHub

Last updated on

On this page