Skip to content

feat(Feat/typeorm migration available): Implement TypeORM migration #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
DB_HOST =
DB_PORT =
DB_USER =
DB_PASSWORD =
DB_NAME =
PORT =
JWT_PRIVATE_KEY =
JWT_PUBLIC_KEY =
JWT_REFRESH_TOKEN_PRIVATE_KEY =
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,61 @@ Base NestJS, We like it
We use [Nestjs/TypeORM](https://docs.nestjs.com/techniques/database)
In this template, We've been trying not to use `Pure SQL` to make the most of TypeORM.

### Migration setup and usage

This project uses TypeORM's migration feature to manage database schema changes. Follow the steps below to generate and apply migrations.

> **Note**
>
> 1. The custom `typeorm` command defined in `package.json` is configured for `NODE_ENV=production`.
> 2. Migrations are intended for production use, while `typeorm synchronize` should be used for development purposes.
> 3. You can see the detailed configuration code [here](/src/common/config/ormconfig.ts)
> 4. As you can see from the configuration code, migration files must be located in the subdirectory of `/src/common/database/migrations/${name}`.

#### 1. Generate a migration file

To reflect new changes in the database, you need to first generate a migration file.

```bash
yarn migration:generate ./src/common/database/migrations/init
```

you can change the name of migration by replacing `init`

#### 2. Run the Migration

To apply the generated migration to the database, run the following command:

```bash
yarn migration:run
```

#### 3. Revert a Migration

To roll back the last applied migration, use the following command:

```bash
yarn migration:revert
```

#### 4. Check Migration Status

To view the current status of your migrations, run:

```bash
yarn migration:show
```

#### Create Migration Command

You can also directly create a migration file using the following `typeorm` command:

```bash
yarn migration:create ./src/common/database/migrations/init
```

This command generates an empty migration file where you can manually add your schema changes.

## [PostgresQL Database](https://www.postgresql.org/)

We use postgresQL for backend database, The default database taht will be used is named 'postgres'
Expand Down
1 change: 1 addition & 0 deletions additional.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
DB_HOST: string;
DB_PORT: string;
DB_USER: string;
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
"test:e2e": "rm -rf dist && jest --config ./test/jest-e2e.json",
"test:e2e:docker": "rm -rf dist && docker-compose -f docker-compose.e2e.yml --env-file ./.test.env up --exit-code-from app",
"g": "plop --plopfile ./generator/plopfile.mjs",
"prepare": "husky"
"prepare": "husky",
"typeorm": "export NODE_ENV=production&& ts-node ./node_modules/typeorm/cli.js -d ./src/common/config/ormconfig.ts",
"migration:create": "typeorm-ts-node-commonjs migration:create",
"migration:generate": "yarn typeorm migration:generate",
"migration:show": "yarn typeorm migration:show",
"migration:run": "yarn build && yarn typeorm migration:run",
"migration:revert": "yarn typeorm migration:revert"
},
"dependencies": {
"@apollo/server": "^4.11.3",
Expand Down
15 changes: 10 additions & 5 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';

import { AuthModule } from './auth/auth.module';
import { CustomCacheModule } from './cache/custom-cache.module';
import {
typeormConfigKey,
typeormConfigLoader,
} from './common/config/ormconfig';
import { getEnvPath } from './common/helper/env.helper';
import { envValidation } from './common/helper/env.validation';
import { SettingModule } from './common/shared/setting/setting.module';
Expand All @@ -19,6 +23,7 @@ import { UserModule } from './user/user.module';
ConfigModule.forRoot({
envFilePath: getEnvPath(`${__dirname}/..`),
validate: envValidation,
load: [typeormConfigLoader],
}),
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
Expand All @@ -28,10 +33,10 @@ import { UserModule } from './user/user.module';
settingService.graphqlUseFactory,
}),
TypeOrmModule.forRootAsync({
imports: [SettingModule],
inject: [SettingService],
useFactory: (settingService: SettingService) =>
settingService.typeOrmUseFactory,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) =>
configService.get(typeormConfigKey),
}),
UserModule,
AuthModule,
Expand Down
39 changes: 39 additions & 0 deletions src/common/config/ormconfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { registerAs } from '@nestjs/config';

import { config } from 'dotenv';
import { join } from 'path';
import { cwd, env } from 'process';
import { DataSource, DataSourceOptions } from 'typeorm';

import { getEnvPath } from '../helper/env.helper';

config({
path: getEnvPath(cwd()),
});

const typeormConfig: DataSourceOptions = {
type: 'postgres',
host: env.DB_HOST,
port: Number(env.DB_PORT),
username: env.DB_USER,
password: env.DB_PASSWORD,
database: env.DB_NAME,
entities:
env.NODE_ENV === 'test'
? [join(cwd(), 'src', '**', '*.entity.{ts,js}')]
: [join(cwd(), 'dist', '**', '*.entity.js')],
synchronize: env.NODE_ENV !== 'production',
dropSchema: env.NODE_ENV === 'test',
migrations: [
join(cwd(), 'dist', 'common', 'database', 'migrations', '*{.ts,.js}'),
],
migrationsRun: false,
logging: false,
};

export const typeormConfigKey = 'typeorm';
export const typeormConfigLoader = registerAs(
typeormConfigKey,
() => typeormConfig,
);
export default new DataSource(typeormConfig);
1 change: 1 addition & 0 deletions src/common/database/migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Migration files should be located here
22 changes: 0 additions & 22 deletions src/common/shared/setting/setting.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ApolloDriverConfig } from '@nestjs/apollo';
import { Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';

import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
import GraphQLJSON from 'graphql-type-json';
Expand Down Expand Up @@ -34,25 +33,4 @@ export class SettingService {
formatError,
};
}

get typeOrmUseFactory():
| TypeOrmModuleOptions
| Promise<TypeOrmModuleOptions> {
return {
type: 'postgres',
host: this.utilService.getString('DB_HOST'),
port: this.utilService.getNumber('DB_PORT'),
username: this.utilService.getString('DB_USER'),
password: this.utilService.getString('DB_PASSWORD'),
database: this.utilService.getString('DB_NAME'),
entities:
this.utilService.nodeEnv === 'test'
? [join(process.cwd(), 'src', '**', '*.entity.{ts,js}')]
: ['dist/**/*.entity.js'],
synchronize: this.utilService.nodeEnv !== 'production',
autoLoadEntities: true,
dropSchema: this.utilService.nodeEnv === 'test',
logging: false, // if you want to see the query log, change it to true
};
}
}
Loading