feat(wip): initial boilerplate
This commit is contained in:
48
src/app.module.ts
Normal file
48
src/app.module.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import * as Joi from 'joi';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
|
||||
import configuration from './config/configuration';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
load: [configuration],
|
||||
validationSchema: Joi.object({
|
||||
NODE_ENV: Joi.string()
|
||||
.valid('development', 'production', 'test', 'provision')
|
||||
.default('development'),
|
||||
PORT: Joi.number().default(3000),
|
||||
DATABASE_CLIENT: Joi.valid('mysql', 'postgres'),
|
||||
DATABASE_HOST: Joi.string(),
|
||||
DATABASE_NAME: Joi.string(),
|
||||
DATABASE_USERNAME: Joi.string(),
|
||||
DATABASE_PASSWORD: Joi.string().empty('').default(''),
|
||||
DATABASE_PORT: Joi.number().default(5432),
|
||||
}),
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
return {
|
||||
type: configService.get<'postgres' | 'mysql'>('database.client'),
|
||||
host: configService.get<string>('database.host'),
|
||||
port: configService.get<number>('database.port'),
|
||||
username: configService.get<string>('database.username'),
|
||||
password: configService.get<string>('database.password'),
|
||||
database: configService.get<string>('database.name'),
|
||||
entities: [],
|
||||
synchronize: true,
|
||||
autoLoadEntities: true,
|
||||
logging: true,
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
};
|
||||
},
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
UsersModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
13
src/config/configuration.ts
Normal file
13
src/config/configuration.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default () => {
|
||||
return {
|
||||
port: parseInt(process.env.PORT, 10) || 3000,
|
||||
database: {
|
||||
client: process.env.DATABASE_CLIENT,
|
||||
host: process.env.DATABASE_HOST,
|
||||
port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
|
||||
username: process.env.DATABASE_USERNAME,
|
||||
password: process.env.DATABASE_PASSWORD || '',
|
||||
name: process.env.DATABASE_NAME,
|
||||
},
|
||||
};
|
||||
};
|
||||
24
src/main.ts
Normal file
24
src/main.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestFastifyApplication>(
|
||||
AppModule,
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.listen(3000);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
9
src/users/dto/create-user.dto.ts
Normal file
9
src/users/dto/create-user.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
}
|
||||
4
src/users/dto/update-user.dto.ts
Normal file
4
src/users/dto/update-user.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
export class UpdateUserDto extends PartialType(CreateUserDto) {}
|
||||
45
src/users/entities/user.entity.ts
Normal file
45
src/users/entities/user.entity.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
firstName: string;
|
||||
|
||||
@Column()
|
||||
lastName: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: true,
|
||||
})
|
||||
deletedAt: Date;
|
||||
|
||||
@VersionColumn()
|
||||
version: number;
|
||||
}
|
||||
20
src/users/users.controller.spec.ts
Normal file
20
src/users/users.controller.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersController', () => {
|
||||
let controller: UsersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
71
src/users/users.controller.ts
Normal file
71
src/users/users.controller.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Put,
|
||||
Param,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createUserDto: CreateUserDto) {
|
||||
return {
|
||||
data: await this.usersService.create(createUserDto),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
const [data, count] = await this.usersService.findAll();
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
data: await this.usersService.findOne(id),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() updateUserDto: UpdateUserDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.usersService.update(id, updateUserDto),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async remove(@Param('id', ParseUUIDPipe) id: string) {
|
||||
await this.usersService.remove(id);
|
||||
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
12
src/users/users.module.ts
Normal file
12
src/users/users.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { User } from './entities/user.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User])],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
18
src/users/users.service.spec.ts
Normal file
18
src/users/users.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
84
src/users/users.service.ts
Normal file
84
src/users/users.service.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { User } from './entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private usersRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
async create(createUserDto: CreateUserDto) {
|
||||
const result = await this.usersRepository.insert(createUserDto);
|
||||
|
||||
return this.usersRepository.findOneOrFail(result.identifiers[0].id);
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return this.usersRepository.findAndCount();
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
try {
|
||||
return await this.usersRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: string, updateUserDto: UpdateUserDto) {
|
||||
try {
|
||||
await this.usersRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.usersRepository.update(id, updateUserDto);
|
||||
|
||||
return this.usersRepository.findOneOrFail(id);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
try {
|
||||
await this.usersRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await this.usersRepository.delete(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user