@cinnabun/db
v0.0.14
Published
Database module for Cinnabun with Drizzle and Prisma adapters
Maintainers
Readme
@cinnabun/db
Database module for the Cinnabun framework with adapter pattern supporting Drizzle ORM and Prisma. Spring Data-style repositories with constructor injection.
Installation
bun add @cinnabun/dbWith Drizzle (SQLite)
bun add drizzle-orm drizzle-kitWith Prisma (PostgreSQL)
bun add @prisma/client prismaQuick Start
1. Define your schema (Drizzle)
// src/schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const users = sqliteTable("users", {
id: integer("id").primaryKey({ autoIncrement: true }),
name: text("name").notNull(),
email: text("email").notNull(),
});2. Configure your application
// src/main.ts
import "reflect-metadata";
import { CinnabunApp, CinnabunFactory } from "@cinnabun/core";
import { DatabaseModule, DatabasePlugin } from "@cinnabun/db";
@CinnabunApp({
port: 3000,
scanPaths: ["./src"],
imports: [
DatabaseModule.forRoot({
adapter: "drizzle",
url: "file:./data.db",
autoMigrate: true,
}),
],
plugins: [new DatabasePlugin()],
})
class App {}
CinnabunFactory.run(App);3. Use repositories
Option A: Spring Data-style (constructor injection)
Extend CrudRepositoryBase and use @RepositoryEntity. The framework provides the implementation at runtime.
// src/repositories/user.repository.ts
import { RepositoryEntity, CrudRepositoryBase } from "@cinnabun/db";
import { users } from "../schema";
type User = typeof users.$inferSelect;
@RepositoryEntity(users)
abstract class UserRepository extends CrudRepositoryBase<User, number> {}
// src/services/user.service.ts
import { Service } from "@cinnabun/core";
import { UserRepository } from "../repositories/user.repository.js";
@Service()
class UserService {
constructor(private readonly userRepo: UserRepository) {}
async findAll() {
return this.userRepo.findAll();
}
async findById(id: number) {
return this.userRepo.findById(id);
}
async create(data: { name: string; email: string }) {
return this.userRepo.save(data);
}
}
// src/controllers/user.controller.ts
import { RestController, GetMapping, PostMapping, Body } from "@cinnabun/core";
import { UserService } from "../services/user.service.js";
@RestController("/api/users")
class UserController {
constructor(private readonly userService: UserService) {}
@GetMapping("/")
async findAll() {
return this.userService.findAll();
}
@PostMapping("/")
async create(@Body() body: { name: string; email: string }) {
return this.userService.create(body);
}
}Register UserRepository and UserService in your module's providers array. With scanPaths: ["./src"], scanned modules will discover them automatically.
Prisma: Use the model name string instead of a table schema:
@RepositoryEntity("Todo")
abstract class TodoCrudRepository extends CrudRepositoryBase<Todo, string> {}Option B: InjectRepository (property injection)
import { RestController, GetMapping, PostMapping, Body } from "@cinnabun/core";
import { InjectRepository, Repository, RepositoryFactory } from "@cinnabun/db";
import { users } from "./schema";
type User = typeof users.$inferSelect;
@RestController("/api/users")
class UserController {
@InjectRepository(users)
private userRepo!: Repository<User>;
constructor(private __repositoryFactory__: RepositoryFactory) {}
@GetMapping("/")
async findAll() {
return this.userRepo.findAll();
}
@PostMapping("/")
async create(@Body() body: { name: string; email: string }) {
return this.userRepo.create(body);
}
}Transactions
Use @Transactional() to wrap methods in database transactions. Inject TransactionManager and pass it to the decorator when using constructor-injected repositories:
import { Service } from "@cinnabun/core";
import {
Transactional,
InjectRepository,
Repository,
RepositoryFactory,
TransactionManager,
} from "@cinnabun/db";
import { users, profiles } from "./schema";
@Service()
class UserService {
@InjectRepository(users)
private userRepo!: Repository<any>;
@InjectRepository(profiles)
private profileRepo!: Repository<any>;
constructor(
private __repositoryFactory__: RepositoryFactory,
private __transactionManager__: TransactionManager,
) {}
@Transactional()
async createUserWithProfile(userData: any, profileData: any) {
const user = await this.userRepo.create(userData);
await this.profileRepo.create({ ...profileData, userId: user.id });
return user;
}
}CLI Commands
# Generate a migration
bun run cinnabun db generate <name>
# Run pending migrations
bun run cinnabun db migrateConfiguration Options
interface DatabaseModuleOptions {
adapter: "drizzle" | "prisma";
url: string;
schema?: string;
migrationsDir?: string;
autoMigrate?: boolean;
logging?: boolean | {
queries?: boolean;
errors?: boolean;
migrations?: boolean;
};
}Repository API
CrudRepositoryBase (Spring Data-style) — extend this with @RepositoryEntity. Do not implement CrudRepository directly; TypeScript requires all methods to be implemented.
@RepositoryEntity(users)
abstract class UserRepository extends CrudRepositoryBase<User, number> {}Methods: findById, findAll, findOne, findMany, save, saveAll, deleteById, delete, existsById, count.
Repository (lower-level, use with @InjectRepository):
interface Repository<T> {
findAll(options?: QueryOptions): Promise<T[]>;
findById(id: string | number): Promise<T | null>;
findOne(where: Partial<T>): Promise<T | null>;
findMany(where: Partial<T>, options?: QueryOptions): Promise<T[]>;
create(data: Partial<T>): Promise<T>;
update(id: string | number, data: Partial<T>): Promise<T>;
delete(id: string | number): Promise<void>;
count(where?: Partial<T>): Promise<number>;
exists(where: Partial<T>): Promise<boolean>;
}License
MIT
