Database

Nest is database agnostic, allowing you to easily integrate with any SQL or NoSQL database. You have a number of options available to you, depending on your preferences. At the most general level, connecting Nest to a database is simply a matter of loading an appropriate Node.js driver for the database, just as you would with Express or Fastify.

You can also directly use any general purpose Node.js database integration library or ORM, such as Sequelize (recipe), Knex.js (tutorial) and TypeORM, to operate at a higher level of abstraction.

For convenience, Nest also provides tight integration with TypeORM out-of-the box with @nestjs/typeorm, which we’ll cover in the current chapter, and Mongoose with @nestjs/mongoose, which is covered in this chapter. These integrations provide additional NestJS-specific features, such as model/repository injection, testability, and asynchronous configuration to make accessing your chosen database even easier.

TypeORM Integration

For integrating with SQL and NoSQL databases, Nest provides the @nestjs/typeorm package. Nest uses TypeORM because it’s the most mature Object Relational Mapper (ORM) available for TypeScript. Since it’s written in TypeScript, it integrates well with the Nest framework.

To begin using it, we first install the required dependencies. In this chapter, we’ll demonstrate using the popular MySQL Relational DBMS, but TypeORM provides support for many relational databases, such as PostgreSQL, Oracle, Microsoft SQL Server, SQLite, and even NoSQL databases like MongoDB. The procedure we walk through in this chapter will be the same for any database supported by TypeORM. You’ll simply need to install the associated client API libraries for your selected database.

  1. $ npm install --save @nestjs/typeorm typeorm mysql

Once the installation process is complete, we can import the TypeOrmModule into the root AppModule.

  1. @@filename(app.module)
  2. import { Module } from '@nestjs/common';
  3. import { TypeOrmModule } from '@nestjs/typeorm';
  4. @Module({
  5. imports: [
  6. TypeOrmModule.forRoot({
  7. type: 'mysql',
  8. host: 'localhost',
  9. port: 3306,
  10. username: 'root',
  11. password: 'root',
  12. database: 'test',
  13. entities: [],
  14. synchronize: true,
  15. }),
  16. ],
  17. })
  18. export class AppModule {}

The forRoot() method accepts the same configuration object as createConnection() from the TypeORM package. Alternatively, rather than passing a configuration object to forRoot(), we can create an ormconfig.json file in the project root directory.

  1. {
  2. "type": "mysql",
  3. "host": "localhost",
  4. "port": 3306,
  5. "username": "root",
  6. "password": "root",
  7. "database": "test",
  8. "entities": ["dist/**/*.entity{.ts,.js}"],
  9. "synchronize": true
  10. }

warning Warning Static glob paths (e.g. dist/**/*.entity{{ '{' }} .ts,.js{{ '}' }}) won’t work properly with webpack.

Then, we can call forRoot() without any options:

  1. @@filename(app.module)
  2. import { Module } from '@nestjs/common';
  3. import { TypeOrmModule } from '@nestjs/typeorm';
  4. @Module({
  5. imports: [TypeOrmModule.forRoot()],
  6. })
  7. export class AppModule {}

Once this is done, the TypeORM Connection and EntityManager objects will be available to inject across the entire project (without needing to import any modules), for example:

  1. @@filename(app.module)
  2. import { Connection } from 'typeorm';
  3. @Module({
  4. imports: [TypeOrmModule.forRoot(), PhotoModule],
  5. })
  6. export class AppModule {
  7. constructor(private readonly connection: Connection) {}
  8. }
  9. @@switch
  10. import { Connection } from 'typeorm';
  11. @Dependencies(Connection)
  12. @Module({
  13. imports: [TypeOrmModule.forRoot(), PhotoModule],
  14. })
  15. export class AppModule {
  16. constructor(connection) {
  17. this.connection = connection;
  18. }
  19. }

Repository pattern

TypeORM supports the repository design pattern, so each entity has its own Repository. These repositories can be obtained from the database connection.

To continue the example, we need at least one entity. We’ll use the Photo entity from the official TypeORM documentation.

  1. @@filename(photo.entity)
  2. import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
  3. @Entity()
  4. export class Photo {
  5. @PrimaryGeneratedColumn()
  6. id: number;
  7. @Column({ length: 500 })
  8. name: string;
  9. @Column('text')
  10. description: string;
  11. @Column()
  12. filename: string;
  13. @Column('int')
  14. views: number;
  15. @Column()
  16. isPublished: boolean;
  17. }

The Photo entity belongs to the photo directory. This directory represents the PhotoModule. It’s your decision where to keep your model files. We recommend creating them near their domain, in the corresponding module directory.

To begin using Photo entity, we need to let TypeORM know about it by inserting it into the entities array (unless you use a static glob path):

  1. @@filename(app.module)
  2. import { Module } from '@nestjs/common';
  3. import { TypeOrmModule } from '@nestjs/typeorm';
  4. import { Photo } from './photo/photo.entity';
  5. @Module({
  6. imports: [
  7. TypeOrmModule.forRoot({
  8. type: 'mysql',
  9. host: 'localhost',
  10. port: 3306,
  11. username: 'root',
  12. password: 'root',
  13. database: 'test',
  14. entities: [Photo],
  15. synchronize: true,
  16. }),
  17. ],
  18. })
  19. export class AppModule {}

Let’s have a look at the PhotoModule now:

  1. @@filename(photo.module)
  2. import { Module } from '@nestjs/common';
  3. import { TypeOrmModule } from '@nestjs/typeorm';
  4. import { PhotoService } from './photo.service';
  5. import { PhotoController } from './photo.controller';
  6. import { Photo } from './photo.entity';
  7. @Module({
  8. imports: [TypeOrmModule.forFeature([Photo])],
  9. providers: [PhotoService],
  10. controllers: [PhotoController],
  11. })
  12. export class PhotoModule {}

This module uses the forFeature() method to define which repositories are registered in the current scope. With that, we can inject the PhotoRepository into the PhotoService using the @InjectRepository() decorator:

  1. @@filename(photo.service)
  2. import { Injectable } from '@nestjs/common';
  3. import { InjectRepository } from '@nestjs/typeorm';
  4. import { Repository } from 'typeorm';
  5. import { Photo } from './photo.entity';
  6. @Injectable()
  7. export class PhotoService {
  8. constructor(
  9. @InjectRepository(Photo)
  10. private readonly photoRepository: Repository<Photo>,
  11. ) {}
  12. findAll(): Promise<Photo[]> {
  13. return this.photoRepository.find();
  14. }
  15. }
  16. @@switch
  17. import { Injectable, Dependencies } from '@nestjs/common';
  18. import { InjectRepository } from '@nestjs/typeorm';
  19. import { Photo } from './photo.entity';
  20. @Injectable()
  21. @Dependencies(InjectRepository(Photo))
  22. export class PhotoService {
  23. constructor(photoRepository) {
  24. this.photoRepository = photoRepository;
  25. }
  26. findAll() {
  27. return this.photoRepository.find();
  28. }
  29. }

warning Notice Don’t forget to import the PhotoModule into the root AppModule.

If you want to use the repository outside of the module which imports TypeOrmModule.forFeature, you’ll need to re-export the providers generated by it.You can do this by exporting the whole module, like this:

  1. @@filename(photo.module)
  2. import { Module } from '@nestjs/common';
  3. import { TypeOrmModule } from '@nestjs/typeorm';
  4. import { Photo } from './photo.entity';
  5. @Module({
  6. imports: [TypeOrmModule.forFeature([Photo])],
  7. exports: [TypeOrmModule]
  8. })
  9. export class PhotoModule {}

Now if we import PhotoModule in PhotoHttpModule, we can use @InjectRepository(Photo) in the providers of the latter module.

  1. @@filename(photo-http.module)
  2. import { Module } from '@nestjs/common';
  3. import { PhotoModule } from './photo.module';
  4. import { PhotoService } from './photo.service';
  5. import { PhotoController } from './photo.controller';
  6. @Module({
  7. imports: [PhotoModule],
  8. providers: [PhotoService],
  9. controllers: [PhotoController]
  10. })
  11. export class PhotoHttpModule {}

Multiple databases

Some projects require multiple database connections. This can also be achieved with this module. To work with multiple connections, first create the connections. In this case, connection naming becomes mandatory.

Suppose you have a Person entity and an Album entity, each stored in their own database.

  1. const defaultOptions = {
  2. type: 'postgres',
  3. port: 5432,
  4. username: 'user',
  5. password: 'password',
  6. database: 'db',
  7. synchronize: true,
  8. };
  9. @Module({
  10. imports: [
  11. TypeOrmModule.forRoot({
  12. ...defaultOptions,
  13. host: 'photo_db_host',
  14. entities: [Photo],
  15. }),
  16. TypeOrmModule.forRoot({
  17. ...defaultOptions,
  18. name: 'personsConnection',
  19. host: 'person_db_host',
  20. entities: [Person],
  21. }),
  22. TypeOrmModule.forRoot({
  23. ...defaultOptions,
  24. name: 'albumsConnection',
  25. host: 'album_db_host',
  26. entities: [Album],
  27. }),
  28. ],
  29. })
  30. export class AppModule {}

warning Notice If you don’t set the name for a connection, its name is set to default. Please note that you shouldn’t have multiple connections without a name, or with the same name, otherwise they will get overridden.

At this point, you have each of your Photo, Person and Album entities registered with their own connection. With this setup, you have to tell the TypeOrmModule.forFeature() function and the @InjectRepository() decorator which connection should be used. If you do not pass any connection name, the default connection is used.

  1. @Module({
  2. imports: [
  3. TypeOrmModule.forFeature([Photo]),
  4. TypeOrmModule.forFeature([Person], 'personsConnection'),
  5. TypeOrmModule.forFeature([Album], 'albumsConnection'),
  6. ],
  7. })
  8. export class AppModule {}

You can also inject the Connection or EntityManager for a given connection:

  1. @Injectable()
  2. export class PersonService {
  3. constructor(
  4. @InjectConnection('personsConnection')
  5. private readonly connection: Connection,
  6. @InjectEntityManager('personsConnection')
  7. private readonly entityManager: EntityManager,
  8. ) {}
  9. }

Testing

When it comes to unit testing an application, we usually want to avoid making a database connection, keeping our test suites independent and their execution process as fast as possible. But our classes might depend on repositories that are pulled from the connection instance. How do we handle that? The solution is to create mock repositories. In order to achieve that, we set up custom providers. Each registered repository is automatically represented by an <EntityName>Repository token, where EntityName is the name of your entity class.

The @nestjs/typeorm package exposes the getRepositoryToken() function which returns a prepared token based on a given entity.

  1. @Module({
  2. providers: [
  3. PhotoService,
  4. {
  5. provide: getRepositoryToken(Photo),
  6. useValue: mockRepository,
  7. },
  8. ],
  9. })
  10. export class PhotoModule {}

Now a substitute mockRepository will be used as the PhotoRepository. Whenever any class asks for PhotoRepository using an @InjectRepository() decorator, Nest will use the registered mockRepository object.

Custom repository

TypeORM provides a feature called custom repositories. Custom repositories allow you to extend a base repository class, and enrich it with several special methods. To learn more about this feature, visit this page.

In order to create your custom repository, use the @EntityRepository() decorator and extend the Repository class.

  1. @EntityRepository(Author)
  2. export class AuthorRepository extends Repository<Author> {}

info Hint Both @EntityRepository() and Repository are imported from the typeorm package.

Once the class is created, the next step is to delegate instantiation responsibility to Nest. For this, we have to pass theAuthorRepository class to the TypeOrm.forFeature() method.

  1. @Module({
  2. imports: [TypeOrmModule.forFeature([AuthorRepository])],
  3. controller: [AuthorController],
  4. providers: [AuthorService],
  5. })
  6. export class AuthorModule {}

Afterward, simply inject the repository using the following construction:

  1. @Injectable()
  2. export class AuthorService {
  3. constructor(private readonly authorRepository: AuthorRepository) {}
  4. }

Async configuration

You may want to pass your repository module options asynchronously instead of statically. In this case, use the forRootAsync() method, which provides several ways to deal with async configuration.

One approach is to use a factory function:

  1. TypeOrmModule.forRootAsync({
  2. useFactory: () => ({
  3. type: 'mysql',
  4. host: 'localhost',
  5. port: 3306,
  6. username: 'root',
  7. password: 'root',
  8. database: 'test',
  9. entities: [__dirname + '/**/*.entity{.ts,.js}'],
  10. synchronize: true,
  11. }),
  12. });

Our factory behaves like any other asynchronous provider (e.g., it can be async and it’s able to inject dependencies through inject).

  1. TypeOrmModule.forRootAsync({
  2. imports: [ConfigModule],
  3. useFactory: async (configService: ConfigService) => ({
  4. type: 'mysql',
  5. host: configService.getString('HOST'),
  6. port: configService.getString('PORT'),
  7. username: configService.getString('USERNAME'),
  8. password: configService.getString('PASSWORD'),
  9. database: configService.getString('DATABASE'),
  10. entities: [__dirname + '/**/*.entity{.ts,.js}'],
  11. synchronize: true,
  12. }),
  13. inject: [ConfigService],
  14. });

Alternatively, you can use the useClass syntax:

  1. TypeOrmModule.forRootAsync({
  2. useClass: TypeOrmConfigService,
  3. });

The construction above will instantiate TypeOrmConfigService inside TypeOrmModule and use it to provide an options object by calling createTypeOrmOptions(). Note that this means that the TypeOrmConfigService has to implement the TypeOrmOptionsFactory interface, as shown below:

  1. @Injectable()
  2. class TypeOrmConfigService implements TypeOrmOptionsFactory {
  3. createTypeOrmOptions(): TypeOrmModuleOptions {
  4. return {
  5. type: 'mysql',
  6. host: 'localhost',
  7. port: 3306,
  8. username: 'root',
  9. password: 'root',
  10. database: 'test',
  11. entities: [__dirname + '/**/*.entity{.ts,.js}'],
  12. synchronize: true,
  13. };
  14. }
  15. }

In order to prevent the creation of TypeOrmConfigService inside TypeOrmModule and use a provider imported from a different module, you can use the useExisting syntax.

  1. TypeOrmModule.forRootAsync({
  2. imports: [ConfigModule],
  3. useExisting: ConfigService,
  4. });

This construction works the same as useClass with one critical difference - TypeOrmModule will lookup imported modules to reuse an existing ConfigService instead of instantiating a new one.

Example

A working example is available here.