What I learned about external configuration of the database connection in NestJS
Tags: 100daysofcode, nestjs, typeorm
Published: 2021-05-07 00:51:13

As part of my 100 day of code challenge, I am building a [web app](https://github.com/rhaecker/buzzz) using NestJS (and a not yet specified frontend framework). So one of the first things I tried setting up was the database connection. I plan on using a mongodb and because I might switch between different devices for development, I decided to sign up for a free account on a shared cluster at mongodb.com. 

This is a longer description of my journey with this. If you are only interested in the results, [click here](link: the results article).

# First steps with the db

Since I’m new to NestJS (and developing with Node as a backend in general) I started this, by following the frameworks [documentation](https://docs.nestjs.com/techniques/database). Although there is support for mongoose(link: https://mongoosejs.com), I decided to go with TypeORM. TypeORM is an Object Relational Mapper, that is written in TypeScript and since NestJS offers an integration, I felt like this would be a good choice. Another reason is, that I have no experience with mongodb, so if, for some reason, I decide, I won’t be using it for this project, it seems like it will be much easier changing databases with TypeORM, than if I would have used mongoose.

So following the documentation, I started by adding the database configuration to the `app.module.ts` like this:

```
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mongodb',
      url: 'mongodb+srv://<username>:<password>@some.subdomains.of.mongodb.net/<databasename>'
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
```

When I then started the server, the connection worked. Nice, this was easy!
Sadly at that point I stopped reading the documentation and started looking at the code. I realised, that I have to commit this to a public repository and I maybe shouldn’t commit login data to a publicly hosted database (actually, I think, no database connection data, publicly hosted or not, should go into a git repository).

# Looking for options to configure

So instead of reading the next few paragraphs in the database part of the [NestJS documentation] (https://docs.nestjs.com/techniques/database#typeorm-integration), I started to search for options on how to do this configuration outside of the code, in a configuration- or environment-file, that I simply don’t have to commit.
So instead of just reading about the ormconfig.json file, that is a way to configure TypeORM I looked for different options. And I found one, the [config module](https://docs.nestjs.com/techniques/configuration) of NestJS.
It internally uses [dotenv](https://github.com/motdotla/dotenv), which is used to load the content of a file called `.env` into the Node process.env. This is a common approach in Node.js and is useful to quickly swap the applications environment. The `.env`-file holds key-value pairs to represent the environment.
The configuration module of NestJS offers different ways to access and to store environment variables, ranging from different locations for the `.env`-file, to ignore the file and only use the runtime environment variables or even use a custom environment file, that allows for a more complex structure such as yaml or json. In this case, the values are parsed into key-value pairs for use in the process.env.
There is a lot more advanced options for more complex situations, but these are the basic options, that the module offers for a simpler use case.

So I again, opened the documentation of NestJS regarding the config module and ended up including it in the app.module.ts like this:

```
@Module({
  imports: [
    ConfigModule.forRoot()
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
```

For a first test, my `.env` file only included one value: 

`testvalue=testvalue`

As I wanted to see this in action, I changed the bootstrapped `app.service.ts` to look like this:

```
@Injectable()
export class AppService {

  constructor(private configService: ConfigService) {}

  getHello(): string {
    console.log(this.configService.get<string>('testvalue'));
    return 'Hello World!' + this.configService.get<string>('testvalue');
  }
}
```

This loads the value of the key ‘testvalue’ from the `.env` file and it works as expected. Nice!

Now one could be so naive to just think, well let’s use this in my `app.module.ts`. That was exactly what I did, but I didn’t get it to work (and I found some examples on the internet, so I think this is more a me problem). So I look into alternatives and found the `ormconfig.json` (yes, the one I could have found immediately …).

# Configuration using the ormconfig.json

So I actually found this in the documentation of TypeORM and because, when I tried to configure TypeORM in the `app.module.ts` with the config module, I got this error:

`Error: No connection options were found in any orm configuration files.`

Searching for this pointed me in the right direction. Configuring the database connection with the `ormconfig.json` file (or a lot of other file [formats](https://typeorm.io/#/using-ormconfig)) is done by creating the file in the root of the project. In my case, I created an `ormconfig.json` and it looks like this:

```
{
    "type": "mongodb",
    "url": "mongodb+srv://<username>:<password>@some.subdomains.of.mongodb.net/<databasename>"
}
```

If this configuration file is used, it is important to not put any configuration in the `app.module.ts`. One problem of this is, that it is an additional configuration file and since I don’t want too many different config files, I’d prefer not to create one only for the database. Another point is that I maybe want to run this in a container and then configure it from Environment variables for example. So this solution might not fit my needs.

I also discovered the option to configure TypeORM from the environment variables directly, by using the keys [TypeORM provides](https://typeorm.io/#/using-ormconfig/using-environment-variables). This would work for me, but there is one thing, that is not possible with this configuration (and most likely I won’t need it for this project, but better having it, than needing it, right?). I can not have more than one database configured.

# Configuration using a configuration service in NestJS 

So what I ended up doing was inspired [here](https://docs.nestjs.com/techniques/database#async-configuration). This describes an injectable service, that implements the `TypeOrmOptionsFactory` interface, that can be used to asynchronously configure TypeORM. I combined this with the `ConfigModule` of NestJS (and I guess, it would be possible to use other config options as well). The service that configures my mongodb looks like this:

```
@Injectable()
export class MongoDBConfigService implements TypeOrmOptionsFactory {

    constructor(private configService: ConfigService) {}

    createTypeOrmOptions(): TypeOrmModuleOptions {
        return {
            type: 'mongodb',
            url: this.configService.get<string>('MONGODB_URL'),
            username: this.configService.get<string>('MONGODB_USER'),
            password: this.configService.get<string>('MONGODB_PASSWORD'),
    };
  }
}
```

In this service I can add all the configuration parameters I need and I can just create one service per database.

# Conclusion

This was an unnecessary long journey. I think I could have avoided part of it, by just reading the next few instructions or doing a little more research.  On the other hand, I figured out a solution myself. From my point of view it works good and is really flexible, but I also don’t know much about NestJS and TypeORM so this is only a feeling I can’t prove.