@kood/claude-code 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/hono/docs/library/drizzle/cloudflare-d1.md +247 -0
- package/templates/hono/docs/library/drizzle/config.md +167 -0
- package/templates/hono/docs/library/drizzle/index.md +259 -0
- package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +147 -0
- package/templates/tanstack-start/docs/library/drizzle/config.md +118 -0
- package/templates/tanstack-start/docs/library/drizzle/crud.md +205 -0
- package/templates/tanstack-start/docs/library/drizzle/index.md +79 -0
- package/templates/tanstack-start/docs/library/drizzle/relations.md +202 -0
- package/templates/tanstack-start/docs/library/drizzle/schema.md +154 -0
- package/templates/tanstack-start/docs/library/drizzle/setup.md +96 -0
- package/templates/tanstack-start/docs/library/drizzle/transactions.md +127 -0
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +204 -0
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +195 -0
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +150 -0
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +150 -0
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +203 -0
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +213 -0
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +1 -1
package/dist/index.js
CHANGED
|
@@ -404,7 +404,7 @@ var init = async (options) => {
|
|
|
404
404
|
|
|
405
405
|
// src/index.ts
|
|
406
406
|
var program = new Command();
|
|
407
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.2.
|
|
407
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.2.3");
|
|
408
408
|
program.option(
|
|
409
409
|
"-t, --template <names>",
|
|
410
410
|
"template names (comma-separated: tanstack-start,hono)"
|
package/package.json
CHANGED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Drizzle - Cloudflare D1
|
|
2
|
+
|
|
3
|
+
> SQLite 기반 서버리스 데이터베이스
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 주의사항
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
D1은 트랜잭션 미지원 (ACID 보장 X)
|
|
11
|
+
Drizzle Kit 마이그레이션 → wrangler CLI로 적용
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick Setup
|
|
17
|
+
|
|
18
|
+
### 설치
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install drizzle-orm
|
|
22
|
+
npm install -D drizzle-kit wrangler
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 스키마 정의
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// src/db/schema.ts
|
|
29
|
+
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'
|
|
30
|
+
|
|
31
|
+
export const users = sqliteTable('users', {
|
|
32
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
33
|
+
email: text('email').notNull().unique(),
|
|
34
|
+
name: text('name'),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export type User = typeof users.$inferSelect
|
|
38
|
+
export type NewUser = typeof users.$inferInsert
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### drizzle.config.ts
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { defineConfig } from 'drizzle-kit'
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
dialect: 'sqlite',
|
|
48
|
+
schema: './src/db/schema.ts',
|
|
49
|
+
out: './drizzle/migrations',
|
|
50
|
+
driver: 'd1-http',
|
|
51
|
+
dbCredentials: {
|
|
52
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
53
|
+
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
|
|
54
|
+
token: process.env.CLOUDFLARE_D1_TOKEN!,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Hono + D1
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Hono } from 'hono'
|
|
63
|
+
import { drizzle } from 'drizzle-orm/d1'
|
|
64
|
+
import * as schema from './db/schema'
|
|
65
|
+
|
|
66
|
+
type Bindings = { DB: D1Database }
|
|
67
|
+
|
|
68
|
+
const app = new Hono<{ Bindings: Bindings }>()
|
|
69
|
+
|
|
70
|
+
app.get('/users', async (c) => {
|
|
71
|
+
const db = drizzle(c.env.DB, { schema })
|
|
72
|
+
const users = await db.select().from(schema.users)
|
|
73
|
+
return c.json(users)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export default app
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## D1 데이터베이스 생성
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx wrangler d1 create my-database
|
|
85
|
+
# database_id = "xxxx-xxxx-xxxx-xxxx"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### wrangler.jsonc
|
|
89
|
+
|
|
90
|
+
```jsonc
|
|
91
|
+
{
|
|
92
|
+
"name": "hono-d1-app",
|
|
93
|
+
"main": "src/index.ts",
|
|
94
|
+
"compatibility_date": "2025-01-01",
|
|
95
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
96
|
+
"d1_databases": [{
|
|
97
|
+
"binding": "DB",
|
|
98
|
+
"database_name": "my-database",
|
|
99
|
+
"database_id": "YOUR_DATABASE_ID",
|
|
100
|
+
"migrations_dir": "drizzle/migrations"
|
|
101
|
+
}]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 마이그레이션 워크플로우
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# 1. 마이그레이션 파일 생성
|
|
111
|
+
npx drizzle-kit generate
|
|
112
|
+
|
|
113
|
+
# 2. 로컬 적용
|
|
114
|
+
npx wrangler d1 migrations apply my-database --local
|
|
115
|
+
|
|
116
|
+
# 3. 원격 적용
|
|
117
|
+
npx wrangler d1 migrations apply my-database --remote
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 완전한 CRUD API
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { Hono } from 'hono'
|
|
126
|
+
import { zValidator } from '@hono/zod-validator'
|
|
127
|
+
import { z } from 'zod'
|
|
128
|
+
import { HTTPException } from 'hono/http-exception'
|
|
129
|
+
import { drizzle } from 'drizzle-orm/d1'
|
|
130
|
+
import { eq } from 'drizzle-orm'
|
|
131
|
+
import * as schema from './db/schema'
|
|
132
|
+
|
|
133
|
+
type Bindings = { DB: D1Database }
|
|
134
|
+
type Variables = { db: ReturnType<typeof drizzle> }
|
|
135
|
+
|
|
136
|
+
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()
|
|
137
|
+
|
|
138
|
+
// Drizzle 미들웨어
|
|
139
|
+
app.use('*', async (c, next) => {
|
|
140
|
+
c.set('db', drizzle(c.env.DB, { schema }))
|
|
141
|
+
await next()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
app.get('/users', async (c) => {
|
|
145
|
+
const db = c.get('db')
|
|
146
|
+
const users = await db.select().from(schema.users)
|
|
147
|
+
return c.json(users)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
app.post('/users',
|
|
151
|
+
zValidator('json', z.object({ email: z.email(), name: z.string().optional() })),
|
|
152
|
+
async (c) => {
|
|
153
|
+
const db = c.get('db')
|
|
154
|
+
const data = c.req.valid('json')
|
|
155
|
+
|
|
156
|
+
const existing = await db.select().from(schema.users).where(eq(schema.users.email, data.email))
|
|
157
|
+
if (existing.length > 0) {
|
|
158
|
+
throw new HTTPException(409, { message: 'Email already exists' })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const [user] = await db.insert(schema.users).values(data).returning()
|
|
162
|
+
return c.json(user, 201)
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
app.get('/users/:id', async (c) => {
|
|
167
|
+
const db = c.get('db')
|
|
168
|
+
const id = Number(c.req.param('id'))
|
|
169
|
+
|
|
170
|
+
const [user] = await db.select().from(schema.users).where(eq(schema.users.id, id))
|
|
171
|
+
if (!user) {
|
|
172
|
+
throw new HTTPException(404, { message: 'User not found' })
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return c.json(user)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
app.put('/users/:id',
|
|
179
|
+
zValidator('json', z.object({ name: z.string() })),
|
|
180
|
+
async (c) => {
|
|
181
|
+
const db = c.get('db')
|
|
182
|
+
const id = Number(c.req.param('id'))
|
|
183
|
+
const data = c.req.valid('json')
|
|
184
|
+
|
|
185
|
+
const [user] = await db.update(schema.users)
|
|
186
|
+
.set(data)
|
|
187
|
+
.where(eq(schema.users.id, id))
|
|
188
|
+
.returning()
|
|
189
|
+
|
|
190
|
+
if (!user) {
|
|
191
|
+
throw new HTTPException(404, { message: 'User not found' })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return c.json(user)
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
app.delete('/users/:id', async (c) => {
|
|
199
|
+
const db = c.get('db')
|
|
200
|
+
const id = Number(c.req.param('id'))
|
|
201
|
+
|
|
202
|
+
await db.delete(schema.users).where(eq(schema.users.id, id))
|
|
203
|
+
return c.json({ success: true })
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
export default app
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 로컬 개발
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Worker 실행
|
|
215
|
+
npx wrangler dev
|
|
216
|
+
|
|
217
|
+
# D1 조회
|
|
218
|
+
npx wrangler d1 execute my-database --local \
|
|
219
|
+
--command "SELECT * FROM users;"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 배포
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npx drizzle-kit generate
|
|
228
|
+
npx wrangler d1 migrations apply my-database --remote
|
|
229
|
+
npx wrangler deploy
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 제한사항
|
|
235
|
+
|
|
236
|
+
| 항목 | D1 |
|
|
237
|
+
|------|-----|
|
|
238
|
+
| 트랜잭션 | 미지원 |
|
|
239
|
+
| 마이그레이션 | wrangler 사용 |
|
|
240
|
+
| 접속 방식 | HTTP (D1 binding) |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 관련 문서
|
|
245
|
+
|
|
246
|
+
- [Drizzle 개요](./index.md)
|
|
247
|
+
- [Cloudflare 배포](../../deployment/cloudflare.md)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Drizzle - Config 파일
|
|
2
|
+
|
|
3
|
+
> drizzle.config.ts 설정
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 기본 설정
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// drizzle.config.ts
|
|
11
|
+
import { defineConfig } from 'drizzle-kit'
|
|
12
|
+
|
|
13
|
+
export default defineConfig({
|
|
14
|
+
dialect: 'postgresql',
|
|
15
|
+
schema: './src/db/schema/index.ts',
|
|
16
|
+
out: './drizzle/migrations',
|
|
17
|
+
dbCredentials: {
|
|
18
|
+
url: process.env.DATABASE_URL!,
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 파일 위치
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
프로젝트/
|
|
29
|
+
├── drizzle.config.ts # 프로젝트 루트
|
|
30
|
+
├── drizzle/
|
|
31
|
+
│ └── migrations/ # 마이그레이션 파일
|
|
32
|
+
├── src/
|
|
33
|
+
│ └── db/
|
|
34
|
+
│ ├── index.ts # Drizzle client
|
|
35
|
+
│ └── schema/ # 스키마 정의
|
|
36
|
+
│ ├── index.ts
|
|
37
|
+
│ ├── user.ts
|
|
38
|
+
│ └── post.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## PostgreSQL
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { defineConfig } from 'drizzle-kit'
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
dialect: 'postgresql',
|
|
50
|
+
schema: './src/db/schema/index.ts',
|
|
51
|
+
out: './drizzle/migrations',
|
|
52
|
+
dbCredentials: {
|
|
53
|
+
url: process.env.DATABASE_URL!,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## MySQL
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { defineConfig } from 'drizzle-kit'
|
|
64
|
+
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
dialect: 'mysql',
|
|
67
|
+
schema: './src/db/schema/index.ts',
|
|
68
|
+
out: './drizzle/migrations',
|
|
69
|
+
dbCredentials: {
|
|
70
|
+
url: process.env.DATABASE_URL!,
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## SQLite
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { defineConfig } from 'drizzle-kit'
|
|
81
|
+
|
|
82
|
+
export default defineConfig({
|
|
83
|
+
dialect: 'sqlite',
|
|
84
|
+
schema: './src/db/schema/index.ts',
|
|
85
|
+
out: './drizzle/migrations',
|
|
86
|
+
dbCredentials: {
|
|
87
|
+
url: './sqlite.db',
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Cloudflare D1
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { defineConfig } from 'drizzle-kit'
|
|
98
|
+
|
|
99
|
+
export default defineConfig({
|
|
100
|
+
dialect: 'sqlite',
|
|
101
|
+
schema: './src/db/schema/index.ts',
|
|
102
|
+
out: './drizzle/migrations',
|
|
103
|
+
driver: 'd1-http',
|
|
104
|
+
dbCredentials: {
|
|
105
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
106
|
+
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
|
|
107
|
+
token: process.env.CLOUDFLARE_D1_TOKEN!,
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 설정 옵션
|
|
115
|
+
|
|
116
|
+
| 옵션 | 설명 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| `dialect` | DB 종류 (postgresql, mysql, sqlite) |
|
|
119
|
+
| `schema` | 스키마 파일 경로 |
|
|
120
|
+
| `out` | 마이그레이션 출력 폴더 |
|
|
121
|
+
| `dbCredentials` | DB 연결 정보 |
|
|
122
|
+
| `driver` | 드라이버 (d1-http 등) |
|
|
123
|
+
| `verbose` | 상세 로그 출력 |
|
|
124
|
+
| `strict` | 엄격 모드 |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 환경변수 로드
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// drizzle.config.ts
|
|
132
|
+
import 'dotenv/config'
|
|
133
|
+
import { defineConfig } from 'drizzle-kit'
|
|
134
|
+
|
|
135
|
+
export default defineConfig({
|
|
136
|
+
dialect: 'postgresql',
|
|
137
|
+
schema: './src/db/schema/index.ts',
|
|
138
|
+
out: './drizzle/migrations',
|
|
139
|
+
dbCredentials: {
|
|
140
|
+
url: process.env.DATABASE_URL!,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 다중 스키마 파일
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { defineConfig } from 'drizzle-kit'
|
|
151
|
+
|
|
152
|
+
export default defineConfig({
|
|
153
|
+
dialect: 'postgresql',
|
|
154
|
+
schema: './src/db/schema/*.ts', // glob 패턴
|
|
155
|
+
out: './drizzle/migrations',
|
|
156
|
+
dbCredentials: {
|
|
157
|
+
url: process.env.DATABASE_URL!,
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 관련 문서
|
|
165
|
+
|
|
166
|
+
- [Drizzle 개요](./index.md)
|
|
167
|
+
- [Cloudflare D1](./cloudflare-d1.md)
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Drizzle ORM - Database ORM
|
|
2
|
+
|
|
3
|
+
> Type-safe, lightweight TypeScript ORM
|
|
4
|
+
|
|
5
|
+
@config.md
|
|
6
|
+
@cloudflare-d1.md
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 설치
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install drizzle-orm pg
|
|
14
|
+
npm install -D drizzle-kit @types/pg
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 스키마 정의
|
|
20
|
+
|
|
21
|
+
### 폴더 구조
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/
|
|
25
|
+
├── db/
|
|
26
|
+
│ ├── index.ts # Drizzle client
|
|
27
|
+
│ └── schema/
|
|
28
|
+
│ ├── index.ts # export all
|
|
29
|
+
│ ├── user.ts # User 테이블
|
|
30
|
+
│ └── post.ts # Post 테이블
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### user.ts
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { pgTable, serial, text, timestamp, boolean } from 'drizzle-orm/pg-core'
|
|
37
|
+
|
|
38
|
+
// 사용자 테이블
|
|
39
|
+
export const users = pgTable('users', {
|
|
40
|
+
id: serial('id').primaryKey(),
|
|
41
|
+
email: text('email').notNull().unique(), // 로그인 이메일
|
|
42
|
+
name: text('name'), // 표시 이름
|
|
43
|
+
verified: boolean('verified').notNull().default(false),
|
|
44
|
+
createdAt: timestamp('created_at').notNull().defaultNow(),
|
|
45
|
+
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// 타입 추론
|
|
49
|
+
export type User = typeof users.$inferSelect
|
|
50
|
+
export type NewUser = typeof users.$inferInsert
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Drizzle Client
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// src/db/index.ts
|
|
59
|
+
import { drizzle } from 'drizzle-orm/node-postgres'
|
|
60
|
+
import * as schema from './schema'
|
|
61
|
+
|
|
62
|
+
export const db = drizzle(process.env.DATABASE_URL!, { schema })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## CRUD
|
|
68
|
+
|
|
69
|
+
### Create
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { db } from '@/db'
|
|
73
|
+
import { users } from '@/db/schema'
|
|
74
|
+
|
|
75
|
+
const user = await db.insert(users).values({
|
|
76
|
+
email: 'user@example.com',
|
|
77
|
+
name: 'John',
|
|
78
|
+
}).returning()
|
|
79
|
+
|
|
80
|
+
// Batch insert
|
|
81
|
+
await db.insert(users).values([
|
|
82
|
+
{ email: 'user1@example.com', name: 'User 1' },
|
|
83
|
+
{ email: 'user2@example.com', name: 'User 2' },
|
|
84
|
+
])
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Read
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { eq, and, or, gt, like, desc, asc } from 'drizzle-orm'
|
|
91
|
+
|
|
92
|
+
// 전체 조회
|
|
93
|
+
const allUsers = await db.select().from(users)
|
|
94
|
+
|
|
95
|
+
// 조건 조회
|
|
96
|
+
const user = await db.select().from(users).where(eq(users.id, 1))
|
|
97
|
+
|
|
98
|
+
// 복합 조건
|
|
99
|
+
const filtered = await db.select().from(users).where(
|
|
100
|
+
and(
|
|
101
|
+
eq(users.verified, true),
|
|
102
|
+
gt(users.createdAt, new Date('2024-01-01'))
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// 정렬 + 페이지네이션
|
|
107
|
+
const paginated = await db.select().from(users)
|
|
108
|
+
.orderBy(desc(users.createdAt))
|
|
109
|
+
.limit(10)
|
|
110
|
+
.offset(0)
|
|
111
|
+
|
|
112
|
+
// 특정 필드만 선택
|
|
113
|
+
const emails = await db.select({ email: users.email }).from(users)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Update
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
await db.update(users)
|
|
120
|
+
.set({ name: 'Updated Name' })
|
|
121
|
+
.where(eq(users.id, 1))
|
|
122
|
+
|
|
123
|
+
// Upsert
|
|
124
|
+
await db.insert(users)
|
|
125
|
+
.values({ email: 'user@example.com', name: 'John' })
|
|
126
|
+
.onConflictDoUpdate({
|
|
127
|
+
target: users.email,
|
|
128
|
+
set: { name: 'John Updated' },
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Delete
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
await db.delete(users).where(eq(users.id, 1))
|
|
136
|
+
await db.delete(users).where(eq(users.verified, false))
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 필터링 연산자
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { eq, ne, gt, gte, lt, lte, like, ilike, inArray, isNull, between, and, or, not } from 'drizzle-orm'
|
|
145
|
+
|
|
146
|
+
// 비교
|
|
147
|
+
where(eq(users.id, 1)) // =
|
|
148
|
+
where(ne(users.id, 1)) // !=
|
|
149
|
+
where(gt(users.age, 18)) // >
|
|
150
|
+
where(gte(users.age, 18)) // >=
|
|
151
|
+
where(lt(users.age, 65)) // <
|
|
152
|
+
where(lte(users.age, 65)) // <=
|
|
153
|
+
|
|
154
|
+
// 문자열
|
|
155
|
+
where(like(users.name, '%John%'))
|
|
156
|
+
where(ilike(users.name, '%john%')) // 대소문자 무시
|
|
157
|
+
|
|
158
|
+
// 배열
|
|
159
|
+
where(inArray(users.id, [1, 2, 3]))
|
|
160
|
+
|
|
161
|
+
// NULL
|
|
162
|
+
where(isNull(users.deletedAt))
|
|
163
|
+
|
|
164
|
+
// 범위
|
|
165
|
+
where(between(users.age, 18, 65))
|
|
166
|
+
|
|
167
|
+
// 논리
|
|
168
|
+
where(and(eq(users.role, 'admin'), eq(users.verified, true)))
|
|
169
|
+
where(or(eq(users.role, 'admin'), eq(users.role, 'moderator')))
|
|
170
|
+
where(not(eq(users.role, 'guest')))
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 트랜잭션
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const result = await db.transaction(async (tx) => {
|
|
179
|
+
const [user] = await tx.insert(users)
|
|
180
|
+
.values({ email: 'user@example.com' })
|
|
181
|
+
.returning()
|
|
182
|
+
|
|
183
|
+
await tx.insert(posts).values({ title: 'First Post', authorId: user.id })
|
|
184
|
+
|
|
185
|
+
return user
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// 롤백
|
|
189
|
+
await db.transaction(async (tx) => {
|
|
190
|
+
const [account] = await tx.select().from(accounts).where(eq(accounts.id, 1))
|
|
191
|
+
if (account.balance < 100) {
|
|
192
|
+
tx.rollback() // 예외 발생, 트랜잭션 롤백
|
|
193
|
+
}
|
|
194
|
+
await tx.update(accounts).set({ balance: account.balance - 100 }).where(eq(accounts.id, 1))
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Hono와 함께 사용
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { Hono } from 'hono'
|
|
204
|
+
import { zValidator } from '@hono/zod-validator'
|
|
205
|
+
import { z } from 'zod'
|
|
206
|
+
import { HTTPException } from 'hono/http-exception'
|
|
207
|
+
import { eq } from 'drizzle-orm'
|
|
208
|
+
import { db } from '@/db'
|
|
209
|
+
import { users } from '@/db/schema'
|
|
210
|
+
|
|
211
|
+
const app = new Hono()
|
|
212
|
+
|
|
213
|
+
app.post('/users',
|
|
214
|
+
zValidator('json', z.object({ email: z.email(), name: z.string().optional() })),
|
|
215
|
+
async (c) => {
|
|
216
|
+
const data = c.req.valid('json')
|
|
217
|
+
|
|
218
|
+
const existing = await db.select().from(users).where(eq(users.email, data.email))
|
|
219
|
+
if (existing.length > 0) {
|
|
220
|
+
throw new HTTPException(409, { message: 'Email already exists' })
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const [user] = await db.insert(users).values(data).returning()
|
|
224
|
+
return c.json({ user }, 201)
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
app.get('/users/:id', async (c) => {
|
|
229
|
+
const id = Number(c.req.param('id'))
|
|
230
|
+
const [user] = await db.select().from(users).where(eq(users.id, id))
|
|
231
|
+
|
|
232
|
+
if (!user) {
|
|
233
|
+
throw new HTTPException(404, { message: 'User not found' })
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return c.json({ user })
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
export default app
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## CLI 명령어
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npx drizzle-kit generate # 마이그레이션 생성
|
|
248
|
+
npx drizzle-kit migrate # 마이그레이션 실행
|
|
249
|
+
npx drizzle-kit push # 스키마 동기화 (개발용)
|
|
250
|
+
npx drizzle-kit pull # DB에서 스키마 가져오기
|
|
251
|
+
npx drizzle-kit studio # GUI 실행
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 관련 문서
|
|
257
|
+
|
|
258
|
+
- [Config 파일](./config.md) - drizzle.config.ts 설정
|
|
259
|
+
- [Cloudflare D1](./cloudflare-d1.md)
|