Setup TypeORM

This commit is contained in:
Philipinho 2023-08-04 16:26:43 +01:00
parent 11018ea976
commit cebad0e0a5
23 changed files with 1018 additions and 70 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
.env
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

3
server/.gitignore vendored
View File

@ -1,3 +1,4 @@
.env
# compiled output
/dist
/node_modules
@ -32,4 +33,4 @@ lerna-debug.log*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/extensions.json

View File

@ -45,6 +45,30 @@ $ npm run start:dev
$ npm run start:prod
```
## Migrations
```bash
# This creates a new empty migration file named 'init'
$ npm run migration:create --name=init
# Generates 'init' migration file from existing entities to update the database schema
$ npm run migration:generate --name=init
# Runs all pending migrations to update the database schema
$ npm run migration:run
# Reverts the last executed migration
$ npm run migration:revert
# Reverts all migrations
$ npm run migration:revert
# Shows the list of executed and pending migrations
$ npm run migration:show
## Test
```bash

677
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,16 +17,30 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "typeorm-ts-node-commonjs -d ./src/database/typeorm.config.ts",
"migration:generate": "npm run typeorm migration:generate ./src/database/migrations/$npm_config_name",
"migration:create": "typeorm-ts-node-commonjs migration:create ./src/database/migrations/$npm_config_name",
"migration:run": "npm run typeorm migration:run",
"migration:revert": "npm run typeorm migration:revert",
"migration:revert:all": "while npm run migration:revert; do :; done",
"migration:show": "npm run typeorm migration:show"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/mapped-types": "^2.0.2",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-fastify": "^10.1.3",
"@nestjs/typeorm": "^10.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"fastify": "^4.21.0",
"pg": "^8.11.2",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"typeorm": "^0.3.17"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",

View File

@ -1,22 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});

View File

@ -1,9 +1,22 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CoreModule } from './core/core.module';
import { EnvironmentModule } from './environment/environment.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppDataSource } from './database/typeorm.config';
@Module({
imports: [],
imports: [
CoreModule,
EnvironmentModule,
TypeOrmModule.forRoot({
...AppDataSource.options,
entities: ['dist/src/**/*.entity.ts'],
migrations: ['dist/src/**/migrations/*.{ts,js}'],
autoLoadEntities: true,
}),
],
controllers: [AppController],
providers: [AppService],
})

View File

@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class CoreModule {}

View File

@ -0,0 +1 @@
export class CreateUserDto {}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}

View File

@ -0,0 +1,49 @@
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column({ unique: true })
email: string;
@Column({ nullable: true })
emailVerifiedAt: Date;
@Column()
password: string;
@Column({ nullable: true })
avatar_url: string;
@Column({ nullable: true })
locale: string;
@Column({ nullable: true })
timezone: string;
@Column({ type: 'jsonb', nullable: true })
settings: any;
@Column({ nullable: true })
lastLoginAt: Date;
@Column({ nullable: true })
lastLoginIp: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@ -0,0 +1,4 @@
import { Repository } from 'typeorm';
import { User } from '../entities/user.entity';
export class UserRepository extends Repository<User> {}

View File

@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';
describe('UserController', () => {
let controller: UserController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserController],
providers: [UserService],
}).compile();
controller = module.get<UserController>(UserController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@ -0,0 +1,37 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UserService],
}).compile();
service = module.get<UserService>(UserService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UserRepository } from './repositories/user.repository';
@Injectable()
export class UserService {
constructor(@InjectRepository(User) private userRepository: UserRepository) {}
create(createUserDto: CreateUserDto) {
return 'This action adds a new user';
}
findAll() {
return `This action returns all user`;
}
findOne(id: number) {
return `This action returns a #${id} user`;
}
update(id: number, updateUserDto: UpdateUserDto) {
return `This action updates a #${id} user`;
}
remove(id: number) {
return `This action removes a #${id} user`;
}
}

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateUserTable1691158956520 implements MigrationInterface {
name = 'CreateUserTable1691158956520';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "email" character varying NOT NULL, "emailVerifiedAt" TIMESTAMP, "password" character varying NOT NULL, "avatar_url" character varying, "locale" character varying, "timezone" character varying, "settings" jsonb, "lastLoginAt" TIMESTAMP, "lastLoginIp" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email"), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "users"`);
}
}

View File

@ -0,0 +1,14 @@
import { DataSource } from 'typeorm';
import * as dotenv from 'dotenv';
dotenv.config();
export const AppDataSource = new DataSource({
type: 'postgres',
url:
process.env.DATABASE_URL ||
'postgresql://postgres:password@localhost:5432/dc?schema=public',
entities: ['src/**/*.entity.ts'],
migrations: ['src/**/migrations/*.{ts,js}'],
subscribers: [],
synchronize: process.env.NODE_ENV === 'development',
logging: process.env.NODE_ENV === 'development',
});

View File

@ -0,0 +1,18 @@
import { Global, Module } from '@nestjs/common';
import { EnvironmentService } from './environment.service';
import { ConfigModule } from '@nestjs/config';
import { validate } from './environment.validation';
@Global()
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
expandVariables: true,
validate,
}),
],
providers: [EnvironmentService],
exports: [EnvironmentService],
})
export class EnvironmentModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EnvironmentService } from './environment.service';
describe('EnvironmentService', () => {
let service: EnvironmentService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EnvironmentService],
}).compile();
service = module.get<EnvironmentService>(EnvironmentService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class EnvironmentService {
constructor(private configService: ConfigService) {}
getEnv(): string {
return this.configService.get<string>('NODE_ENV');
}
getPort(): string {
return this.configService.get<string>('PORT');
}
getDatabaseURL(): string {
return this.configService.get<string>('DATABASE_URL');
}
getJwtSecret(): string {
return this.configService.get<string>('JWT_SECRET_KEY');
}
getJwtTokenExpiresIn(): string {
return this.configService.get<string>('JWT_TOKEN_EXPIRES_IN');
}
}

View File

@ -0,0 +1,20 @@
import { IsString, IsUrl, validateSync } from 'class-validator';
import { plainToClass } from 'class-transformer';
export class EnvironmentVariables {
@IsString()
NODE_ENV: string;
@IsUrl({ protocols: ['postgres', 'postgresql'], require_tld: false })
DATABASE_URL: string;
}
export function validate(config: Record<string, any>) {
const validatedConfig = plainToClass(EnvironmentVariables, config);
const errors = validateSync(validatedConfig);
if (errors.length > 0) {
throw new Error(errors.toString());
}
return validatedConfig;
}