Appearance
Database Schema
Quickback uses Drizzle ORM to define your database schema. Drizzle provides type-safe database access with a SQL-like syntax that compiles to efficient queries.
What is Drizzle?
Drizzle is a TypeScript ORM that lets you define your database tables as code. Unlike traditional ORMs that abstract away SQL, Drizzle embraces SQL syntax while adding type safety. Your schema definitions become the source of truth for both your database structure and TypeScript types.
Defining Tables
Tables are defined using Drizzle's schema builders. Here's an example:
typescript
// definitions/schema/rooms.ts
import { pgTable, text, integer, boolean, timestamp } from 'drizzle-orm/pg-core';
export const rooms = pgTable('rooms', {
id: text('id').primaryKey(),
name: text('name').notNull(),
description: text('description'),
capacity: integer('capacity').notNull().default(10),
roomType: text('room_type').notNull(),
isActive: boolean('is_active').notNull().default(true),
// Ownership - Quickback auto-detects this for the firewall
organizationId: text('organization_id').notNull(),
});Column Types
Drizzle supports all standard SQL column types:
| Type | Drizzle Function | Example |
|---|---|---|
| String | text(), varchar() | text('name') |
| Integer | integer(), bigint() | integer('count') |
| Boolean | boolean() | boolean('is_active') |
| Timestamp | timestamp() | timestamp('created_at') |
| JSON | json(), jsonb() | jsonb('metadata') |
| UUID | uuid() | uuid('id') |
| Decimal | decimal(), numeric() | decimal('price', { precision: 10, scale: 2 }) |
Column Modifiers
typescript
// Required field
name: text('name').notNull()
// Default value
isActive: boolean('is_active').default(true)
// Primary key
id: text('id').primaryKey()
// Unique constraint
email: text('email').unique()
// Default to current timestamp
createdAt: timestamp('created_at').defaultNow()Schema Organization
Organize your schemas by feature or domain:
definitions/
├── schema/
│ ├── index.ts # Re-exports all schemas
│ ├── users.ts # User-related tables
│ ├── rooms.ts # Room-related tables
│ ├── bookings.ts # Booking-related tables
│ └── organizations.ts # Organization tablesRe-export all schemas from an index file:
typescript
// definitions/schema/index.ts
export * from './users';
export * from './rooms';
export * from './bookings';
export * from './organizations';Audit Fields
Quickback automatically adds and manages these audit fields - you don't need to define them in your schema:
| Field | Type | Description |
|---|---|---|
createdAt | timestamp | Set when record is created |
createdBy | text | User ID who created the record |
modifiedAt | timestamp | Updated on every change |
modifiedBy | text | User ID who last modified |
deletedAt | timestamp | Set on soft delete |
deletedBy | text | User ID who deleted |
These columns are added to your database automatically and populated by Quickback on every operation.
Ownership Fields
For the firewall to work, include the appropriate ownership columns:
typescript
// For user-owned data
ownerId: text('owner_id').notNull()
// For organization-scoped data
organizationId: text('organization_id').notNull()
// For team-scoped data
teamId: text('team_id').notNull()The column names can be customized in your firewall configuration.
Relations (Optional)
Drizzle supports defining relations for type-safe joins:
typescript
import { relations } from 'drizzle-orm';
export const roomsRelations = relations(rooms, ({ one, many }) => ({
organization: one(organizations, {
fields: [rooms.organizationId],
references: [organizations.id],
}),
bookings: many(bookings),
}));Database Configuration
Configure database options in your Quickback config:
typescript
// quickback.config.ts
export default {
database: {
generateId: 'uuid', // 'uuid' | 'cuid' | 'nanoid' | 'serial' | false
namingConvention: 'camelCase', // 'camelCase' | 'snake_case'
usePlurals: true, // Table names: 'users' vs 'user'
},
compiler: {
features: {
auditFields: true, // Auto-manage audit timestamps
}
}
};Next Steps
Once your schema is defined, you can:
- Define resources with security layers
- Configure the firewall for data isolation
- Set up access control for CRUD operations