Skip to content

Database Reference (@thebookingkit/db)

@thebookingkit/db provides the Drizzle ORM schema, a typed database client factory, inferred TypeScript types for all tables, and a migration runner for custom SQL that Drizzle’s kit does not handle.

Terminal window
npm install @thebookingkit/db drizzle-orm postgres

Creates a Drizzle database client connected to a Postgres instance. Uses postgres.js as the driver.

import { createDb } from "@thebookingkit/db";
const db = createDb(process.env.DATABASE_URL!, {
max: 10, // connection pool size
});

Parameters

ParameterTypeRequiredDescription
databaseUrlstringYesPostgres connection string
options.maxnumberNoMaximum pool connections. Default: 10
options.sslboolean | objectNoSSL config. Defaults to false for localhost, "require" otherwise

Returns Database — a Drizzle ORM instance with the full BookingKit schema loaded.

The type of the client returned by createDb. Use this to type function parameters that accept a database connection.

import type { Database } from "@thebookingkit/db";
async function getBooking(db: Database, id: string) {
return db.query.bookings.findFirst({
where: (b, { eq }) => eq(b.id, id),
});
}

All tables are exported from @thebookingkit/db and can be used with Drizzle’s query builder or SQL template tags.

import {
bookings,
providers,
eventTypes,
availabilityRules,
availabilityOverrides,
outOfOffice,
organizations,
teams,
teamMembers,
bookingEvents,
bookingQuestionsResponses,
bookingSeats,
recurringBookings,
payments,
routingForms,
routingSubmissions,
workflows,
workflowLogs,
webhooks,
webhookDeliveries,
emailDeliveryLog,
customerPreferences,
walkInQueue,
resources,
resourceAvailabilityRules,
resourceAvailabilityOverrides,
} from "@thebookingkit/db";

Core tables

Table exportDB table nameDescription
bookingsbookingsAll booking records with status, times, and customer info
bookingEventsbooking_eventsAppend-only audit trail of every booking state change
bookingSeatsbooking_seatsIndividual seat reservations for group events
bookingQuestionsResponsesbooking_questions_responsesCustomer answers to event type intake questions
recurringBookingsrecurring_bookingsRecurring series parent records

Provider and scheduling tables

Table exportDB table nameDescription
providersprovidersService providers (staff members, contractors)
eventTypesevent_typesBookable service definitions with duration, price, and settings
availabilityRulesavailability_rulesRRULE-based recurring availability windows
availabilityOverridesavailability_overridesOne-off date overrides (extra availability or blocks)
outOfOfficeout_of_officeMulti-day out-of-office periods

Organization and team tables

Table exportDB table nameDescription
organizationsorganizationsMulti-tenant organization records
teamsteamsProvider groups with shared scheduling
teamMembersteam_membersProvider-to-team membership with roles

Payment and commerce tables

Table exportDB table nameDescription
paymentspaymentsPayment intent and capture records

Automation tables

Table exportDB table nameDescription
workflowsworkflowsTrigger-based automation definitions
workflowLogsworkflow_logsExecution history for workflow runs
webhookswebhooksWebhook subscription registrations
webhookDeliverieswebhook_deliveriesDelivery attempt log with response codes
emailDeliveryLogemail_delivery_logTransactional email send history
routingFormsrouting_formsConditional routing form definitions
routingSubmissionsrouting_submissionsCustomer responses to routing forms
customerPreferencescustomer_preferencesPer-customer notification and timezone preferences

Queue and resource tables

Table exportDB table nameDescription
walkInQueuewalk_in_queueWalk-in waitlist entries
resourcesresourcesBookable resources (rooms, equipment, vehicles)
resourceAvailabilityRulesresource_availability_rulesRRULE-based resource availability
resourceAvailabilityOverridesresource_availability_overridesOne-off resource availability overrides

All tables include id (UUID), created_at, and updated_at columns. Most tables include an optional organization_id column for multi-tenancy.


Enum types derived from the Drizzle pgEnum definitions. Use these for type-safe status comparisons without importing Drizzle internals.

import type {
BookingStatusDb,
PaymentStatusDb,
BookingEventTypeDb,
PaymentTypeDb,
LocationTypeDb,
AssignmentStrategyDb,
TeamMemberRoleDb,
QuestionFieldTypeDb,
RecurringFrequencyDb,
SeatStatusDb,
WorkflowTriggerDb,
WorkflowActionTypeDb,
EmailDeliveryStatusDb,
BookingSourceDb,
WalkInStatusDb,
} from "@thebookingkit/db";
TypeValues
BookingStatusDb"pending", "confirmed", "completed", "cancelled", "rejected", "rescheduled", "no_show"
PaymentStatusDb"unpaid", "paid", "partially_refunded", "refunded", "failed"
BookingEventTypeDb"created", "confirmed", "cancelled", "rescheduled", "completed", "no_show", "payment_received", "refunded", "rejected"
PaymentTypeDb"full", "deposit", "free"
LocationTypeDb"in_person", "video", "phone", "custom"
AssignmentStrategyDb"round_robin", "least_busy", "fixed", "customer_choice"
TeamMemberRoleDb"owner", "admin", "member"
QuestionFieldTypeDb"text", "textarea", "select", "checkbox", "phone", "email", "number", "date"
RecurringFrequencyDb"daily", "weekly", "biweekly", "monthly"
SeatStatusDb"reserved", "confirmed", "cancelled"
WorkflowTriggerDb"booking_created", "booking_confirmed", "booking_cancelled", "booking_rescheduled", "before_event", "after_event"
WorkflowActionTypeDb"send_email", "send_sms", "webhook", "update_status", "add_to_calendar"
EmailDeliveryStatusDb"queued", "sent", "delivered", "failed", "bounced"
BookingSourceDb"web", "api", "embed", "walk_in", "manual"
WalkInStatusDb"waiting", "called", "in_progress", "completed", "cancelled", "no_show"

TypeScript types inferred from Drizzle’s InferSelectModel. These represent rows as they come back from database queries.

import type {
Booking,
Provider,
EventType,
AvailabilityRule,
AvailabilityOverride,
OutOfOffice,
Organization,
Team,
TeamMember,
BookingEvent,
BookingQuestionResponse,
BookingSeat,
RecurringBooking,
Payment,
RoutingForm,
RoutingSubmission,
Workflow,
WorkflowLog,
Webhook,
WebhookDelivery,
EmailDeliveryLogEntry,
CustomerPreference,
WalkInQueue,
Resource,
ResourceAvailabilityRule,
ResourceAvailabilityOverride,
} from "@thebookingkit/db";

TypeScript types inferred from Drizzle’s InferInsertModel. These represent the data required to insert a new row. Fields with database defaults (id, created_at, updated_at) are optional.

import type {
NewBooking,
NewProvider,
NewEventType,
NewAvailabilityRule,
NewAvailabilityOverride,
NewOutOfOffice,
NewOrganization,
NewTeam,
NewTeamMember,
NewBookingEvent,
NewBookingQuestionResponse,
NewBookingSeat,
NewRecurringBooking,
NewPayment,
NewRoutingForm,
NewRoutingSubmission,
NewWorkflow,
NewWorkflowLog,
NewWebhook,
NewWebhookDelivery,
NewEmailDeliveryLogEntry,
NewCustomerPreference,
NewWalkInQueue,
NewResource,
NewResourceAvailabilityRule,
NewResourceAvailabilityOverride,
} from "@thebookingkit/db";

Runs the custom SQL migration files that Drizzle Kit does not manage — extensions, exclusion constraints, audit triggers, stored functions, and resource tables.

import { runCustomMigrations } from "@thebookingkit/db";
await runCustomMigrations(process.env.DATABASE_URL!);

Parameters

ParameterTypeRequiredDescription
databaseUrlstringYesPostgres connection string

Migration files applied (in order)

FilePurpose
0001_setup_extensions.sqlEnable btree_gist, uuid-ossp, and other required extensions
0002_booking_audit_trigger.sqlInstall the trigger that writes to booking_events on every status change
0003_gdpr_anonymize.sqlInstall the anonymize_customer stored function
0004_create_booking_function.sqlInstall the atomic create_booking function with exclusion constraint enforcement
0005_resources.sqlSet up the EXCLUDE USING gist constraint on the resources table

Run this once after your initial schema push and after any upgrade that adds new migration files.


import { createDb, bookings, providers } from "@thebookingkit/db";
import { eq, and, gte, lte } from "drizzle-orm";
const db = createDb(process.env.DATABASE_URL!);
// Find upcoming confirmed bookings for a provider
const upcoming = await db
.select()
.from(bookings)
.where(
and(
eq(bookings.providerId, providerId),
eq(bookings.status, "confirmed"),
gte(bookings.startsAt, new Date()),
)
)
.orderBy(bookings.startsAt);
import { createDb, bookings, type NewBooking } from "@thebookingkit/db";
const db = createDb(process.env.DATABASE_URL!);
const newBooking: NewBooking = {
providerId: "uuid-here",
eventTypeId: "uuid-here",
customerEmail: "alice@example.com",
customerName: "Alice",
startsAt: new Date("2026-04-15T10:00:00.000Z"),
endsAt: new Date("2026-04-15T10:30:00.000Z"),
status: "pending",
timezone: "America/New_York",
};
const [inserted] = await db.insert(bookings).values(newBooking).returning();
import type { Booking, BookingStatusDb } from "@thebookingkit/db";
function isConfirmed(booking: Booking): boolean {
return booking.status === "confirmed" satisfies BookingStatusDb;
}
// lib/db.ts — singleton database client
import { createDb, type Database } from "@thebookingkit/db";
import { runCustomMigrations } from "@thebookingkit/db";
let _db: Database | null = null;
export function getDb(): Database {
if (!_db) {
_db = createDb(process.env.DATABASE_URL!, { max: 5 });
}
return _db;
}
// In your migration script / startup:
// await runCustomMigrations(process.env.DATABASE_URL!);