Magic way to use YAML config surprisingly easy (NestJS + RavenDB tutorial 1)

Share this post on:

Hello folks! Now that the holiday season is approaching, I’m aiming to create a tutorial series related to NestJS and RavenDB with topics such as configuration, repository pattern, indexes or entities, maybe even a bit of MVC with Handlebars as templating engine. I really enjoy working with this stack, it quickly became my favorite one out there. I particularly like how it’s organized, the pace that I manage to produce high quality code, security and integrations.

So this series of tutorials is addressed to whom wants to learn more about the NestJS and RavenDB stack (RavenNest). A developer background is needed to tackle all the concepts. All the source code can be found on my Github project page. The codebase that I used at the start of this tutorial series is located in the tutorial-initial branch.

NestJS app configuration

Today we are going to kick off with something more trivial but important for the application base in general. NestJS has built-in configuration management via the @nestjs/config npm package and it’s really easy to use. Moreover, it’s very easy to change the configuration source which is what we want to achieve in this tutorial. What I don’t like in the default implementation is that it uses .env files, specifically in our case:

RAVEN_SERVER=http://localhost:8080
RAVEN_DATABASE=raven-nest

As you can notice, settings in .env files are straightforward but frugal as well. My recommendation is to store them in YAML files because it’s more organized and it’s easier to have different data types like numbers, booleans or even arrays. The YAML file that we will use onwards looks like this:

environment: 'development'
names:
  - Will
  - Bob
  - Chris

db:
  raven:
    database: 'raven-nest'
    url: 'http://localhost:8080/'

Making YAML magic in NestJS

Ok, let’s start the implementation. First of all, we need to create the config.yml file in our project and add the content from above:

Secondly, we require to install the js-yaml npm package, which provides support for YAML file handling:

yarn add js-yaml

Once this is complete, we will need to add a new module inside src folder called app-config. This will be loaded by the main module of the app and it will load our new config file. After that, we can inject the built-in ConfigService to access the defined settings exactly like before.

The file src/app-config/configuration.ts would look like this:

import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';

const YAML_CONFIG_FILENAME = 'conf/config.yml';

export default () => {
  return yaml.load(
    readFileSync(join(YAML_CONFIG_FILENAME), 'utf8'),
  ) as Record<string, any>;
};

This is the code that loads the YAML configuration and returns the Javascript object representation of it. Next to it we have the index.ts module declaration which basically tells NestJS where to get this new configuration from. This is how it looks like:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      ignoreEnvFile: true,
      load: [configuration],
      isGlobal: true,
    })
  ],
  exports: [],
})
export class AppConfigModule { }

All we have to do now is to import it in our AppModule:

@Module({
  imports: [
    AppConfigModule,
  ],
  controllers: [AppController],
  providers: [AppService, RavendbService],
})
export class AppModule {}

Important: remove any other ConfigModule.forRoot calls inside the app.module.ts as now AppConfigModule takes care of it and it’s no longer necessary.

Testing it out

Magic magic, but who believes in magic if there is no proof? 😁 By the way, you will need to change the key names for the settings in a similar manner like below:

Allright, let’s proceed by creating an endpoint which will return us the values of the settings defined in our config.yml, environment and names respectively. Be wary that names is an array and we will return it as such. To achieve this, first we need to inject the ConfigService in the app.controller.ts constructor:

constructor(
    private readonly appService: AppService,
    private readonly ravenService: RavendbService,
    private readonly config: ConfigService) {}

Now the last thing to do is to add the actual endpoint:

@Get('config')
getConfig() {
  const environment = this.config.get<string>('environment');
  const names = this.config.get<string[]>('names');

  return { environment, names };
}

This basically gets the values from the settings and returns them to the client. Notice the type specification on the config.get method, which tells what kind of variable we are looking for. This is an array in the case of the names key.

You can now go to http://localhost:3000/app/config and check that the values from the settings are correctly returned to the user:

{
  "environment": "development",
  "names": [
    "Will",
    "Bob",
    "Chris"
  ]
}

Well there you go, this is a proof that magic works 🙂

Conclusion

Besides magic that works, we can conclude that configuring a NestJS app is easy and tweakable. I’m not sure if it works with a database though, it might require some async methods which might not be supported yet. But if anyone is interested, I can definitely have a look and write some article about that as well.

Thanks for reading, I hope you found this article useful and interesting. If you have any suggestions don’t hesitate to contact me. If you found my content useful please consider a small donation. Any support is greatly appreciated! Cheers  😉

Hi there 👋
It’s nice to meet you.

Sign up to receive useful content in your inbox, every month.

Spam is a waste of time, I prefer something creative! Read the privacy policy for more info.

Author: afivan

Enthusiast adventurer, software developer with a high sense of creativity, discipline and achievement. I like to travel, I like music and outdoor sports. Because I have a broken ligament, I prefer safer activities like running or biking. In a couple of years, my ambition is to become a good technical lead with entrepreneurial mindset. From a personal point of view, I’d like to establish my own family, so I’ll have lots of things to do, there’s never time to get bored 😂

View all posts by afivan >