Skip to content

Quickstart

The fastest way to see Apiary in action is to carve a single operating window into bookable slots:

import { generateTimeSlots } from "@apiary-dev/core";
const now = new Date("2026-01-05T08:00:00Z");
const slots = generateTimeSlots(
{
windows: [
{
start: new Date("2026-01-05T09:00:00Z"),
end: new Date("2026-01-05T17:00:00Z"),
},
],
existingBookings: [
{
start: new Date("2026-01-05T11:30:00Z"),
end: new Date("2026-01-05T12:00:00Z"),
},
],
durationMinutes: 30,
intervalMinutes: 15,
buffers: { beforeMinutes: 5, afterMinutes: 5 },
minNoticeMinutes: 30,
},
now,
);
const open = slots.filter((s) => s.available);

open is now a list of TimeSlots — 30-minute appointments starting every 15 minutes — with the 11:30 booking blocked out, a 5-minute buffer enforced on each side, and anything starting within 30 minutes of now excluded.

Each slot carries a reason explaining its state:

// TimeSlot[] where reason is one of:
// 'AVAILABLE' | 'COLLISION' | 'OUTSIDE_BOUNDS'
// | 'MIN_NOTICE_VIOLATION' | 'MAX_HORIZON_EXCEEDED'

Real systems rarely hand you windows directly. expandWeeklyRules turns recurring weekly rules into windows for a date range:

import { expandWeeklyRules } from "@apiary-dev/core";
const windows = expandWeeklyRules({
rules: [
{ dayOfWeek: 1, startTime: "09:00", endTime: "12:00" }, // Mon AM
{ dayOfWeek: 1, startTime: "13:00", endTime: "17:00" }, // Mon PM
{ dayOfWeek: 3, startTime: "09:00", endTime: "17:00" }, // Wed
],
startDate: new Date("2026-01-05T00:00:00Z"), // Mon Jan 5, 2026
endDate: new Date("2026-01-19T00:00:00Z"),
timeZone: "America/New_York",
});

Rules are expressed in local wall-clock time; windows come back as exact UTC Dates, with daylight-saving transitions handled per day.

Combine the two steps for a complete pipeline:

const slots = generateTimeSlots(
{
windows,
existingBookings: await fetchBookingsInRange(start, end),
durationMinutes: 30,
intervalMinutes: 15,
buffers: { beforeMinutes: 5, afterMinutes: 5 },
minNoticeMinutes: 30,
maxHorizonMinutes: 7 * 24 * 60,
},
new Date(),
);

Once the migration has been applied, the adapter gives you a full BookingRepository:

import { Pool } from "pg";
import { PostgresBookingRepository } from "@apiary-dev/postgres";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const bookings = new PostgresBookingRepository(pool);
// Dynamic availability for the next 7 days
const windows = await bookings.getAvailableWindows({
startDate: new Date(),
endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
mode: "dynamic",
timeZone: "America/New_York",
});