Skip to content
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

feat(backend)!: move from typeorm to prisma as ORM provider. #1720

Merged
merged 12 commits into from
Dec 11, 2023
Merged
731 changes: 322 additions & 409 deletions backend/package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
"@nestjs/schematics": "^10.0.0",
"@nestjs/swagger": "^7.0.3",
"@nestjs/testing": "^10.0.0",
"@nestjs/typeorm": "^10.0.0",
"@prisma/client": "^5.7.0",
"dotenv": "^16.0.1",
"nestjs-prisma": "^0.22.0",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"rimraf": "^5.0.0",
"rxjs": "^7.8.0",
"swagger-ui-express": "^5.0.0",
"typeorm": "^0.3.17",
"winston": "^3.11.0",
"nest-winston": "^1.9.4",
"helmet": "^7.0.0"
Expand All @@ -62,6 +62,7 @@
"jest-sonar-reporter": "^2.0.0",
"lint-staged": "^15.0.2",
"prettier": "^3.0.3",
"prisma": "^5.7.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.1",
Expand Down
14 changes: 14 additions & 0 deletions backend/prisma/prisma-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
let prisma: PrismaClient;
import {PrismaClient} from '@prisma/client';


if (!prisma) {
prisma = new PrismaClient({
log: ['query', 'info', "error", "warn"],
errorFormat: 'pretty',
datasourceUrl: `postgresql://${DB_USER}:${DB_PWD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?schema=${DB_SCHEMA}&connection_limit=5`

});
}

export default prisma;
14 changes: 14 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model users {
id Decimal @id(map: "USER_PK") @default(dbgenerated("nextval('\"USER_SEQ\"'::regclass)")) @db.Decimal
name String @db.VarChar(200)
email String @db.VarChar(200)
}
55 changes: 34 additions & 21 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
import "dotenv/config";
import { MiddlewareConsumer, Module } from '@nestjs/common';
import {TypeOrmModule} from "@nestjs/typeorm";
import {ConfigModule} from "@nestjs/config";
import {AppController} from "./app.controller";
import {AppService} from "./app.service";
import {UsersModule} from "./users/users.module";
import { HTTPLoggerMiddleware } from './middleware/req.res.logger';
import { Logger, MiddlewareConsumer, Module } from "@nestjs/common";
import { HTTPLoggerMiddleware } from "./middleware/req.res.logger";
import { loggingMiddleware, PrismaModule } from "nestjs-prisma";
import { ConfigModule } from "@nestjs/config";
import { UsersModule } from "./users/users.module";
import { AppService } from "./app.service";
import { AppController } from "./app.controller";

const DB_HOST = process.env.POSTGRESQL_HOST || "localhost";
const DB_USER = process.env.POSTGRESQL_USER || "postgres";
const DB_PWD = encodeURIComponent(process.env.POSTGRESQL_PASSWORD || "default"); // this needs to be encoded, if the password contains special characters it will break connection string.
const DB_PORT = process.env.POSTGRESQL_PORT || 5432;
const DB_NAME = process.env.POSTGRESQL_DATABASE || "postgres";
const DB_SCHEMA = process.env.DB_SCHEMA || "users";

@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: "postgres",
host: process.env.POSTGRES_HOST || "127.0.0.1",
port: 5432,
database: process.env.POSTGRES_DATABASE || "postgres",
username: process.env.POSTGRES_USER || "postgres",
password: process.env.POSTGRES_PASSWORD || "postgres", // helps in UT and e2e testing
// entities: [User],
autoLoadEntities: true, // Auto load all entities regiestered by typeorm forFeature method.
schema: "users",
//logging: "all"
PrismaModule.forRoot({
isGlobal: true,
prismaServiceOptions:{
prismaOptions:{
log: ["query", "info", "error", "warn"],
errorFormat: "pretty",
datasourceUrl: `postgresql://${DB_USER}:${DB_PWD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?schema=${DB_SCHEMA}&connection_limit=5`,
},
middlewares: [
// configure your prisma middleware
loggingMiddleware({
logger: new Logger("PrismaMiddleware"),
logLevel: "log"
})
]
},
}),
UsersModule,
UsersModule
],
controllers: [AppController],
providers: [AppService],
providers: [AppService]
})
export class AppModule { // let's add a middleware on all routes
configure(consumer: MiddlewareConsumer) {
consumer.apply(HTTPLoggerMiddleware).forRoutes("*");
}}
}
}
28 changes: 0 additions & 28 deletions backend/src/users/entities/users.entity.ts

This file was deleted.

20 changes: 13 additions & 7 deletions backend/src/users/users.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {Test, TestingModule} from "@nestjs/testing";
import {getRepositoryToken} from "@nestjs/typeorm";
import {UsersController} from "./users.controller";
import {UsersService} from "./users.service";
import {Users} from "./entities/users.entity";
import * as request from 'supertest';
import {HttpException, INestApplication} from "@nestjs/common";
import {CreateUserDto} from "./dto/create-user.dto";
import {UpdateUserDto} from "./dto/update-user.dto";
import {UserDto} from "./dto/user.dto";
import { PrismaService } from "nestjs-prisma";

describe("UserController", () => {
let controller: UsersController;
Expand All @@ -20,7 +19,7 @@ describe("UserController", () => {
providers: [
UsersService,
{
provide: getRepositoryToken(Users),
provide: PrismaService,
useValue: {},
},
],
Expand Down Expand Up @@ -62,20 +61,27 @@ describe("UserController", () => {
});
describe('findAll', () => {
it('should return an array of users', async () => {
const result: Users[] = [];
const result = [];
result.push({id: 1, name: 'Alice', email: '[email protected]'});
jest.spyOn(usersService, 'findAll').mockResolvedValue(result);
expect(await controller.findAll()).toBe(result);
});
});
describe('findOne', () => {
it('should return a user object', async () => {
const result = new Users('john', 'John Doe');
result.id = 1;
const result: UserDto = { id: 1, name:"john", email:'[email protected]'};
jest.spyOn(usersService, 'findOne').mockResolvedValue(result);

expect(await controller.findOne('1')).toBe(result);
});
it('should throw error if user not found', async () => {
jest.spyOn(usersService, 'findOne').mockResolvedValue(undefined);
try {
await controller.findOne('1');
}catch (e) {
expect(e).toBeInstanceOf(HttpException);
expect(e.message).toBe('User not found.');
}
});
});
describe('update', () => {
it('should update and return a user object', async () => {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {ApiTags} from "@nestjs/swagger";
import {UsersService} from "./users.service";
import {CreateUserDto} from "./dto/create-user.dto";
import {UpdateUserDto} from "./dto/update-user.dto";
import { UserDto } from "./dto/user.dto";

@ApiTags("users")
@Controller({path: "users", version: "1"})
Expand All @@ -24,7 +25,7 @@ export class UsersController {
}

@Get()
findAll() {
findAll() : Promise<UserDto[]> {
return this.usersService.findAll();
}

Expand Down
8 changes: 3 additions & 5 deletions backend/src/users/users.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UsersService } from "./users.service";
import { UsersController } from "./users.controller";
import { Users } from "./entities/users.entity";

@Module({
imports: [TypeOrmModule.forFeature([Users])],
controllers: [UsersController],
providers: [UsersService],
providers: [UsersService]
})
export class UsersModule {}
export class UsersModule {
}
Loading
Loading