Skip to content

Commit 64b2f26

Browse files
committed
docs: add readme
1 parent ae4da00 commit 64b2f26

File tree

1 file changed

+381
-0
lines changed

1 file changed

+381
-0
lines changed

packages/db/README.md

Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
# @acme/db
2+
3+
A type-safe database package built with Prisma, Kysely, and PostgreSQL. This package provides a unified database client with support for both ORM-style queries (Prisma) and SQL query building (Kysely).
4+
5+
## Features
6+
7+
- **Prisma ORM** - Type-safe database access with intuitive API
8+
- **Kysely Integration** - Advanced SQL query building capabilities
9+
- **Zod Schema Generation** - Auto-generated Zod schemas for validation
10+
- **PostgreSQL** - Built for PostgreSQL databases
11+
- **Type Safety** - Full TypeScript support with generated types
12+
- **Environment Validation** - Type-safe environment variable handling
13+
14+
## Database Schema
15+
16+
The database includes three main models:
17+
18+
- **User** - User accounts with email, name, and image
19+
- **Account** - OAuth provider accounts linked to users
20+
- **VerificationToken** - Email verification tokens with rate limiting
21+
22+
## Installation
23+
24+
This package is part of a monorepo and is not meant to be installed independently. Import it from other packages in the workspace:
25+
26+
```typescript
27+
import { db } from '@acme/db'
28+
```
29+
30+
## Setup
31+
32+
### 1. Configure Environment Variables
33+
34+
Create a `.env` file in the root of your monorepo with your database URL (you can do this by copying the existing `.env.example`):
35+
36+
```env
37+
DATABASE_URL="postgresql://user:password@localhost:5432/database"
38+
```
39+
40+
### 2. Generate Prisma Client
41+
42+
```bash
43+
pnpm generate
44+
```
45+
46+
This generates:
47+
48+
- Prisma Client
49+
- Kysely types
50+
- Zod schemas
51+
52+
This auto-runs before `npm run dev`
53+
54+
### 3. Run Migrations
55+
56+
```bash
57+
# Development
58+
pnpm migrate:dev
59+
60+
# Production
61+
pnpm migrate:deploy
62+
```
63+
64+
## Usage
65+
66+
### Basic Queries with Prisma
67+
68+
```typescript
69+
import { db } from '@acme/db'
70+
71+
// Create a user
72+
const user = await db.user.create({
73+
data: {
74+
75+
name: 'John Doe',
76+
image: 'https://example.com/avatar.jpg',
77+
},
78+
})
79+
80+
// Find a user
81+
const user = await db.user.findUnique({
82+
where: { email: '[email protected]' },
83+
include: { accounts: true },
84+
})
85+
86+
// Update a user
87+
const updatedUser = await db.user.update({
88+
where: { id: 'user-id' },
89+
data: { name: 'Jane Doe' },
90+
})
91+
92+
// Delete a user
93+
await db.user.delete({
94+
where: { id: 'user-id' },
95+
})
96+
```
97+
98+
### Advanced Queries with Kysely
99+
100+
The database client is extended with Kysely for complex SQL queries:
101+
102+
```typescript
103+
import { db } from '@acme/db'
104+
105+
// Use Kysely for complex queries
106+
const users = await db.$kysely
107+
.selectFrom('User')
108+
.select(['id', 'email', 'name'])
109+
.where('email', 'like', '%@example.com')
110+
.orderBy('name', 'asc')
111+
.execute()
112+
113+
// Joins and aggregations
114+
const userAccounts = await db.$kysely
115+
.selectFrom('User')
116+
.innerJoin('Account', 'Account.user_id', 'User.id')
117+
.select(['User.email', 'Account.provider'])
118+
.execute()
119+
120+
// Complex filtering
121+
const activeUsers = await db.$kysely
122+
.selectFrom('User')
123+
.select(['id', 'email'])
124+
.where(({ eb, or }) =>
125+
or([
126+
eb('email', 'like', '%@gmail.com'),
127+
eb('email', 'like', '%@yahoo.com'),
128+
]),
129+
)
130+
.execute()
131+
```
132+
133+
### Using Zod Schemas for Validation
134+
135+
Generated Zod schemas are available for runtime validation:
136+
137+
```typescript
138+
import { UserCreateSchema } from '@acme/db/generated/zod'
139+
140+
// Validate user input
141+
const result = UserCreateSchema.safeParse({
142+
143+
name: 'John Doe',
144+
})
145+
146+
if (result.success) {
147+
const user = await db.user.create({
148+
data: result.data,
149+
})
150+
}
151+
```
152+
153+
### Transactions
154+
155+
```typescript
156+
import { db } from '@acme/db'
157+
158+
// Prisma transactions
159+
const result = await db.$transaction(async (tx) => {
160+
const user = await tx.user.create({
161+
data: { email: '[email protected]' },
162+
})
163+
164+
const account = await tx.account.create({
165+
data: {
166+
userId: user.id,
167+
provider: 'google',
168+
providerAccountId: 'google-id',
169+
},
170+
})
171+
172+
return { user, account }
173+
})
174+
175+
// Interactive transactions with timeout
176+
const result = await db.$transaction(
177+
async (tx) => {
178+
// Your transaction logic
179+
},
180+
{
181+
maxWait: 5000, // 5 seconds
182+
timeout: 10000, // 10 seconds
183+
},
184+
)
185+
186+
// Kysely transactions
187+
We are not supposed to use `db.$kysely.transaction()` directly as it is not supported by the extension. Instead, use Prisma transactions as shown above.
188+
```
189+
190+
### Raw SQL Queries
191+
192+
```typescript
193+
import { db } from '@acme/db'
194+
195+
// Execute raw SQL
196+
const users = await db.$queryRaw`
197+
SELECT * FROM "User" WHERE email LIKE ${pattern}
198+
`
199+
200+
// Execute raw SQL without returning data
201+
await db.$executeRaw`
202+
UPDATE "User" SET name = ${newName} WHERE id = ${userId}
203+
`
204+
205+
Prisma\'s `queryRaw` and `executeRaw` template strings automatically escapes to prevent SQL injection. To leverage on this feature, **DO NOT** build your queries up piece meal but pass the full query in here, with string parameters (`${your_parameter}`) as needed
206+
```
207+
208+
## Available Scripts
209+
210+
```bash
211+
# Generate Prisma Client, Kysely types, and Zod schemas
212+
pnpm generate
213+
214+
# Open Prisma Studio (database GUI on port 5556). This is also available at root as `pnpm db:studio`
215+
pnpm studio
216+
217+
# Push schema changes to database (development only). This is also available at root as `pnpm db:push`
218+
pnpm push
219+
220+
# Create a new migration
221+
pnpm migrate:dev
222+
223+
# Apply migrations (production)
224+
pnpm migrate:deploy
225+
226+
# Reset database and run migrations
227+
pnpm reset
228+
229+
# Seed database
230+
pnpm seed
231+
232+
# Build TypeScript
233+
pnpm build
234+
235+
# Type checking
236+
pnpm typecheck
237+
238+
# Linting
239+
pnpm lint
240+
241+
# Format checking
242+
pnpm format
243+
```
244+
245+
## Package Exports
246+
247+
### Default Export (`@acme/db`)
248+
249+
```typescript
250+
import { db, Prisma, PrismaClient } from '@acme/db'
251+
```
252+
253+
- `db` - Main database client (Prisma with Kysely extension)
254+
- `Prisma` - Prisma namespace for types
255+
- `PrismaClient` - Raw Prisma client type
256+
257+
### Client Export (`@acme/db/client`)
258+
259+
```typescript
260+
import { PrismaClient } from '@acme/db/client'
261+
```
262+
263+
Raw PrismaClient exports, mainly for testing purposes.
264+
265+
### Browser Export (`@acme/db/browser`)
266+
267+
```typescript
268+
import { Prisma } from '@acme/db/browser'
269+
```
270+
271+
Browser-compatible Prisma types (no actual database connection).
272+
273+
### Enums Export (`@acme/db/enums`)
274+
275+
```typescript
276+
import { QueryMode, SortOrder } from '@acme/db/enums'
277+
```
278+
279+
Prisma-generated enums for use in queries.
280+
281+
## Development Workflow
282+
283+
### Adding a New Model
284+
285+
1. Update `prisma/schema.prisma` with your new model
286+
2. Generate migration: `pnpm migrate:dev --name add_new_model`
287+
3. Regenerate types: `pnpm generate`
288+
4. The generated types will be available automatically
289+
290+
### Modifying Existing Models
291+
292+
1. Update the model in `prisma/schema.prisma`
293+
2. Create migration: `pnpm migrate:dev --name describe_change`
294+
3. Regenerate types: `pnpm generate`
295+
296+
### Seeding Data
297+
298+
Edit `prisma/seed.ts` and add your seed data:
299+
300+
```typescript
301+
import { db } from '../src/index'
302+
303+
async function main() {
304+
await db.user.createMany({
305+
data: [
306+
{ email: '[email protected]', name: 'User 1' },
307+
{ email: '[email protected]', name: 'User 2' },
308+
],
309+
})
310+
}
311+
```
312+
313+
Run the seed:
314+
315+
```bash
316+
pnpm seed
317+
```
318+
319+
## Type Safety
320+
321+
All operations are fully type-safe:
322+
323+
```typescript
324+
// TypeScript will enforce correct field names and types
325+
const user = await db.user.create({
326+
data: {
327+
email: '[email protected]', // string
328+
name: 'John', // string | null
329+
// TypeScript error if you add unknown fields
330+
},
331+
})
332+
333+
// Kysely queries are also type-safe
334+
const result = await db.$kysely
335+
.selectFrom('User')
336+
.select(['id', 'email']) // Only valid columns are allowed
337+
.execute()
338+
```
339+
340+
## Best Practices
341+
342+
2. **Leverage Kysely for Complex Queries** - Use Kysely for queries that are difficult to express with Prisma
343+
3. **Use Generated Zod Schemas** - Validate external input before passing to database
344+
4. **Index Frequently Queried Fields** - Add indexes in your schema for better performance
345+
5. **Use Connection Pooling** - The package uses PrismaPg adapter with connection pooling built-in
346+
347+
## Troubleshooting
348+
349+
### Prisma Client Not Found
350+
351+
Run `pnpm generate` to generate the Prisma Client.
352+
353+
### Migration Errors
354+
355+
If migrations fail, you can reset the database:
356+
357+
```bash
358+
pnpm reset
359+
```
360+
361+
**Warning**: This will delete all data.
362+
363+
### Type Errors After Schema Changes
364+
365+
After modifying the schema, always run:
366+
367+
```bash
368+
pnpm generate
369+
```
370+
371+
This regenerates all types and ensures TypeScript has the latest schema information.
372+
373+
### Generation Errors
374+
375+
If `pnpm generate` fails, ensure your `schema.prisma` is valid and that your database is reachable. If you have modified the list of plugins recently, delete the `src/generated` folder and try again.
376+
377+
## Links
378+
379+
- [Prisma Documentation](https://www.prisma.io/docs)
380+
- [Kysely Documentation](https://kysely.dev/)
381+
- [Zod Documentation](https://zod.dev/)

0 commit comments

Comments
 (0)