Skip to content

Adapters

The Booking Kit uses the adapter pattern to abstract external dependencies. Each adapter is a TypeScript interface — implement it for your preferred provider.

interface AuthAdapter {
getSession(request: Request): Promise<AuthSession | null>;
getUser(userId: string): Promise<AuthUser | null>;
}

Default: NextAuth.js 5.x Alternatives: Supabase Auth, Clerk, Lucia, custom JWT

interface EmailAdapter {
sendEmail(options: SendEmailOptions): Promise<EmailResult>;
}
interface SendEmailOptions {
to: string;
subject: string;
html: string;
text?: string;
from?: string;
replyTo?: string;
attachments?: EmailAttachment[];
}

Default: Resend Alternatives: SendGrid, AWS SES, Postmark, Nodemailer

interface CalendarAdapter {
createEvent(options: CalendarEventOptions): Promise<CalendarEventResult>;
updateEvent(eventId: string, options: CalendarEventOptions): Promise<CalendarEventResult>;
deleteEvent(eventId: string): Promise<void>;
getConflicts(start: Date, end: Date): Promise<CalendarConflict[]>;
}

Default: Google Calendar (OAuth) Alternatives: Microsoft Outlook, CalDAV

interface JobAdapter {
enqueue(jobName: string, payload: unknown, options?: { delay?: number }): Promise<void>;
scheduleAt(jobName: string, payload: unknown, runAt: Date): Promise<void>;
}

Default: Inngest 3.x Alternatives: Trigger.dev, BullMQ, Vercel Cron, pg-boss

interface PaymentAdapter {
createPaymentIntent(options: CreatePaymentIntentOptions): Promise<CreatePaymentIntentResult>;
createSetupIntent(options: CreateSetupIntentOptions): Promise<CreateSetupIntentResult>;
capturePayment(paymentIntentId: string): Promise<CaptureResult>;
refundPayment(paymentIntentId: string, amountCents?: number): Promise<RefundResult>;
}

Default: Stripe Alternatives: Implement for your payment provider

interface StorageAdapter {
encrypt(plaintext: string): Promise<string>;
decrypt(ciphertext: string): Promise<string>;
}

Default: Env var encryption key (AES-256) Alternatives: HashiCorp Vault, AWS KMS, Azure Key Vault

interface SmsAdapter {
sendSms(options: SendSmsOptions): Promise<SmsResult>;
}

Default: Not implemented (optional) Alternatives: Twilio, MessageBird, Vonage

While The Booking Kit is built for PostgreSQL, it provides specialized adapters for other database environments.

Cloudflare D1 / SQLite (@thebookingkit/d1)

Section titled “Cloudflare D1 / SQLite (@thebookingkit/d1)”

Cloudflare D1 requires special handling for date storage and concurrency. This package provides:

  • Canonical Date Encoding: SQLite has no native timestamp type. D1DateCodec ensures all dates are stored as sortable UTC-Z strings.
  • Advisory Locking: Since SQLite lacks EXCLUDE constraints, D1BookingLock provides application-level mutexes to prevent double-booking.
  • Schedule Adapters: Utilities to intersect personal and location schedules into RRULE windows.
import { D1DateCodec, D1BookingLock } from "@thebookingkit/d1";
// Ensure a slot is available and book it atomically
const lock = new D1BookingLock(db);
await lock.withLock(`provider:${id}`, async () => {
// Read-check-write logic here
});
// Example: Custom email adapter using SendGrid
import sgMail from "@sendgrid/mail";
const sendGridAdapter: EmailAdapter = {
async sendEmail(options) {
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
const [response] = await sgMail.send({
to: options.to,
from: options.from || "noreply@example.com",
subject: options.subject,
html: options.html,
text: options.text,
});
return {
messageId: response.headers["x-message-id"],
status: "sent" as const,
};
},
};

Pass adapters to the functions that need them:

import { sendConfirmationEmail } from "@thebookingkit/core";
await sendConfirmationEmail(bookingPayload, sendGridAdapter);

Adapters are injected at the call site, not globally configured. This makes testing easy — pass a mock adapter in tests.