Cal.diy Onboarding LabChapter IV — What actually gets written: the Booking model and its relations0/4Contents
Chapter IV

What actually gets written: the Booking model and its relations

The createBooking() call from the trace writes one Booking row, but that row is deliberately thin: its references and seatsReferences relations stay empty at creation time and are filled in later by separate writes once EventManager talks to the external calendar or video provider. This module reads the Booking, BookingReference, Attendee, and BookingSeat models in packages/prisma/schema.prisma to show exactly which fields createBooking() populates immediately and which related tables are populated afterward, and why the application code (createBooking, EventManager, RegularBookingService) is written to match the shape of those Prisma models rather than the reverse.


walkthrough

buildNewBookingData: turning request data into a Prisma.BookingCreateInput

packages/features/bookings/lib/handleNewBooking/createBooking.ts · lines 168202
lines 181–182

Before assembling the write, getAttendeesData(evt) and getEventTypeRel(eventType.id) pre-shape two pieces of the incoming request into the exact structures Prisma's relation API expects, so the object literal below can just reference them.

line 184

uid is set as a bare scalar — this is the value the caller generated and passed in, landing directly on the Booking.uid @unique column.

lines 188–189

startTime/endTime are normalized to UTC with dayjs.utc(...).toDate() before being written, matching the plain DateTime columns on Booking that carry no timezone of their own.

line 192

status is decided right here, not left to the schema's @default(ACCEPTED) — a ternary on eventType.isConfirmedByDefault picks BookingStatus.ACCEPTED or BookingStatus.PENDING, which later determines whether RegularBookingService immediately syncs to the calendar or queues a confirmation webhook instead.

line 195

eventType: eventTypeRel writes the pre-built { connect: { id: eventTypeId } } object rather than an eventTypeId scalar — Prisma's nested-write API models relations as connect/create instructions, not raw foreign keys, so getEventTypeRel exists purely to produce that shape.

lines 198–201

attendees: { createMany: { data: attendeesData } } } is a nested write: Prisma creates one Attendee row per entry in attendeesData inside the same tx.booking.create call, and stamps each with this booking's bookingId — no separate insert is needed for the fan-out to happen.

function buildNewBookingData(params: CreateBookingParams) {  const {    uid,    evt,    reqBody,    eventType,    input,    originalRescheduledBooking,    rescheduledBy,    creationSource,    tracking,  } = params;  const attendeesData = getAttendeesData(evt);  const eventTypeRel = getEventTypeRel(eventType.id);  const newBookingData: Prisma.BookingCreateInput = {    uid,    userPrimaryEmail: evt.organizer.email,    responses: input.responses === null || evt.seatsPerTimeSlot ? Prisma.JsonNull : input.responses,    title: evt.title,    startTime: dayjs.utc(evt.startTime).toDate(),    endTime: dayjs.utc(evt.endTime).toDate(),    description: evt.seatsPerTimeSlot ? null : evt.additionalNotes,    customInputs: isPrismaObjOrUndefined(evt.customInputs),    status: eventType.isConfirmedByDefault ? BookingStatus.ACCEPTED : BookingStatus.PENDING,    oneTimePassword: evt.oneTimePassword,    location: evt.location,    eventType: eventTypeRel,    smsReminderNumber: input.smsReminderNumber,    metadata: reqBody.metadata,    attendees: {      createMany: {        data: attendeesData,      },    },
step 1 of 6
model BookingReference {  id                         Int      @id @default(autoincrement())  /// @zod.string.min(1)  type                       String  /// @zod.string.min(1)  uid                        String  meetingId                  String?  thirdPartyRecurringEventId String?  meetingPassword            String?  meetingUrl                 String?  booking                    Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade)  bookingId                  Int?  externalCalendarId         String?  deleted                    Boolean?   credential             Credential?           @relation(fields: [credentialId], references: [id], onDelete: SetNull)  credentialId           Int?  delegationCredential   DelegationCredential? @relation(fields: [delegationCredentialId], references: [id], onDelete: SetNull)  delegationCredentialId String?   @@index([bookingId])  @@index([type])  @@index([uid])}
packages/prisma/schema.prisma · lines 801824
Figure I · type (e.g. "google_calendar" or "daily_video"), uid, meetingId, and meetingUrl are the external provider's own identifiers for the calendar or video event; bookingId is the foreign key tying that identity back to one Booking. This is the table EventManager and later webhook code query to answer "which Google Calendar event / Daily.co room belongs to this booking" without ever re-deriving it from scratch.
  try {    if (!isDryRun) {      await deps.prismaClient.booking.update({        where: {          uid: booking.uid,        },        data: {          location: evt.location,          metadata: { ...(typeof booking.metadata === "object" && booking.metadata), ...metadata },          references: {            createMany: {              data: referencesToCreate,            },          },        },      });    }
packages/features/bookings/lib/service/RegularBookingService.ts · lines 24442460
Figure II · By the time this runs, createBooking()'s transaction already committed the Booking and Attendee rows — booking.uid here is the uid from that earlier write. RegularBookingService calls eventManager.create(evt, ...) in between (not shown in this excerpt), collects its referencesToCreate array, and only now issues a second deps.prismaClient.booking.update whose nested references: { createMany } writes the BookingReference rows. The booking's identity in your database and its identity in the external calendar are deliberately written in two separate steps, because the second one depends on a network call that can fail independently of the first.