From 5c6fab0e173c9326c572a6c2412cbecfe56b7cef Mon Sep 17 00:00:00 2001 From: wlgns410 Date: Sun, 29 Jun 2025 16:23:33 +0900 Subject: [PATCH] FEATURE : README --- README.md | 496 +++++++++--------------------------------------------- 1 file changed, 75 insertions(+), 421 deletions(-) diff --git a/README.md b/README.md index 369ca3c..81cd6e4 100644 --- a/README.md +++ b/README.md @@ -1,455 +1,109 @@ -# NestJS/TypeORM/GraphQL/PostgresQL +# πŸ“‹ ResumeLog Backend API -NestJS boilerplate with TypeORM, GraphQL and PostgreSQL +## 🎯 ν”„λ‘œμ νŠΈ κ°œμš” -## Open for Contribution +**ResumeLog**λŠ” 개발자의 이λ ₯μ„œ μž‘μ„±μ„ μœ„ν•œ GraphQL 기반 λ°±μ—”λ“œ APIμž…λ‹ˆλ‹€. 일상적인 개발 ν™œλ™μ„ κΈ°λ‘ν•˜κ³ , 이λ₯Ό λ°”νƒ•μœΌλ‘œ λ§žμΆ€ν˜• 이λ ₯μ„œλ₯Ό 생성할 수 μžˆλŠ” ν”Œλž«νΌμž…λ‹ˆλ‹€. -Totally open for any Pull Request, please feel free to contribute in any ways. -There can be errors related with type or something. It would be very helpful to me for you to fix these errors. +### πŸ—οΈ 기술 μŠ€νƒ -## [NestJS](https://docs.nestjs.com/) +- **Framework**: NestJS 11.x +- **Database**: PostgreSQL with TypeORM +- **API**: GraphQL (Apollo Server) +- **Authentication**: JWT + Passport +- **Language**: TypeScript +- **Container**: Docker & Docker Compose -Base NestJS, We like it +### πŸš€ μ£Όμš” κΈ°λŠ₯ -## [TypeORM](https://typeorm.io/) +#### πŸ‘€ μ‚¬μš©μž 관리 -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. +- **νšŒμ›κ°€μž…/둜그인**: 이메일 인증 기반 νšŒμ›κ°€μž… +- **JWT 인증**: Access Token 기반 인증 μ‹œμŠ€ν…œ -### 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. +- **ν”„λ‘œν•„ 정보**: 이름, μ—°λ½μ²˜, GitHub, λΈ”λ‘œκ·Έ λ“± +- **ν•™λ ₯ 사항**: 학ꡐλͺ…, 전곡, ν•™μœ„, μž¬ν•™ κΈ°κ°„ +- **κ²½λ ₯ 사항**: νšŒμ‚¬λͺ…, 직무, 업무 μš”μ•½, 재직 κΈ°κ°„ +- **ν”„λ‘œμ νŠΈ**: ν”„λ‘œμ νŠΈλͺ…, μ„€λͺ…, 기술 μŠ€νƒ, μ—­ν•  +- **자격증**: 자격증λͺ…, λ°œκΈ‰κΈ°κ΄€, 취득일, 만료일 +- **ν™œλ™ λ‚΄μ—­**: ν™œλ™λͺ…, κΈ°κ΄€, κΈ°κ°„, μ„€λͺ… -> **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 +- **일일 개발 기둝**: νƒœκ·Έλ³„ 개발 ν™œλ™ 기둝 +- **ν”„λ‘œμ νŠΈ 연동**: νŠΉμ • ν”„λ‘œμ νŠΈμ™€ μ—°κ²°λœ 둜그 관리 +- **AI 기반 μΆ”μ²œ**: ChatGPTλ₯Ό ν™œμš©ν•œ νšŒμ‚¬λ³„, 직무별 이λ ₯μ„œ λ‚΄μš© μΆ”μ²œ +- **μ‚¬μš©λŸ‰ μ œν•œ**: 일일/μ›”κ°„ μ‚¬μš©λŸ‰ 관리 -To reflect new changes in the database, you need to first generate a migration file. +#### πŸ” λ³΄μ•ˆ 및 κΆŒν•œ -```bash -npm run migration:generate ./src/common/database/migrations/init -``` - -you can change the name of migration by replacing `init` +- **μ—­ν•  기반 μ ‘κ·Ό μ œμ–΄**: μ‚¬μš©μž/κ΄€λ¦¬μž κΆŒν•œ 뢄리 +- **ν•„λ“œλ³„ κΆŒν•œ 관리**: GraphQL ν•„λ“œ 레벨 λ³΄μ•ˆ +- **쿼리 μ΅œμ ν™”**: μžλ™ JOIN 및 SELECT μ΅œμ ν™” +- **νŠΈλžœμž­μ…˜ 관리**: 데이터 일관성 보μž₯ -#### 2. Run the Migration +### πŸ—„οΈ λ°μ΄ν„°λ² μ΄μŠ€ ꡬ쑰 -To apply the generated migration to the database, run the following command: - -```bash -npm run migration:run ``` - -#### 3. Revert a Migration - -To roll back the last applied migration, use the following command: - -```bash -npm run migration:revert +πŸ“ Schema +β”œβ”€β”€ πŸ‘€ UserProfile (ν”„λ‘œν•„) +β”œβ”€β”€ πŸŽ“ Education (ν•™λ ₯) +β”œβ”€β”€ πŸ’Ό Career (κ²½λ ₯) +β”œβ”€β”€ πŸš€ Project (ν”„λ‘œμ νŠΈ) +β”œβ”€β”€ πŸ† Certificate (자격증) +β”œβ”€β”€ πŸ“ Activity (ν™œλ™) +β”œβ”€β”€ πŸ“ DailyLog (일일 둜그) +β”œβ”€β”€ πŸ“Š DailyLogUsage (μ‚¬μš©λŸ‰) +└── πŸ“₯ ResumeDownload (λ‹€μš΄λ‘œλ“œ) ``` -#### 4. Check Migration Status - -To view the current status of your migrations, run: +### ⚑ API νŠΉμ§• -```bash -npm run migration:show -``` - -#### Create Migration Command +- **GraphQL Code First**: TypeScript μ½”λ“œλ‘œ μŠ€ν‚€λ§ˆ μžλ™ 생성 +- **동적 쿼리 μ΅œμ ν™”**: μš”μ²­λœ ν•„λ“œλ§Œ μ‘°νšŒν•˜μ—¬ μ„±λŠ₯ μ΅œμ ν™” +- **μ‹€μ‹œκ°„ μŠ€ν‚€λ§ˆ**: GraphQL Playgroundμ—μ„œ μ‹€μ‹œκ°„ API λ¬Έμ„œ 확인 +- **배치 처리**: λŒ€λŸ‰ 데이터 처리 지원 -You can also directly create a migration file using the following `typeorm` command: +### πŸ› οΈ 개발 ν™˜κ²½ μ„€μ • ```bash -npm run 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' -You have to have postgresql Database server before getting started. -You can use [Docker postgresQL](https://hub.docker.com/_/postgres) to have server easily - -## [GraphQL](https://graphql.org/) - -##### packages: graphql, apollo-server-express and @nestjs/graphql, [graphqlUpload](https://www.npmjs.com/package/graphql-upload) ... - -We use GraphQL in a Code First approach (our code will create the GraphQL Schemas). - -We don't use [swagger](https://docs.nestjs.com/openapi/introduction) now, but you can use this if you want to. -You can see [playground](http://localhost:8000/graphql) - -We use apollographql as playground. but if you want to use default playground, you can do like below. - -```js -// setting.service.ts - -GraphQLModule.forRootAsync < - ApolloDriverConfig > - { - ... - useFactory: (configService: ConfigService) => ({ - ... - playground: true, - ... - }), - ... - }; -``` - -### Protected queries/mutation by user role with guard - -Some of the GraphQL queries are protected by a NestJS Guard (`GraphqlPassportAuthGuard`) and requires you to be authenticated (and some also requires to have the Admin role). -You can solve them with Sending JWT token in `Http Header` with the `Authorization`. - -```json -# Http Header -{ - "Authorization": "Bearer TOKEN" -} -``` - -#### Example of some protected GraphQL - -- getMe (must be authenticated) -- All methods generated by the generator (must be authenticated and must be admin) - -### GraphQL Query To Select and relations - -#### Dynamic Query Optimization - -- Automatically maps GraphQL queries to optimized SELECT and JOIN clauses in TypeORM. - -- Ensures that only the requested fields and necessary relations are retrieved, reducing over-fetching and improving performance. - -- With using interceptor (name: `UseRepositoryInterceptor`) and paramDecorator (name: `GraphQLQueryToOption`) - -#### How to use - -- You can find example code in [/src/user/user.resolver.ts](/src/user/user.resolver.ts) - -### Permission for specific field - -The [permission guard](/src/common/decorators/query-guard.decorator.ts) is used to block access to specific fields in client requests. - -#### Why it was created - -- In GraphQL, clients can request any field, which could expose sensitive information. This guard ensures that sensitive fields are protected. +# μ˜μ‘΄μ„± μ„€μΉ˜ +npm install -- It allows controlling access to specific fields based on the server's permissions. +# 개발 μ„œλ²„ μ‹€ν–‰ +npm run start:dev -#### How to use +# Docker ν™˜κ²½ μ‹€ν–‰ +npm run docker:development:up -```ts -@Query(()=>Some) -@UseQueryPermissionGuard(Some, { something: true }) -async getManySomeList(){ - return this.someService.getMany() -} -``` - -With this API, if the client request includes the field "something," a `Forbidden` error will be triggered. - -#### Note - -There might be duplicate code when using this guard alongside `other interceptors`(name: `UseRepositoryInterceptor`) in this boilerplate. In such cases, you may need to adjust the code to ensure compatibility. - -## License - -MIT - -## Custom CRUD - -To make most of GraphQL's advantage, We created its own api, such as GetMany or GetOne. -We tried to make it as comfortable as possible, but if you find any mistakes or improvements, please point them out or promote them. - -You can see detail in folder [/src/common/graphql](/src/common/graphql) files - -```js -// query -query($input:GetManyInput) { - getManyPlaces(input:$input){ - data{ - id - logitude - count - } - } -} -``` - -```js -// variables -{ - input: { - pagination: { - size: 10, - page: 0, // Started from 0 - }, - order: { id: 'DESC' }, - dataType: 'data', //all or count or data - default: all - where: { - id: 3, - }, - }, -}; -``` - -You can see detail [here](./process-where.md). - -## Code generator - -There is [CRUD Generator in NestJS](https://docs.nestjs.com/recipes/crud-generator). -In this repository, It has its own generator with [plopjs](https://plopjs.com/documentation/). -You can use like below. - -```bash -$ npm run g -``` - -## Caching - -This project provides a custom decorator that makes it easy to implement method caching in NestJS applications. - -1. **Caching Functionality**: Utilizes `DiscoveryService` and `MetadataScanner` to handle method caching automatically at runtime. -2. **Usage**: Designed for use with any provider. -3. **GraphQL Resolvers**: Resolvers are also part of providers, but due to GraphQL's internal logic, method overrides do not work. Therefore, the functionality has been replaced with an interceptor. - -You can use like below - -```js -@Injectable() -export class ExampleService { - @Cache(...) - async exampleMethod(...args: unknown) { - ... - } -} -``` - -You can find related codes [here](./src/cache/custom-cache.module.ts) - -## Getting Started - -### Installation - -Before you start, make sure you have a recent version of [NodeJS](http://nodejs.org/) environment _>=14.0_ with NPM 6 or npm run. - -The first thing you will need is to install NestJS CLI. - -```bash -$ npm run -g @nestjs/cli -``` - -And do install the dependencies - -```bash -$ npm run install # or npm install -``` - -### Run - -for development - -```bash -$ npm run dev # or npm run dev -``` - -for production - -```bash -$ npm run build # or npm run build -$ npm run start # or npm run start -``` - -or run with docker following below - -## Docker - -### Docker-compose installation - -Download docker from [Official website](https://docs.docker.com/compose/install) - -### Before getting started - -Before running Docker, you need to create an env file named `.production.env`. -The content should be modified based on `.example.env`. -The crucial point is that DB_HOST must be set to 'postgres'. - -### Run - -Open terminal and navigate to project directory and run the following command. - -```bash -# Only for prduction -$ docker compose --env-file ./.production.env up -``` - -### Note - -If you want to use docker, you have to set DB_HOST in .production.env to be `postgres`. -The default set is `postgres` - -### Only database - -You can just create postgresql by below code, sync with .development.env - -```bash -$ docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1q2w3e4r -d postgres -``` - -## TDD - -### Introduction - -[`@nestjs/testing`](https://docs.nestjs.com/fundamentals/testing) = `supertest` + `jest` - -### Before getting started - -Before starting the test, you need to set at least jwt-related environment variables in an env file named `.test.env`. - -### Unit Test (Use mock) - -Unit test(with jest mock) for services & resolvers (\*.service.spec.ts & \*.resolver.spec.ts) - -#### Run - -```bash -$ npm run test:unit -``` - -### Integration Test (Use in-memory DB) - -Integration test(with [pg-mem](https://github.com/oguimbal/pg-mem)) for modules (\*.module.spec.ts) - -#### Run - -```bash -$ npm run test:integration -``` - -### End To End Test (Use docker) - -E2E Test(with docker container) - -#### Run - -```bash -$ npm run test:e2e:docker -``` - -## CI - -### Github actions - -To ensure github actions execution, please set the 'ENV' variable within your github actions secrets as your .test.env configuration. - -**Note:** Github Actions does not recognize newline characters. Therefore, you must remove any newline characters from each environment variable value in your `.env` file, ensuring that the entire content is on a single line when setting the Secret. If you need to use an environment variable value that includes newline characters, encode the value using Base64 and store it in the Github Secret, then decode it within the workflow. - -ex) - -```bash -JWT_PRIVATE_KEY= -----BEGIN RSA PRIVATE KEY-----...MIIEogIBAAKCAQBZ...-----END RSA PRIVATE KEY----- -``` - -### [Husky v9](https://github.com/typicode/husky) - -#### Before getting started - -```bash -$ npm run prepare -``` - -#### Pre commit - -[You can check detail here](./.husky/pre-commit) - -Before commit, The pre-commit hooks is executed. - -Lint checks have been automated to run before a commit is made. - -If you want to add test before commit actions, you can add follow line in [pre-commit](./.husky/pre-commit) file. - -```bash -... -npm run test -... -``` - -#### Pre push - -[You can check detail here](./.husky/pre-push) - -The pre-push hooks is executed before the push action. - -The default rule set in the pre-push hook is to prevent direct pushes to the main branch. - -If you want to enable this action, you should uncomment the lines in the pre push file. - -### [SWC Compiler](https://docs.nestjs.com/recipes/swc) - -[SWC](https://swc.rs/) (Speedy Web Compiler) is an extensible Rust-based platform that can be used for both compilation and bundling. Using SWC with Nest CLI is a great and simple way to significantly speed up your development process. - -#### SWC + Jest error resolution - -After applying `SWC`, the following error was displayed in jest using an in-memory database (`pg-mem`): - -```bash - QueryFailedError: ERROR: function obj_description(regclass,text) does not exist - HINT: πŸ”¨ Please note that pg-mem implements very few native functions. - - πŸ‘‰ You can specify the functions you would like to use via "db.public.registerFunction(...)" - - 🐜 This seems to be an execution error, which means that your request syntax seems okay, - but the resulting statement cannot be executed β†’ Probably not a pg-mem error. - - *️⃣ Failed SQL statement: SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ("table_schema" = 'public' AND "table_name" = 'user'); - - πŸ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and the stacktrace: +# λ°μ΄ν„°λ² μ΄μŠ€ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ +npm run migration:run ``` -`pg-mem` is a library designed to emulate `PostgreSQL`, however, it does not support all features, which is why the above error occurred. +### πŸ“ˆ μ„±λŠ₯ μ΅œμ ν™” -This error can be resolved by implementing or overriding existing functions. Below is the function implementation for the resolution. -Related issues can be checked [here](https://github.com/oguimbal/pg-mem/issues/380). - -```ts -db.public.registerFunction({ - name: 'obj_description', - args: [DataType.text, DataType.text], - returns: DataType.text, - implementation: () => 'test', -}); -``` +- **쿼리 μ΅œμ ν™”**: TypeORM Query Builder ν™œμš© +- **인덱싱**: λ°μ΄ν„°λ² μ΄μŠ€ 인덱슀 μ΅œμ ν™” +- **νŽ˜μ΄μ§€λ„€μ΄μ…˜**: λŒ€μš©λŸ‰ 데이터 효율적 처리 +- **LLM 토큰 μ΅œμ ν™”**: `project_daily_log_view`둜 ChatGPT μš”μ²­ 데이터 μ •λ¦¬ν•˜μ—¬ 토큰 μ‚¬μš©λŸ‰ μ ˆμ•½ -## Todo +### πŸ”„ CI/CD -- [x] TDD +- **μžλ™ν™” ν…ŒμŠ€νŠΈ**: Jest 기반 λ‹¨μœ„/톡합 ν…ŒμŠ€νŠΈ +- **μ½”λ“œ ν’ˆμ§ˆ**: ESLint, Prettier μ½”λ“œ μŠ€νƒ€μΌ 관리 +- **Git Hooks**: Huskyλ₯Ό ν†΅ν•œ 컀밋 μ „ 검증 +- **Docker 배포**: μ»¨ν…Œμ΄λ„ˆν™”λœ 배포 ν™˜κ²½ - - [x] Unit Test (Use mock) - - [x] Integration Test (Use in-memory DB) - - [x] End To End Test (Use docker) +### πŸ“š API λ¬Έμ„œ -- [x] CI +- **GraphQL Playground**: `http://localhost:3000/graphql` +- **μ‹€μ‹œκ°„ μŠ€ν‚€λ§ˆ**: μžλ™ μƒμ„±λ˜λŠ” GraphQL μŠ€ν‚€λ§ˆ +- **인증 헀더**: `Authorization: Bearer ` - - [x] Github actions - - [x] husky +### πŸ€” μ•„μ‰¬μš΄ 점 & κ°œμ„  λ°©ν–₯ -- [x] GraphQL Upload -- [x] Healthcheck -- [x] Divide usefactory -- [x] SWC Compiler -- [x] Refresh Token -- [ ] Redis -- [ ] ElasticSearch -- [x] Caching -- [ ] Graphql Subscription -- [x] Remove lodash -- [ ] [CASL](https://docs.nestjs.com/security/authorization#integrating-casl) +- **νšŒμ›νƒˆν‡΄ μ •μ±… λ―Έμ •**: Soft Delete vs Hard Delete μ •μ±… 수립 ν•„μš” +- **데이터 정리 둜직**: μ‚¬μš©μž μ‚­μ œ μ‹œ κ΄€λ ¨ 데이터 정리 μ „λž΅ 보완 ν•„μš” +- **GraphQL ν™œμš©λ„ λΆ€μ‘±**: REST API처럼 κ³ μ •λœ 쿼리 ꡬ쑰, 동적 필터링 κΈ°λŠ₯ 미흑 +- **GPT λΆ€ν•˜ ν…ŒμŠ€νŠΈ**: ChatGPT API 톡신 λΆ€λΆ„μ˜ λ™μ‹œ μš”μ²­ 처리 및 응닡 μ‹œκ°„ ν…ŒμŠ€νŠΈ λΆ€μž¬