How do I connect to Heroku Postgres from Nest.js? - node.js

When running heroku config I see (with some omissions)
DATABASE_URL: postgres://<xxxx>:<xxxx>#ec2-xx-xx-xxx-xx.compute-1.amazonaws.com:5432/dm74hmu71b97n
which I know is of form postgres://<user>:<password>#<hostname>:<port>/<database>. But right now in my Nest.JS app I connect to postgres like this:
#Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('POSTGRES_HOST'),
port: configService.get('POSTGRES_PORT'),
username: configService.get('POSTGRES_USER'),
password: configService.get('POSTGRES_PASSWORD'),
database: configService.get('POSTGRES_DB'),
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: false,
}),
}),
],
})
export class DatabaseModule {}
I suppose I could manually parse process.env.DATABASE_URL I figured there must be a better/easier way.

useFactory: (configService: ConfigService) => ({
url: configService.get('DATABASE_URL'),
type: 'postgres',
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: false,
}),

Related

No resolvers provided! nestjs-i18n won't workt properly

Package version: 10.2.1
Error:
[I18nService] No resolvers provided! nestjs-i18n won't workt properly, please follow the quick-start guide: https://nestjs-i18n.com/quick-start
Code:
#Module({
imports: [
ConfigModule.forRoot({ cache: true, isGlobal: true }),
I18nModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
fallbackLanguage: 'en',
loaderOptions: {
path: join(__dirname, 'i18n'),
watch: configService.get('NODE_ENV') === 'development',
},
logging: configService.get('NODE_ENV') === 'development', // REVIEW: Is this necessary?
resolvers: [AcceptLanguageResolver],
}),
}),
],
})
Revert to the old version.
Resolved!
#Module({
imports: [
ConfigModule.forRoot({ cache: true, isGlobal: true }),
I18nModule.forRootAsync({
inject: [ConfigService],
resolvers: [AcceptLanguageResolver],
useFactory: (configService: ConfigService) => ({
fallbackLanguage: 'en',
loaderOptions: {
path: join(__dirname, 'i18n'),
watch: configService.get('NODE_ENV') === 'development',
},
logging: configService.get('NODE_ENV') === 'development', // REVIEW: Is this necessary?
}),
}),
],
})

#InjectConnection not working for aTypeORM-SQL-Database, because its forRootAsync()

I am Registering my SQL-server in app.module.ts as follows:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mssql',
host: configService.get('HOST'),
port: 1434,
username: configService.get('USERNAME'),
database: 'testdatabase',
password: configService.get('PASSWORD'),
name: 'myDatabase',
entities: [],
}),
inject: [ConfigService],
}),
In some other service I am Injecting the Database connection as follows:
constructor(
#InjectConnection("myDatabase") private readonly connection: Connection,
) { }
ThisService is inside a Module which is Imported in app.module.ts
If I register the TypeOrm Module without the async (just TypeOrmModule.forRoot()), and don't use config.service I can access the connection, but since I wanna use ConficService it registers the SQL-Server asynchronously. The connection doesn't exist yet when I inject it throwing the error:
Nest can't resolve dependencies
of the UsersService (?). Please make sure that the argument myDatabaseConnection at index [0] is available in the UsersModule context.
I use the injected connection to run SQL-Queries on it.
How can I make this work?
If you use name in the #InjectConnection(), then name also needs to be at the same level as imports and useFactory as well as inside the options. There's two things keeping track of the name here, Nest, which needs it for the injection tokens, and TypeORM, which needs it for the metadata storage.
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mssql',
host: configService.get('HOST'),
port: 1434,
username: configService.get('USERNAME'),
database: 'testdatabase',
password: configService.get('PASSWORD'),
name: 'myDatabase',
entities: [],
}),
inject: [ConfigService],
name: 'myDatabase',
}),

NestJs: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string

i have a problem with connecting to database in nest.js with typeorm and postgres.
I created a .env file in the root project directory with the following content
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_DATABASE=db-name
In the app.module.ts I writed the code below:
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { TypeOrmModule } from '#nestjs/typeorm';
import { FeedModule } from './feed/feed.module';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: parseInt(<string>process.env.POSTGRES_PORT),
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
autoLoadEntities: true,
synchronize: true,
}),
FeedModule,
],
})
export class AppModule {}
But when im running the app by npm start it throws this error: new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')
What am I missing or doing wrong?
In NestJs you should use ConfigService to get environment variables inside your typeorm module, read the docs for more information.
You can use it like that:
import { ConfigModule, ConfigService } from '#nestjs/config';
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
#Module({
imports: [
ConfigModule.forRoot(
envFilePath: `.${process.env.NODE_ENV}.env`
),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
injects: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get("POSTGRES_HOST"),
port: configService.get("POSTGRES_PORT"),
username: configService.get("POSTGRES_USER"),
password: configService.get("POSTGRES_PASSWORD"),
database: configService.get("POSTGRES_DB"),
entities: [],
synchronize: true,
}),
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
As explained in the docs, you can define a factory function where you inject the config-service allowing you to resolve the corresponding values:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('POSTGRES_HOST'),
port: +configService.get<number>('POSTGRES_PORT'),
username: configService.get('POSTGRES_USER'),
password: configService.get('POSTGRES_PASSWORD'),
database: configService.get('POSTGRES_DATABASE'),
synchronize: true,
autoLoadEntities: true,
}),
inject: [ConfigService],
});
I was able to fix the problem by using the config module.
Just do npm i #nestjs/config. Then in the imports array just above the TypeOrmModule put ConfigModule.forRoot({ isGlobal: true }),. This allows your module to get the environment variables from the .env file
I got this error because I put the .env file inside the src by mistake. If you put it outside of the src it will fix it
I was facing the same issue and it was weird because I modified several times that configuration just to check if "something new happens" but have no success.
Long story short, I deleted the "dist" folder of the project and build the app again (npm run build) and it worked! It appeared that I had a "bad build" running over and over again so this workaround kind of "refreshed" the build and let things running well again.
Hope this help!

How to get values from custom configuration file in Nestjs with interface?

I'm trying to set up all of my configurations in one file in "config.ts", load it to ConfigService and then with config interfaces get values from it.
So here is my config.ts that have ENV vars from my .env file and static variables.
UPD: Made repo with this example
import { Config } from './config.interface';
const config: Config = {
typeorm: {
type: 'postgres',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: process.env.NODE_ENV !== 'prod',
logging: true,
entities: [User, RefreshToken],
},
};
export default () => config;
And here is my interfaces:
export interface Config {
typeorm: TypeOrmConfig;
}
export interface TypeOrmConfig {
type: string;
host: string;
port: number;
username: string;
password: string;
database: string;
synchronize: boolean;
logging: boolean;
entities: any[];
}
The config is loaded to ConfigModule in app.module.ts
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.dev.env',
load: [config],
}),
}),
For example I want to set up my TypeOrmModule with this setting.
Based on NestJs documentation
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
const config = configService.get<TypeOrmConfig>('typeorm');
console.log(config);
return {
type: config.type,
host: config.host,
port: +config.port,
username: config.username,
password: config.password,
database: config.database,
synchronize: config.synchronize,
logging: config.logging,
entities: config.entities,
};
},
inject: [ConfigService],
}),
Here is the problem. Static values from config is okay, but all my ENV vars is undefined. Here is console.log output:
{
type: 'postgres',
host: undefined,
port: NaN,
username: undefined,
password: undefined,
database: undefined,
synchronize: true,
logging: true,
entities: [
[class User extends CoreEntity],
[class RefreshToken extends CoreEntity]
]
}
I don't understand what's the problem with undefined ENV vars
I would be grateful for any explanations and help
In short, NestJS requires you to define a namespace for your custom configuration "typeorm" which you are trying to access here (see Configuration Namespace):
const config = configService.get<TypeOrmConfig>('typeorm');
This means that you must use registerAs function to create your namespace:
import { registerAs } from '#nestjs/config';
import { TypeOrmConfig } from './config.interface';
export default registerAs(
'typeorm',
(): TypeOrmConfig => ({
type: 'postgres',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: process.env.NODE_ENV !== 'prod',
logging: true,
entities: [],
}),
);
This will do the trick. However, we can improve upon this.
Instead of defining your own interface for the TypeORM configuration, there are existing interfaces you can use and / or extend from to reduce boilerplate code (below I mention ConnectionOptions though TypeOrmModuleOptions might be more appropriate depending on your configuration needs). I would also suggest providing fallback values to environmental variables:
import { registerAs } from '#nestjs/config';
import { ConnectionOptions } from 'typeorm';
const CONNECTION_TYPE = 'postgres';
export default registerAs(
'typeorm',
(): ConnectionOptions => ({
type: CONNECTION_TYPE,
host: process.env.DB_HOST || 'default_value',
port: +process.env.DB_PORT || 3000,
username: process.env.DB_USERNAME || 'default_value',
password: process.env.DB_PASSWORD || 'default_value',
database: process.env.DB_NAME || 'default_value',
synchronize: process.env.NODE_ENV !== 'prod',
logging: true,
entities: [],
}),
);
Also, there is no need to explicitly assign configuration values to TypeOrmModule again as you can simply:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) =>
await configService.get('typeorm'),
inject: [ConfigService],
}),
As a personal flavour I like to create configuration enum to contain configuration names to prevent misspelling. Consider this:
export enum ConfigEnum {
TYPEORM = 'typeorm',
}
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) =>
await configService.get<Promise<TypeOrmModuleOptions>>(
ConfigEnum.TYPEORM,
),
inject: [ConfigService],
})
Async / Await pattern is also redundant here, as we do not actually perform anything async, so this will work as well:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) =>
configService.get<TypeOrmModuleOptions>(ConfigEnum.TYPEORM),
inject: [ConfigService],
})
Welcome to StackOverflow!
Ps. I have made a pull request, see https://github.com/JustDenP/nest-config/pull/1

Reading .env for nestjs app with typeorm having custom provider

I am new to nestJS and I want to setup .env for existing application & facing issue.
I have custom provider for appModule as below,
#Module({
providers: [
AbcService,
XyzService,
],
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'xxxxxxxx',
port: 3230,
username: 'xyz',
password: 'password',
database: 'xyz-db',
entities: [__dirname + '/entities/**/*.entity{.ts,.js}'],
synchronize: true,
migrationsRun: true,
logging: true,
}),
TypeOrmModule.forFeature([
Transaction,
Payment,
]),
KafkaModule.forRoot(serviceConfig),
],
exports: [],
controllers: [ServiceSubscriptionController],
})
export class TopicModule { }
I have imported it inside AppModule as below,
#Module({
imports: [TopicModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
I want to keep these stuff inside .env and I tried it as per documentation as below,
TypeOrmModule.forRootAsync({
imports: [ConfigModule.forRoot({
envFilePath: '.env',
})],
useFactory: async (configService: ConfigService) => {
return {
host: configService.get('HOST'),
type: 'mysql',
port: 3230,
username: 'xyz',
password: 'password',
database: 'xyz-db',
entities: [__dirname + '/entities/**/*.entity{.ts,.js}'],
synchronize: true,
migrationsRun: true,
logging: true,
}
},
inject: [ConfigService]
}),
I have .env at root path with HOST key-value pair as below but it read undefined from it.
In package.json,
"start": "nest start",
"start:dev": "nest start --watch",
It seems that Nest's ConfigModule will run fs.readFileSync(envFilePath) if you pass a file path to the forRoot() method. If you want it to read from the root directory, either remove the envFilePath option, or set the full file path, from your user's home directory.
I have loaded config in main.ts manually as below.
import { config } from 'dotenv';
async function bootstrap() {
//factory method for normal TS app
await config();
const app = await NestFactory.create(AppModule);
Now I can access it as,
configService.get('HOST') // as provided in question
or as process.env.HOST
Note: I have to use forRootAsync instead of forRoot to access process.env

Resources