Files
template-showcase/src/entities/note/api/repository.ts
T

60 lines
2.0 KiB
TypeScript

import { and, desc, eq } from 'drizzle-orm'
import type { drizzle } from 'drizzle-orm/postgres-js'
import { notes, type Note } from '@/entities/note/model/schema'
import type { NoteInput } from '@/entities/note/model/input'
/** The handle the shared `db()` returns (`@/shared/api/db/server`). */
export type NoteDb = ReturnType<typeof drizzle>
/**
* Note persistence over one database handle. The handle is a parameter so a test
* can run the same code against its own Postgres.
*
* Every write names its author in the `where`: a visitor can change only the
* notes they wrote (tenancy, D16).
*/
export function noteRepository(db: NoteDb) {
return {
async create(input: NoteInput & { authorId: string }): Promise<Note> {
const [row] = await db.insert(notes).values(input).returning()
if (!row) throw new Error('the insert returned no row')
return row
},
async get(id: string): Promise<Note | undefined> {
const [row] = await db.select().from(notes).where(eq(notes.id, id)).limit(1)
return row
},
async listByAuthor(authorId: string, limit = 50): Promise<Note[]> {
return db
.select()
.from(notes)
.where(eq(notes.authorId, authorId))
.orderBy(desc(notes.createdAt))
.limit(limit)
},
/** The updated note, or `undefined` when `authorId` did not write it. */
async update(id: string, authorId: string, input: NoteInput): Promise<Note | undefined> {
const [row] = await db
.update(notes)
.set({ ...input, updatedAt: new Date() })
.where(and(eq(notes.id, id), eq(notes.authorId, authorId)))
.returning()
return row
},
/** `true` when the note existed and `authorId` wrote it. */
async remove(id: string, authorId: string): Promise<boolean> {
const rows = await db
.delete(notes)
.where(and(eq(notes.id, id), eq(notes.authorId, authorId)))
.returning({ id: notes.id })
return rows.length > 0
},
}
}
export type NoteRepository = ReturnType<typeof noteRepository>