Is there a way to store my Typeorm data source configuration in a separate variable?

I am currently working with NestJS 10 and TypeORM 0.3.17. In my src/config/data-source.ts file, I have the following code snippet...

import * as dotenv from 'dotenv';
import * as dotenvExpand from 'dotenv-expand';
import { DataSource } from 'typeorm';

dotenvExpand.expand(dotenv.config());

export default new DataSource({
  type: 'postgres',
  host: process.env.DATABASE_HOST,
  port: Number(process.env.DATABASE_PORT) || 5432,
  database: process.env.DATABASE_NAME,
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  entities: ['dist/**/*.entity.js'],
  migrations: ['dist/database/migrations/*.js'],
  extra: {
    charset: 'utf8mb4_unicode_ci',
  },
 });

I am looking to refactor the configuration by separating the arguments into a variable for export in my src/config/typeorm.ts file, which currently contains duplicate code...

import { ConfigModule, ConfigService } from '@nestjs/config';
import {
  TypeOrmModuleAsyncOptions,
  TypeOrmModuleOptions,
} from '@nestjs/typeorm';

export const typeOrmAsyncConfig: TypeOrmModuleAsyncOptions = {
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: async (): Promise<TypeOrmModuleOptions> => {
    return {
      type: 'postgres',
      host: process.env.DATABASE_HOST,
      port: Number(process.env.DATABASE_PORT) || 5432,
      database: process.env.DATABASE_NAME,
      username: process.env.DATABASE_USERNAME,
      password: process.env.DATABASE_PASSWORD,
      synchronize: false,
      logging: true,
    };
  },
};

export const typeOrmConfig: TypeOrmModuleOptions = {
    type: 'postgres',
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT, 10),
    username: process.env.DB_USERNAME,
    database: process.env.DB_NAME,
    password: process.env.DB_PASSWORD,
    entities: ['dist/**/*.entity.js'],
    migrations: ['dist/migrations/**/*.js'],
    extra: {
      charset: 'utf8mb4_unicode_ci',
    },
    logging: true,
};

However, when I try to refactor my src/config/data-source.ts file in the following way...

export const dsConfig = {
    type: 'postgres',
    host: process.env.DATABASE_HOST,
    port: Number(process.env.DATABASE_PORT) || 5432,
    database: process.env.DATABASE_NAME,
    username: process.env.DATABASE_USERNAME,
    password: process.env.DATABASE_PASSWORD,
    entities: ['dist/**/*.entity.js'],
    migrations: ['dist/database/migrations/*.js'],
    extra: {
      charset: 'utf8mb4_unicode_ci',
    },
   };

export default new DataSource(dsConfig);

I encounter the error

Argument of type '{ type: string; host: string; port: number; database: string; username: string; password: string; entities: string[]; migrations: string[]; extra: { charset: string; }; }' is not assignable to parameter of type 'DataSourceOptions'.
  Type '{ type: string; host: string; port: number; database: string; username: string; password: string; entities: string[]; migrations: string[]; extra: { charset: string; }; }' is missing the following properties from type 'AuroraMysqlConnectionOptions': region, secretArn, resourceArn

While trying to export new DataSource(dsConfig). How can I correctly refactor my data source configuration file?

Answer №1

To access the configuration data source file in my repository, simply visit this link. It helps to separate the configuration data in the app.module.ts file:

    TypeOrmModule.forRootAsync(typeOrmAsyncConfig),

Your configuration file should look something like this:

const config: ConfigService = new ConfigService();
const dbConfig = {
type: 'postgres' as const,
  host: config.get<string>('DATABASE_HOST'),
  port: config.get<number>('DATABASE_PORT'),
  database: config.get<string>('DATABASE_NAME'),
  username: config.get<string>('DATABASE_USER'),
  password: config.get<string>('DATABASE_PASS'),
  entities: ['dist/**/*.entity.{ts,js}'],
  migrations: ['dist/migrations/*.{ts,js}'],
  migrationsRun: true,
  migrationsTableName: 'typeorm_migrations',
  synchronize: false,
  ssl: config.get('SSL_MODE', false),
  cli: {
    migrationsDir: 'src/migrations',
  },
  logging: true,
}

export const typeOrmAsyncConfig: TypeOrmModuleAsyncOptions = {
  useFactory: async (): Promise<TypeOrmModuleOptions> => {
    return dbConfig;
  },
};
export default new DataSource(dbConfig);

In your package.json file, ensure the following configurations are set in place:

    "typeorm": "ts-node ./node_modules/typeorm/cli.js -d src/utils/config/database/config.service.ts",
    "migration:generate": "npm run build && npm run typeorm -- migration:generate ./src/migrations/$npm_config_name",
    "migration:run": "npm run typeorm -- migration:run",
    "migration:create": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli migration:create ./src/migrations/$npm_config_name",
    "migration:revert": "npm run typeorm -- migration:revert"

Once you have set up the configurations, use the following commands:

npm run migration:generate --name=<your_migration_name> //auto generate migration with entities
npm run migration:create --name=<your_migration_name> //create empty migration file
npm run migration:revert    //revert your last migration
npm run migration:run // in case you set `migrationsRun: false` in your config and run it manually

Answer №2

In my approach to working with TypeORM and NestJS, I have come up with a straightforward strategy. I utilize named exports for my DataSource instance, create a DatabaseService that implements TypeormOptionsFactory from @nestjs/typeorm, and then return the dataSource options within the createTypeormOptions method.

@Injectable()
export class DatabaseService implements TypeOrmOptionsFactory {
    public createTypeOrmOptions(): TypeOrmModuleOptions {
        return dataSource.options;
    }
}

Following this, I set up a DatabaseModule, configure TypeOrmModule with .forRootAsync, and utilize dataSourceFactory to initialize the DataSource instance.

@Module({
    imports: [
        TypeOrmModule.forRootAsync({
            useClass: DatabaseService,
            dataSourceFactory: async (
                options?: DataSourceOptions,
            ): Promise<DataSource> => {
                if (!options) {
                    throw new Error('No DataSource options provided');
                }

                return dataSource.initialize();
            },
        }),
    ],
})
export class DatabaseModule {}

If you're interested in seeing how this setup works, you can check out my github repository.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can TypeScript support passing named rest arguments within the type declaration?

Using Tuple types in TypeScript enables us to create typesafe rest arguments: type Params = [string,number,string] const fn = (...args: Params) => null // Type is (args_0: string, args_1: number, args_2: string) => null Is there a method to assign ...

Transpilation appears to dissect a function

I've encountered a strange issue where the transpiler is splitting a single method into two parts: Before transpiling: MemberSchema.methods.addAccount = (account: IAccount): void => { this.account = new Account({ ...account, i ...

Injecting services with an abstract class is a common practice in Angular library modules

In my development workflow, I have established an Angular Component Library that I deploy using NPM (via Nexus) to various similar projects. This library includes a PageComponent, which contains a FooterComponent and a NavbarComponent. Within the NavbarCom ...

"Creating a sleek and efficient AI chess game using chess.js with Angular's

Cannot read property 'moves' of undefine Hello there! I am currently working on developing a chess game using Angular. I'm facing an issue with the artificial intelligence in the game where the piece seems to get stuck in the mouse. The l ...

What could be causing the malfunction of my button's event handlers?

Users can fill out a form in which they provide information about a grocery item they want to add to their cart. After completing the form, they can click on the "Add Item" button. Here's the form in my app.component.html: <form (ngSubmit)="a ...

Setting up React Context API with TypeScript: A Step-by-Step Guide

I recently updated my app.js file to app.tsx for improved functionality. However, I encountered an error with the current value. app.tsx import React, { createContext, useState } from "react"; import SelectPage from "./pages/select/select& ...

Signing in to a Discord.js account from a React application with Typescript

import React from 'react'; import User from './components/User'; import Discord, { Message } from 'discord.js' import background from './images/background.png'; import './App.css'; const App = () => { ...

Add the list of information during the looping process (map)

I am currently facing a challenge where I need to update my data during the iteration and send the results to an API call. The API Call expects a request with data structured in the following format: { list: [{ id: "1", name: "Hello" ...

Issue encountered when transitioning from Angular 8 to Angular 9: Value discrepancy at index 0

While updating my Angular project from version 8 to 9, I encountered an issue after following the update guide on update.angular.io and running npm update. When attempting to compile the website using ng serve, the following error occurred: ERROR in Faile ...

Combining individual TypeScript files together

Recently, I encountered an issue with 2 typescript files: a.ts: let some : string = "some"; b.ts: console.log(some); Surprisingly, when compiling both files together by including them in the tsconfig or command line, there was no error about 'som ...

Unlocking the power of module augmentation in Typescript: Enhancing library models within your app domain

I currently work with two applications that share the same code base for their models. I am interested in developing and sharing a library model using inheritance in TypeScript. For instance, Pet extends Author. In my current Angular application, I need ...

Preserve the checkbox state upon refreshing the page

I am facing an issue with keeping the checkbox state saved after a page reload. Currently, I have stored my unchecked checkboxes in localStorage, but I am unsure about what steps to take next. In simple terms, I want the checkbox to remain unchecked when I ...

When invoking an external API within a Firebase Cloud Function, it may result in a

I've been attempting to use a basic API for currency conversion within a Firebase Cloud Function written in Typescript, but I keep getting a 'null' response. import { https } from 'firebase-functions'; import * as axios from ' ...

Angular 4 Filtering Pipe: Simplify Data Filtering in Angular

Attempting to replicate AngularJS's OrderBy feature. Working with an array like this, I am aiming to filter the cars by their car category. [ { "car_category": 3, "name": "Fusion", "year": "2010" }, { "car_category": 2, "na ...

Angular2: Error - trying to access 'this.' which is not defined

I have a function that is designed to retrieve and display the "best player" from an array of objects, which essentially refers to the player with the most likes. The functionality of this function works as intended and displays the desired output. However ...

The implementation of the "setValue" function from react-hook-form resulted in the generation of over 358,000 TypeScript diagnostics for various types

In my experience, I have frequently used react-hook-forms and `setValue` in various parts of my application without encountering any issues. However, I recently came across a problem while compiling in a newly created branch based on the main branch. Desp ...

Encountering difficulties importing an NPM library into StackBlitz

Hey there, I'm currently attempting to replicate an Angular example online but am encountering issues importing my Tabulator Library in stackblitz. I keep receiving an error when trying to import it in the hello component. import Tabulator from &apo ...

Using TypeScript with Watermelondb

I'm currently developing a React App and I want to implement Watermelondb for Offline Storage, but I'm unsure about using it with TypeScript. I have already set up the database and created Course and Lesson model files from the Watermelondb libra ...

"Encountering issues with importing Splitpanes while using VueJs and TypeScript combination

My VueJS view was originally written in JavaScript using the component "splitpanes" from npm package. The code was functioning well with the following structure: <template> <div> <Splitpanes :dbl-click-splitter="false" :horizont ...

Array of generic types in Typescript

Here's a method that I have: getFiveObjectsFromArray(array: T[]) { return array.slice(0, 5); } I've been using this method multiple times. Is there a way in TypeScript to pass a generic argument instead of using multiple types? Also, when ...