Database (TypeORM)
The API uses TypeORM with PostgreSQL for relational data storage.
Entity Relationships
Entities
Entities live in src/data/entities/ and define the database schema.
typescript
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
@Entity("books")
export class Book {
@PrimaryGeneratedColumn("uuid")
id: string
@Column()
title: string
@Column({ nullable: true })
description?: string
@ManyToOne(() => User, (user) => user.books)
owner: User
@Column()
ownerId: string
@CreateDateColumn()
createdAt: Date
@UpdateDateColumn()
updatedAt: Date
}Repositories
Repositories extend TypeormRepository<T> for standardized data access:
typescript
import { TypeormRepository } from "./TypeormRepository"
export class BookRepository extends TypeormRepository<Book> {
constructor() {
super(Book)
}
async findByOwner(ownerId: string): Promise<Book[]> {
return this.repository.find({
where: { ownerId },
order: { createdAt: "DESC" },
})
}
}Built-in Methods
TypeormRepository<T> provides:
findById(id)- Get by IDfindAll()- Get allfind(query)- Paged querycreate(data)- Insertupdate(id, data)- Updatedelete(id)- DeleteverifyValue(value)- SQL injection protection
Caching
Repositories use Redis caching automatically:
typescript
// Cache is invalidated on insert/update/delete
const book = await this.bookRepository.findById(id)Migrations
Generate Migration
After changing entities:
bash
pnpm db:generate MigrationNameRun Migrations
bash
pnpm db:migrateRevert Migration
bash
pnpm db:revertRemote Migrations
For dev/prod environments (requires Tailscale):
bash
./scripts/postgres/db-migrate.sh dev
./scripts/postgres/db-migrate.sh prodQuery Building
Use TypeORM's query builder for complex queries:
typescript
async findWithStats(ownerId: string) {
return this.repository
.createQueryBuilder("book")
.leftJoinAndSelect("book.readings", "reading")
.where("book.ownerId = :ownerId", { ownerId })
.select([
"book.id",
"book.title",
"COUNT(reading.id) as readCount",
])
.groupBy("book.id")
.getRawMany()
}Relations
Define Relations
typescript
@Entity("users")
export class User {
@OneToMany(() => Book, (book) => book.owner)
books: Book[]
}Load Relations
typescript
// Eager load
const book = await this.repository.findOne({
where: { id },
relations: ["owner"],
})
// Lazy load (entity must define as Promise)
const owner = await book.owner