Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d841b46
chore: 개발 도구 설정
labyrinth30 May 26, 2026
8812b4c
chore: 환경변수와 데이터베이스 설정
labyrinth30 May 26, 2026
827223f
feat: 헬스체크와 API 문서 설정
labyrinth30 May 26, 2026
f679a6c
docs: 셋업 가이드 작성
labyrinth30 May 26, 2026
bd9a4f8
chore: 환경변수 검증과 셋업 문서 정리
labyrinth30 May 26, 2026
ce90973
chore: GitHub Actions CI 파이프라인 구성
labyrinth30 May 26, 2026
c32753d
ci: PR 트리거 브랜치 필터 제거
labyrinth30 May 26, 2026
2ba5527
chore: useImportType 규칙 비활성화
labyrinth30 May 26, 2026
d8dcee8
chore: biome-ignore 주석 제거
labyrinth30 May 26, 2026
ff0c219
chore: ignore env example variants
labyrinth30 May 26, 2026
597feaa
chore: PostgreSQL 버전 16 -> 18로 업그레이드
labyrinth30 May 28, 2026
bfc0636
feat: @nestjs/terminus 기반 DB 헬스체크 추가
labyrinth30 May 28, 2026
b89cea1
refactor: 코드 리뷰 반영
labyrinth30 May 29, 2026
7644102
chore: TypeScript 6.0 업그레이드 및 tsgo 도입
labyrinth30 May 29, 2026
0cdec55
chore: vitest → bun test 전환
labyrinth30 May 29, 2026
48ba064
chore: .agents, .claude, skills-lock.json gitignore 추가
labyrinth30 May 29, 2026
780ae78
chore: 코드래빗 리뷰 반영
labyrinth30 May 29, 2026
b372322
Merge pull request #5 from mash-up-kr/chore/3-ci-pipeline
labyrinth30 Jun 6, 2026
eebc98e
chore: main 브랜치 머지 및 Hono 어댑터 기반 통합
labyrinth30 Jun 6, 2026
c5671d2
chore: biome 린트 에러 수정
labyrinth30 Jun 6, 2026
3bd3c53
fix: e2e 테스트 CI 환경 DATABASE_URL 누락 수정
labyrinth30 Jun 6, 2026
f3552ad
fix: e2e 테스트 CI 환경 DATABASE_URL 누락 수정
labyrinth30 Jun 6, 2026
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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://postgres:postgres@localhost:5432/team_mino
DB_POOL_SIZE=10

@minsour minsour May 29, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디비 풀 기본값은 Supabase 환경 고려해서 수정이 필요할 수도 있는거죠?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supabase PgBouncer 사용한다면 풀은 1~2로 줄일게요!


POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=team_mino
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
pull_request:
push:
branches: [main]

jobs:
ci:
name: Lint & Test
runs-on: ubuntu-latest
Comment on lines +3 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

중복 실행 방지를 위한 concurrency 설정을 추가하세요.

Line 3-11 기준으로 PR 업데이트 시 이전 실행을 취소하지 않아 CI 자원 낭비와 결과 지연이 발생할 수 있습니다.

🔧 제안 수정
 on:
   pull_request:
   push:
     branches: [main]
 
+concurrency:
+  group: ci-${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   ci:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
pull_request:
push:
branches: [main]
jobs:
ci:
name: Lint & Test
runs-on: ubuntu-latest
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
ci:
name: Lint & Test
runs-on: ubuntu-latest
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 3-6: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 3 - 11, The workflow lacks a
concurrency policy so multiple runs for the same PR can run concurrently; add a
top-level concurrency block to the workflow (near the existing on: section) that
defines a stable group (e.g., using github.ref or github.workflow and
github.head_ref) and sets cancel-in-progress: true to automatically cancel
previous runs when a new PR update triggers the same job (apply this to the CI
workflow containing jobs.ci / name: Lint & Test).

Source: Linters/SAST tools


steps:
Comment on lines +1 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

permissions 미지정으로 GITHUB_TOKEN 권한이 과도할 수 있습니다.

Line 1-13 기준으로 워크플로우에 permissions: 블록이 없어 기본 권한 정책에 의존합니다. 최소권한 원칙에 맞게 명시적으로 축소하세요.

🔧 제안 수정
 name: CI
 
 on:
   pull_request:
   push:
     branches: [main]
 
+permissions:
+  contents: read
+
 jobs:
   ci:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
ci:
name: Lint & Test
runs-on: ubuntu-latest
steps:
name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
ci:
name: Lint & Test
runs-on: ubuntu-latest
steps:
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 1-28: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)


[warning] 3-6: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1 - 13, 워크플로우가 기본 GITHUB_TOKEN 권한을
사용하도록 leaving permissions unspecified — 위험하므로 top-level에 명시적인 permissions: 블록을
추가해 최소 권한 원칙을 적용하세요; 예를 들어 아래에 있는 "name: CI" / job "ci" 워크플로우에 대해 최소 필수 권한만
허용하도록 permissions: contents: read 및 필요시 actions: read, packages: read 등으로 축소하고
불필요한 write 권한(예: contents: write, workflows: write)을 제거하세요.

Source: Linters/SAST tools

- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
Comment on lines +14 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 워크플로우에서 태그 기반 uses, 권한/토큰 설정 누락을 점검합니다.
rg -n '^\s*uses:\s*[^@]+@v[0-9]+' .github/workflows
rg -n 'persist-credentials|^\s*permissions:' .github/workflows/ci.yml

Repository: mash-up-kr/Team-MINO-Node

Length of output: 284


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== ci.yml lines 1-60 ==="
nl -ba .github/workflows/ci.yml | sed -n '1,80p'

echo "=== persist-credentials occurrences ==="
rg -n 'persist-credentials' .github/workflows || true

echo "=== permissions occurrences (ci.yml) ==="
rg -n '^\s*permissions:' .github/workflows/ci.yml || true

Repository: mash-up-kr/Team-MINO-Node

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== ci.yml lines 1-60 ==="
nl -ba .github/workflows/ci.yml | sed -n '1,80p'

echo "=== persist-credentials occurrences ==="
rg -n 'persist-credentials' .github/workflows || true

echo "=== permissions occurrences (ci.yml) ==="
rg -n '^\s*permissions:' .github/workflows/ci.yml || true

Repository: mash-up-kr/Team-MINO-Node

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== workflow files ==="
find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print | sort

echo "=== ci.yml (raw, first 120 lines) ==="
if [ -f .github/workflows/ci.yml ]; then
  sed -n '1,120p' .github/workflows/ci.yml
else
  echo "Missing: .github/workflows/ci.yml"
fi

echo "=== persist-credentials occurrences ==="
rg -n 'persist-credentials' .github/workflows || true

echo "=== permissions occurrences in ci.yml ==="
if [ -f .github/workflows/ci.yml ]; then
  rg -n '^\s*permissions:' .github/workflows/ci.yml || true
fi

echo "=== uses@v in ci.yml ==="
if [ -f .github/workflows/ci.yml ]; then
  rg -n '^\s*uses:\s*[^@]+@v[0-9]+' .github/workflows/ci.yml || true
fi

Repository: mash-up-kr/Team-MINO-Node

Length of output: 777


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== workflow files ==="
find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print | sort

echo "=== ci.yml (raw, first 120 lines) ==="
if [ -f .github/workflows/ci.yml ]; then
  sed -n '1,120p' .github/workflows/ci.yml
else
  echo "Missing: .github/workflows/ci.yml"
fi

echo "=== persist-credentials occurrences ==="
rg -n 'persist-credentials' .github/workflows || true

echo "=== permissions occurrences in ci.yml ==="
if [ -f .github/workflows/ci.yml ]; then
  rg -n '^\s*permissions:' .github/workflows/ci.yml || true
fi

echo "=== uses@v in ci.yml ==="
if [ -f .github/workflows/ci.yml ]; then
  rg -n '^\s*uses:\s*[^@]+@v[0-9]+' .github/workflows/ci.yml || true
fi

Repository: mash-up-kr/Team-MINO-Node

Length of output: 777


🌐 Web query:

GitHub actions/checkout v4 persist-credentials default value token persisted git config

💡 Result:

In GitHub Actions actions/checkout@v4, the persist-credentials input has a default value of true [1][2][3]. When persist-credentials is set to true (the default), the authentication token or SSH key used for the checkout is automatically persisted in the local git configuration of the repository [3][4]. This functionality allows subsequent steps in your workflow to execute authenticated git commands (such as git push or git pull) without needing to manually configure credentials [3][4]. To maintain security, the action automatically removes this token during the post-job cleanup phase [3][4]. If you wish to opt out of this behavior—for example, if you do not want the token to be available to other steps in your job—you can explicitly set persist-credentials: false in your workflow configuration [3][4].

Citations:


CI 워크플로우에서 actions/checkout/setup-bun 액션을 SHA로 고정하고 persist-credentials: false로 토큰 영속화를 막으세요.

File: .github/workflows/ci.yml
Lines: 14-18

      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: "1.3.14"
  • 현재 @v4/@v2 태그 기반이라 공급망 리스크가 남습니다(풀 SHA로 핀ning 필요).
  • actions/checkout의 persist-credentials가 지정돼 있지 않아 기본값(true)으로 체크아웃에 사용된 토큰/SSH 키가 작업 중 로컬 git 설정에 영속됩니다. (따라서 persist-credentials: false로 명시 비활성화 필요)
🔧 제안 수정
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@<FULL_SHA_FOR_V4>
+        with:
+          persist-credentials: false
 
-      - uses: oven-sh/setup-bun@v2
+      - uses: oven-sh/setup-bun@<FULL_SHA_FOR_V2>
         with:
           bun-version: "1.3.14"
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 14-14: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 14-14: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 14 - 18, Update the CI workflow to pin
the GitHub Actions to immutable SHAs and disable credential persistence: replace
the tag-based references for actions/checkout and oven-sh/setup-bun (the current
uses: entries for actions/checkout@v4 and oven-sh/setup-bun@v2) with their
corresponding full commit SHAs, and add persist-credentials: false under the
actions/checkout step to prevent token/SSH key persistence; keep the bun-version
input for setup-bun unchanged while switching its uses to the SHA-pinned
reference.

Source: Linters/SAST tools


- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check (lint + format + imports)
run: bun run check

- name: Test
run: bun run test
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
bin/
dist/
node_modules/
.agents/
.claude/
skills-lock.json
.env
.env.*
!.env.example
!.env.*.example
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
Expand Down
102 changes: 102 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Team MINO Node

NestJS 기반 Team MINO 백엔드 API 서버입니다.

## Requirements

- Bun 1.3.x
- Docker

## Setup Guide

### 1. Install Dependencies

```bash
bun install
```

### 2. Configure Environment

```bash
cp .env.example .env
```

기본 로컬 DB 연결 정보는 아래 값입니다.

```env
DATABASE_URL=postgres://postgres:postgres@localhost:5432/team_mino
```

### 3. Start Local Database

```bash
docker compose up -d postgres
```

컨테이너 상태를 확인합니다.

```bash
docker compose ps
```

### 4. Start Development Server

```bash
bun run start:dev
```

서버 기본 포트는 `3000`입니다.

```text
http://localhost:3000
```

### 5. Check Health API

```bash
curl http://localhost:3000/health
```

정상 응답은 아래와 같습니다.

```json
{
"status": "ok"
}
```

## Available Scripts

```bash
bun run start:dev
bun run build
bun run check
bun run lint
bun run format
bun run test
```

## API Docs

개발 서버 실행 후 Swagger 문서는 아래 경로에서 확인할 수 있습니다.

```text
http://localhost:3000/api-docs
```

## Database

로컬 PostgreSQL은 Docker Compose로 실행합니다.

```bash
docker compose up -d postgres
docker compose down
```

Drizzle 설정은 `drizzle.config.ts`를 사용합니다.

```bash
bun run db:generate
bun run db:migrate
bun run db:push
```
27 changes: 27 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
"files": {
"includes": ["**", "!dist", "!node_modules"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"useImportType": "off"
}
}
}
}
259 changes: 254 additions & 5 deletions bun.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
postgres:
image: postgres:18-alpine
container_name: team-mino-postgres
restart: unless-stopped
ports:
- "${POSTGRES_PORT:-5432}:5432"
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-team_mino}
volumes:
- postgres_data:/var/lib/postgresql/data
Comment on lines +12 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

postgres 18-alpine docker image does mounting volume at /var/lib/postgresql/data still persist data via compatibility symlink

💡 Result:

No, mounting a volume at /var/lib/postgresql/data does not persist data in the way it did in previous versions for PostgreSQL 18 and newer [1][2]. In PostgreSQL 18+, the official Docker image structure has changed significantly [1][2]: 1. Changed Data Location: The default data directory (PGDATA) is now located at /var/lib/postgresql/18/docker [3][2]. 2. Volume Mount Point: The VOLUME directive in the Dockerfile has been moved from /var/lib/postgresql/data to /var/lib/postgresql [2]. 3. No Compatibility Symlink: While there was an initial attempt to include a compatibility symlink, it was explicitly removed in the Alpine images (and others) to avoid issues [4]. If you attempt to mount a volume at /var/lib/postgresql/data, it will generally be ignored or result in an unused mount point, because the container expects the data to reside within the /var/lib/postgresql directory structure [1][3]. Recommended Configuration: To ensure data persists correctly in PostgreSQL 18+, you should mount your volume at the parent directory: volumes: - my_db_data:/var/lib/postgresql This configuration allows PostgreSQL to place its data in the correct version-specific subdirectory (e.g., /var/lib/postgresql/18/docker), which is essential for proper functionality and future upgrades using tools like pg_upgrade [1][3]. If you persist in using the old mount path, you may encounter silent data loss where the container initializes a new database in an anonymous volume while ignoring your existing data on the host [3].

Citations:


PostgreSQL 18용 볼륨 마운트 경로를 /var/lib/postgresql로 변경 권장

현재 volumes가 postgres_data:/var/lib/postgresql/data로 되어 있는데, Postgres 18+ 공식 이미지의 데이터 디렉터리/VOLUME 기준이 /var/lib/postgresql로 변경되었습니다. 따라서 올드 경로(/var/lib/postgresql/data)를 유지하면 업그레이드/영속성에서 문제가 생길 수 있어, 마운트 지점을 권장 경로로 맞추는 게 안전합니다.

권장 예시
   volumes:
-    - postgres_data:/var/lib/postgresql/data
+    - postgres_data:/var/lib/postgresql

이전 리뷰에서 언급된 대로 compose를(KKardy가) 별도로 정비할 때 함께 반영해 주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
- postgres_data:/var/lib/postgresql
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 12 - 13, Update the docker-compose volume
mount for the Postgres service: replace the current mapping that uses
postgres_data:/var/lib/postgresql/data with the Postgres 18+ recommended mount
target postgres_data:/var/lib/postgresql so the service (the volumes entry
referencing postgres_data and the current /var/lib/postgresql/data path) uses
the new VOLUME path and avoids persistence/upgrade issues with Postgres 18+.

healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5

volumes:
postgres_data:
16 changes: 16 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "drizzle-kit";

const databaseUrl = process.env.DATABASE_URL;

if (!databaseUrl) {
throw new Error("DATABASE_URL is required");
}
Comment on lines +3 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

DATABASE_URL 형식 검증도 같이 해두는 게 안전합니다.

빈 값만 막고 있어서 오타 URL이 들어오면 이후 단계에서 에러가 납니다. 여기서 즉시 검증하면 원인 파악이 쉬워집니다.

수정 예시
 const databaseUrl = process.env.DATABASE_URL;
 
 if (!databaseUrl) {
   throw new Error("DATABASE_URL is required");
 }
+
+try {
+  new URL(databaseUrl);
+} catch {
+  throw new Error("DATABASE_URL must be a valid URL");
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is required");
}
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is required");
}
try {
new URL(databaseUrl);
} catch {
throw new Error("DATABASE_URL must be a valid URL");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@drizzle.config.ts` around lines 3 - 7, 현재는 process.env.DATABASE_URL을 빈값만 체크하고
있어 오타나 잘못된 URL 형식이 통과됩니다; databaseUrl 변수를 읽은 직후 (databaseUrl /
process.env.DATABASE_URL) URL 형식을 검증하도록 추가하세요 — 예를 들어 프로토콜(mysql://, postgres://
등) 또는 전체 URL 파싱(정규식 또는 URL 생성자)으로 유효성 검사를 하고, 실패하면 기존 throw new
Error("DATABASE_URL is required") 대신 또는 함께 명확한 에러 메시지(예: "DATABASE_URL is
missing or invalid: <value>")를 던져 즉시 실패하게 만드세요.


export default defineConfig({
schema: "./src/database/schema/index.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: databaseUrl,
},
});
28 changes: 24 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
{
"name": "team-mino",
"version": "1.0.0",
"packageManager": "bun@1.3.14",
"main": "dist/src/main",
"scripts": {
"build": "bun run scripts/build.ts",
"check": "biome check .",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push",
"format": "biome format --write .",
"lint": "biome lint .",
"start": "nest start",
"start:dev": "nest start --watch",
"start:prod": "./dist/server",
"test": "bun test test/"
"test": "bun test test/",
"test:watch": "bun test --watch",
"typecheck": "tsgo --noEmit",
"typecheck:tsc": "tsc --noEmit"
},
"keywords": [],
"author": "",
Expand All @@ -15,18 +26,27 @@
"dependencies": {
"@kiyasov/platform-hono": "^2.0.3",
"@nestjs/common": "^11.1.24",
"@nestjs/config": "^4.0.2",
"@nestjs/config": "^4.0.4",
"@nestjs/core": "^11.1.24",
"@nestjs/swagger": "^11.4.4",
"@nestjs/terminus": "^11.1.1",
"drizzle-orm": "^0.45.2",
"nestjs-pino": "^4.6.1",
"pino-http": "^11.0.0",
"reflect-metadata": "^0.2.2"
"postgres": "^3.4.9",
"reflect-metadata": "^0.2.2",
"valibot": "^1.4.1"
},
"devDependencies": {
"@biomejs/biome": "^2.4.15",
"@nestjs/cli": "^11.0.21",
"@nestjs/schematics": "^11.1.0",
"@nestjs/testing": "^11.1.24",
"@types/bun": "^1.3.14",
"@types/node": "^25.9.1",
"@typescript/native-preview": "^7.0.0-dev.20260421.2",
"drizzle-kit": "^0.31.10",
"pino-pretty": "^13.1.3",
"typescript": "^5.9.3"
"typescript": "^6.0.0"
Comment on lines +47 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# TypeScript 설정 검증

echo "=== Running TypeScript compiler (tsc) ==="
bun run typecheck:tsc

echo ""
echo "=== Running tsgo compiler ==="
bun run typecheck

echo ""
echo "=== Running NestJS build ==="
bun run build

Repository: mash-up-kr/Team-MINO-Node

Length of output: 319


🌐 Web query:

TypeScript 6.0.0 compatibility with NestJS 11

💡 Result:

NestJS 11 is compatible with TypeScript 6.0.x in practice, but you should verify via your specific NestJS 11 package versions because NestJS 11’s repo has been pinned to TypeScript 5.9.x at various points. What we can say with sources: 1) TypeScript 6.0.0 was released in March 2026, and it is positioned as the “last release based on the current JavaScript codebase” (i.e., it’s a normal compiler release intended for adoption) [1]. 2) NestJS 11 requires Node.js v20 or higher [2], but those sources do not state any explicit NestJS 11 incompatibility with TypeScript 6.0.0. 3) However, the NestJS repo has been updated to TypeScript 5.9.3 in its dependencies (example commits show TypeScript moving within the 5.9 series) [3], and there is evidence of a pending bump from TypeScript 5.9.3 to 6.0.2 in April 2026 [4]. That strongly suggests the maintainers were still targeting the 5.9.x line during early/mid 2026, and TypeScript 6 adoption was actively being rolled in. Practical recommendation for today (2026-05-29): - If you want the safest “supported by the repo” path, start with TypeScript 5.9.x, aligning with NestJS 11’s visible dependency target (e.g., 5.9.3) [3]. - If you specifically need TypeScript 6.0.0/6.x, it should generally work because NestJS is TypeScript-authored and TypeScript’s 6.0 is a standard compiler release [1], but you may encounter build/tooling issues in your exact stack (SWC/ts-jest/ts-node/eslint plugins, etc.). If you tell me your exact NestJS 11 package set (e.g., @nestjs/core version) and tooling (SWC vs tsc, eslint/ts-jest/ts-node), I can narrow this to a more definitive “yes/no + specific fixes” for your configuration.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show package.json around the mentioned lines and also scripts/deps
echo "=== package.json (relevant section) ==="
nl -ba package.json | sed -n '1,120p'

echo
echo "=== Extract dependency versions for TypeScript packages ==="
node -e "const p=require('./package.json'); const d=p.dependencies||{}; const dev=p.devDependencies||{}; const all={...d,...dev}; ['typescript','`@typescript/native-preview`','tsgo','drizzle-kit','drizzle-orm'].forEach(k=>{ if(all[k]) console.log(k+':', all[k]); });"

echo
echo "=== package.json scripts (typecheck/build) ==="
node -e "const p=require('./package.json'); const s=p.scripts||{}; Object.keys(s).filter(k=>/typecheck|tsc|tsgo|build/i.test(k)).sort().forEach(k=>console.log(k+':', s[k]));"

Repository: mash-up-kr/Team-MINO-Node

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== package.json (relevant section) ==="
cat -n package.json | sed -n '1,120p'

echo
echo "=== Extract dependency versions for TypeScript packages ==="
node -e "const p=require('./package.json'); const d=p.dependencies||{}; const dev=p.devDependencies||{}; const all={...d,...dev}; ['typescript','`@typescript/native-preview`','drizzle-kit','drizzle-orm'].forEach(k=>{ if(all[k]) console.log(k+':', all[k]); else console.log(k+': (not found)'); });"

echo
echo "=== package.json scripts (typecheck/build) ==="
node -e "const p=require('./package.json'); const s=p.scripts||{}; Object.keys(s).filter(k=>/typecheck|tsc|tsgo|build/i.test(k)).sort().forEach(k=>console.log(k+':', s[k]));"

Repository: mash-up-kr/Team-MINO-Node

Length of output: 2129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== tsconfig files ==="
fd -t f "tsconfig*.json" . || true

echo
echo "=== nest-cli config files ==="
fd -t f "nest-cli*.json" . || true
fd -t f "nest*.json" . -g "*nest*cli*.json" || true

echo
echo "=== Print nest-cli.json if present ==="
for f in $(fd -t f -g "nest-cli.json" .); do
  echo "--- $f ---"
  head -n 120 "$f" | cat -n
done || true

echo
echo "=== Print tsconfig.json (or first found) ==="
for f in $(fd -t f -g "tsconfig.json" .); do
  echo "--- $f ---"
  head -n 160 "$f" | cat -n
done || true

echo
echo "=== Search for tsgo/tsc/nest build related config ==="
rg -n --hidden --glob '!**/node_modules/**' "(tsgo|tsc|typescript|nest build|nest-cli)" package.json tsconfig*.json nest-cli*.json 2>/dev/null || true

Repository: mash-up-kr/Team-MINO-Node

Length of output: 2134


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== tsconfig.build.json ==="
if [ -f tsconfig.build.json ]; then
  cat -n tsconfig.build.json
else
  echo "tsconfig.build.json not found"
fi

echo
echo "=== Locate tsgo usage/config ==="
rg -n --hidden --glob '!**/node_modules/**' "tsgo" . || true

echo
echo "=== Check whether tsgo is declared in package.json ==="
node -e "const p=require('./package.json'); const all={...(p.dependencies||{}),...(p.devDependencies||{})}; console.log('tsgo:', all.tsgo || '(not declared)');"

echo
echo "=== Quick look at package-lock/pnpm/yarn presence (if any) ==="
ls -la | head

Repository: mash-up-kr/Team-MINO-Node

Length of output: 1717


🏁 Script executed:

#!/bin/bash
# TypeScript 설정 검증

echo "=== Running TypeScript compiler (tsc) ==="
bun run typecheck:tsc

echo ""
echo "=== Running tsgo compiler ==="
bun run typecheck

echo ""
echo "=== Running NestJS build ==="
bun run build

Repository: mash-up-kr/Team-MINO-Node

Length of output: 319


🌐 Web query:

tsgo bin @typescript/native-preview which TypeScript version does it use

💡 Result:

The @typescript/native-preview package corresponds to the upcoming TypeScript 7.0 release [1][2]. It provides a native, Go-based implementation of the TypeScript compiler, which is accessed via the tsgo command [3][4][1]. While the package is currently labeled with version numbers such as 7.0.0-dev (or 7.0.0-beta), it represents the development efforts for TypeScript 7 [3][1][2]. This native compiler is intended to eventually replace the existing JavaScript-based TypeScript compiler (historically referred to as "Strada") and be merged into the official TypeScript package [1][5].

Citations:


🌐 Web query:

NestJS nest build uses which TypeScript version local typescript dependency

💡 Result:

The nest build command uses the TypeScript version installed as a local dependency in your project's node_modules directory [1][2]. When you create a new NestJS project, the Nest CLI automatically adds typescript as a devDependency in your package.json file [1][2]. The nest build command is designed as a wrapper around the standard tsc (TypeScript compiler) or build tools like webpack (in monorepos) [1][2][3]. Because it relies on these locally installed tools, the build process consistently uses the version of TypeScript defined in your project's local dependency tree, ensuring that all environments—such as your local machine, team members' machines, or CI/CD pipelines—utilize the same compiler version [1][2]. If you encounter issues related to compiler features or errors, you should verify the version specified in your project's package.json or lock file, as the CLI will utilize whichever version is resolved by your package manager (e.g., npm, pnpm, or yarn) in the local node_modules [4].

Citations:


🏁 Script executed:

#!/bin/bash
# TypeScript 설정 검증
echo "=== typecheck:tsc (TS 6.x) ==="
bun run typecheck:tsc

echo "=== typecheck (tsgo / native-preview: TS 7.0 preview) ==="
bun run typecheck

echo "=== nest build (uses local typescript: TS 6.x) ==="
bun run build

Repository: mash-up-kr/Team-MINO-Node

Length of output: 356


typecheck(tsgo/TS7 preview)와 nest build(tsc/TS6)가 서로 다른 컴파일러 버전을 쓰므로 일관성 확인 및 정렬 필요

현재 package.json에서 devDependencies는 typescript: ^6.0.0과 @typescript/native-preview: ^7.0.0-dev.20260421.2를 동시에 사용합니다. 또한 스크립트가 build: nest build이고, typecheck: tsgo --noEmit로 분리돼 있어 nest build는 로컬 typescript(6.x)로 컴파일되며, tsgo는 @typescript/native-preview가 제공하는(= TypeScript 7.0 개발 라인) 네이티브 컴파일러로 타입체크됩니다. 이 조합이면 타입 정의/판정 결과가 달라져 빌드 통과 여부가 불안정해질 수 있습니다(특히 @nestjs/*@^11.1.x, drizzle-orm@^0.45.2 조합).

다음 확인/조치가 필요합니다.

  • bun run typecheck:tsc(TS 6)와 bun run typecheck(tsgo/TS 7 preview)의 에러/진단이 일치하는지 비교
  • bun run build가 typecheck/typecheck:tsc 결과와 동일한 전제로 돌아가는지 확인
  • 불일치가 나오면 CI에서 한 가지 컴파일러만 쓰도록 정렬(예: 타입체크를 typecheck:tsc로 단일화하거나, 최소한 TypeScript 버전을 빌드/타입체크에 맞춰 동일 라인으로 고정)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 45 - 47, package.json에서 devDependencies의
"typescript" (TS6)와 "`@typescript/native-preview`" (TS7 preview) 및 scripts
"typecheck" (tsgo) vs "typecheck:tsc" (tsc) / "build" (nest build)이 서로 다른 컴파일러
라인을 사용해 결과가 불일치할 수 있으므로, 먼저 로컬에서 bun run typecheck (tsgo/TS7 preview)과 bun run
typecheck:tsc (tsc/TS6)을 실행해 에러·진단이 일치하는지 비교하고 bun run build 결과가 어느 쪽과 동일한지
확인하세요; 불일치가 발생하면 CI와 package.json을 단일 컴파일러로 정렬하도록 수정(예: 제거하거나 고정: 삭제 또는 통일된
TypeScript 버전으로 "typescript"만 사용, 또는 모든 스크립트를 tsgo 또는 tsc로 통일)하고
scripts("typecheck","typecheck:tsc","build")를 해당 컴파일러에 맞게 업데이트해 빌드와 타입체크가 같은
컴파일러/버전에서 동작하도록 고정하십시오.

}
}
4 changes: 4 additions & 0 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

// Prevent bundling of NestJS's optional dependencies
const dependencies = [
"@mikro-orm/core",
"@nestjs/microservices",
"@nestjs/microservices/microservices-module",
"@nestjs/mongoose",
"@nestjs/platform-express",
"@nestjs/platform-socket.io",
"@nestjs/sequelize/dist/common/sequelize.utils",
"@nestjs/typeorm/dist/common/typeorm.utils",
"@nestjs/websockets/socket-module",
"class-transformer",
"class-transformer/storage",
Expand Down
10 changes: 10 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Module, RequestMethod } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TerminusModule } from "@nestjs/terminus";
import { LoggerModule } from "nestjs-pino";
import { validateEnv } from "./config/env.schema";
import { DatabaseModule } from "./database/database.module";
import { DrizzleHealthIndicator } from "./health/drizzle.health-indicator";
import { HealthController } from "./health/health.controller";

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validate: validateEnv,
}),
LoggerModule.forRootAsync({
inject: [ConfigService],
Expand All @@ -20,6 +26,10 @@ import { LoggerModule } from "nestjs-pino";
exclude: [{ method: RequestMethod.ALL, path: "*" }],
}),
}),
DatabaseModule,
TerminusModule,
],
controllers: [HealthController],
providers: [DrizzleHealthIndicator],
})
export class AppModule {}
32 changes: 32 additions & 0 deletions src/config/env.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as v from "valibot";

const envSchema = v.object({
NODE_ENV: v.optional(
v.picklist(["development", "test", "production"]),
"development",
),
PORT: v.optional(v.pipe(v.string(), v.regex(/^\d+$/), v.transform(Number), v.minValue(1), v.maxValue(65535)), "3000"),
DATABASE_URL: v.pipe(v.string(), v.minLength(1), v.url()),
DB_POOL_SIZE: v.optional(v.pipe(v.string(), v.regex(/^\d+$/), v.transform(Number), v.minValue(1)), "10"),
});

export type Env = v.InferOutput<typeof envSchema>;

export function validateEnv(config: Record<string, unknown>): Env {
const result = v.safeParse(envSchema, config);

if (!result.success) {
const messages = result.issues
.map((issue) => {
const path =
issue.path?.map((item) => String(item.key)).join(".") ?? "unknown";

return `${path}: ${issue.message}`;
})
.join("\n");

throw new Error(`Invalid environment variables:\n${messages}`);
}

return result.output;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
8 changes: 8 additions & 0 deletions src/database/database.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from "@nestjs/common";
import { DatabaseService } from "./database.service";

@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}
25 changes: 25 additions & 0 deletions src/database/database.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable, type OnModuleDestroy } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import type { Env } from "../config/env.schema";

@Injectable()
export class DatabaseService implements OnModuleDestroy {
private readonly client: ReturnType<typeof postgres>;
readonly db: ReturnType<typeof drizzle>;

constructor(configService: ConfigService<Env>) {
const databaseUrl = configService.getOrThrow("DATABASE_URL", { infer: true });
const max = configService.get("DB_POOL_SIZE", 10, { infer: true });

this.client = postgres(databaseUrl, {
max,
});
this.db = drizzle(this.client);
}

async onModuleDestroy() {
await this.client.end();
}
}
1 change: 1 addition & 0 deletions src/database/schema/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
Loading
Loading