@bookinglab/booking-journey-api
v1.8.1
Published
TypeScript library for BookingLab and JRNI APIs with React hooks support
Readme
@bookinglab/booking-journey-api
A lightweight, developer-friendly TypeScript library for BookingLab and JRNI APIs. Supports both vanilla JavaScript/TypeScript and React applications.
Features
- 🎯 Type-safe - Full TypeScript support with comprehensive type definitions
- ⚛️ React-ready - Context providers and hooks for seamless React integration
- 🌳 Tree-shakable - Only bundle what you use
- 🔧 Flexible - Works with vanilla JS/TS or React
- 📦 Lightweight - Minimal dependencies
Installation
npm install @bookinglab/booking-journey-apiFor React applications, you'll also need:
npm install @tanstack/react-queryQuick Start
Vanilla JavaScript/TypeScript
JRNI API
import { createJrniClient } from '@bookinglab/booking-journey-api';
const client = createJrniClient('https://api.jrni.com', {
appId: 'your-app-id',
appKey: 'your-app-key',
});
// Login
const response = await client.login({
email: '[email protected]',
password: 'password',
});
console.log(response.data.auth_token);BookingLab API
import { createBookingLabClient } from '@bookinglab/booking-journey-api';
const client = createBookingLabClient({ baseUrl: 'https://api.bookinglab.com' });
client.setAuthToken('your-auth-token');
// Get bookings
const bookings = await client.getBookings();
// Get services
const services = await client.getServices();
// Create a booking
const newBooking = await client.createBooking({
userId: 'user-123',
serviceId: 'service-456',
date: '2024-01-15T10:00:00Z',
});React Applications
Single Provider (JRNI Only)
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { JrniProvider, useLogin } from '@bookinglab/booking-journey-api';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<JrniProvider
baseUrl="https://api.jrni.com"
config={{ appId: 'your-app-id', appKey: 'your-app-key' }}
>
<LoginForm />
</JrniProvider>
</QueryClientProvider>
);
}
function LoginForm() {
const loginMutation = useLogin();
const handleLogin = () => {
loginMutation.mutate({
email: '[email protected]',
password: 'password',
});
};
return (
<button onClick={handleLogin} disabled={loginMutation.isPending}>
{loginMutation.isPending ? 'Logging in...' : 'Login'}
</button>
);
}Single Provider (BookingLab Only)
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BookingLabProvider, useBookings, useServices } from '@bookinglab/booking-journey-api';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<BookingLabProvider
baseUrl="https://api.bookinglab.com"
authToken="your-auth-token"
>
<Dashboard />
</BookingLabProvider>
</QueryClientProvider>
);
}
function Dashboard() {
const { data: bookings, isLoading } = useBookings();
const { data: services } = useServices();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h2>Bookings: {bookings?.length}</h2>
<h2>Services: {services?.length}</h2>
</div>
);
}Combined Provider (Both APIs)
import { ApiClientProvider, useJrniClient, useBookingLabClient } from '@bookinglab/booking-journey-api';
function App() {
return (
<ApiClientProvider
bookingLabBaseUrl="https://api.bookinglab.com"
jrniBaseUrl="https://api.jrni.com"
jrniConfig={{ appId: 'your-app-id', appKey: 'your-app-key' }}
authToken="your-auth-token"
>
<YourApp />
</ApiClientProvider>
);
}
function YourApp() {
const jrniClient = useJrniClient();
const bookingLabClient = useBookingLabClient();
// Use both clients as needed
}API Reference
Core Clients
createJrniClient(baseUrl, config)
Creates a new JRNI client instance.
const client = createJrniClient('https://api.jrni.com', {
appId: 'your-app-id',
appKey: 'your-app-key',
});Methods:
client.login(credentials)- Authenticate a userclient.getChildCompanies(companyId, params?)- Get child companies for a parent companyclient.getResources(companyId)- Get bookable resources for a companyclient.getServices(companyId)- Get services for a companyclient.createBasket(request)- Create a new basketclient.clearBaskets()- Clear all basketsclient.addServiceItem(basketId, serviceItem)- Add a service item to a basketclient.getTimes(companyId, params)- Get available booking times for a serviceclient.getQuestions(companyId, params)- Get questions for a detail groupclient.checkoutBasket(basketId, request)- Checkout a basket with client detailsclient.setAuthToken(token)- Set auth token for subsequent requests
createBookingLabClient(options)
Creates a new BookingLab client instance.
const client = createBookingLabClient({ baseUrl: 'https://api.bookinglab.com' });
client.setAuthToken('your-auth-token');Methods:
client.getBookings()- Get all bookingsclient.getBooking(id)- Get a specific bookingclient.createBooking(data)- Create a new bookingclient.updateBooking(id, data)- Update a bookingclient.cancelBooking(id)- Cancel a bookingclient.deleteBooking(id)- Delete a bookingclient.getServices()- Get all servicesclient.getService(id)- Get a specific serviceclient.setAuthToken(token)- Set auth token for subsequent requests
React Providers
JrniProvider
Provides JRNI client context to React components.
<JrniProvider
baseUrl="https://api.jrni.com"
config={{ appId: 'your-app-id', appKey: 'your-app-key' }}
>
{children}
</JrniProvider>BookingLabProvider
Provides BookingLab client context to React components.
<BookingLabProvider
baseUrl="https://api.bookinglab.com"
authToken="your-auth-token"
>
{children}
</BookingLabProvider>ApiClientProvider
Combined provider for applications using multiple API clients.
<ApiClientProvider
bookingLabBaseUrl="https://api.bookinglab.com"
jrniBaseUrl="https://api.jrni.com"
jrniConfig={{ appId: 'your-app-id', appKey: 'your-app-key' }}
authToken="your-auth-token"
>
{children}
</ApiClientProvider>React Hooks
JRNI Hooks
useLogin()- Hook for user authenticationuseChildCompanies(companyId, params?, enabled?)- Get child companiesuseResources(companyId, enabled?)- Get bookable resourcesuseServices(companyId, enabled?)- Get services for a companyuseCreateBasket()- Create a new basketuseClearBaskets()- Clear all basketsuseAddServiceItem(basketId)- Add a service item to a basketuseTimes(companyId, params, enabled?)- Get available booking times for a serviceuseQuestions(companyId, params, enabled?)- Get questions for a detail groupuseCheckoutBasket(basketId, authToken)- Checkout a basket with client detailsuseListBookings(companyId, memberId, params, authToken, enabled?)- Get member bookingsuseJrniClient()- Access the JRNI client directly
const loginMutation = useLogin();
loginMutation.mutate({ email: '[email protected]', password: 'password' });
const { data: childCompanies } = useChildCompanies(37000);
const { data: resources } = useResources(37000);
const { data: services } = useServices(37000);
const createBasketMutation = useCreateBasket();
createBasketMutation.mutate({ company_id: 37000 });
// With auth token for authenticated users:
// createBasketMutation.mutate({ company_id: 37000, authToken: 'user-auth-token' });
const clearBasketsMutation = useClearBaskets();
clearBasketsMutation.mutate();
// With auth token: clearBasketsMutation.mutate({ authToken: 'user-auth-token' });
const addServiceItemMutation = useAddServiceItem('basket-123');
addServiceItemMutation.mutate({
service_id: 1,
start: '2024-01-15T10:00:00Z',
questions: [{ id: '123', answer: 'John Doe' }, { id: '456', answer: '[email protected]' }]
});
// With auth token, pass as second argument to mutate
const { data: times } = useTimes(37000, { service_id: 48320, start_date: '2025-01-15' });
// times.times = [{ start: "2025-01-15T09:00:00+00:00", available: true, durations: [30, 60, 90] }, ...]
const { data: questions } = useQuestions(37000, { detail_group_id: 18529 });
// questions.questions = [{ id: 3, name: "Do you require equipment?", detail_type: "text_area", ... }]
const checkoutMutation = useCheckoutBasket('basket-123', 'user-auth-token');
checkoutMutation.mutate({
client: { id: 'client-456' },
take_from_wallet: false,
no_notifications: false,
reference: 'REF-001'
});
const { data: bookings } = useListBookings(
37001, // companyId
19, // memberId
{
start_date: '2026-01-15',
end_date: '2026-07-15',
include_cancelled: 'false'
},
'user-auth-token' // authToken (required)
);
// bookings._embedded.bookings = [{ id: 523, service_name: "...", datetime: "...", ... }]
const client = useJrniClient();BookingLab Hooks
useBookings()- Get all bookingsuseBooking(id)- Get a specific bookinguseCreateBooking()- Create a new bookinguseUpdateBooking()- Update a bookinguseCancelBooking()- Cancel a bookinguseServices()- Get all servicesuseService(id)- Get a specific serviceuseBookingLabClient()- Access the BookingLab client directly
const { data: bookings, isLoading } = useBookings();
const { data: services } = useServices();
const createBookingMutation = useCreateBooking();
createBookingMutation.mutate({
userId: 'user-123',
serviceId: 'service-456',
date: '2024-01-15T10:00:00Z',
});
const client = useBookingLabClient();Types
The library exports comprehensive TypeScript types:
import type {
// JRNI Types
LoginRequest,
LoginResponse,
ChildCompany,
ChildCompanyAddress,
ChildCompanySettings,
ChildCompaniesResponse,
GetChildCompaniesParams,
Resource,
ResourceLinks,
ResourcesResponse,
JrniService,
ServicesResponse,
Question,
QuestionsResponse,
Location,
LocationsResponse,
Vehicle,
VehiclesResponse,
JrniBooking,
MemberBookingsResponse,
AvailabilityTime,
AvailabilityTimesResponse,
ClearBasketsResponse,
AddServiceItemRequest,
AddServiceItemAssets,
ServiceItemResponse,
GetTimesParams,
TimeSlot,
TimesResponse,
GetQuestionsParams,
Question,
QuestionsResponse,
CreateBasketRequest,
CreateBasketResponse,
CheckoutBasketRequest,
CheckoutBasketResponse,
CheckoutBasketClient,
// BookingLab Types
Booking,
CreateBookingRequest,
Service,
} from '@bookinglab/booking-journey-api';Example Response: ServicesResponse
When calling useServices(companyId) or client.getServices(companyId), you'll receive a ServicesResponse:
{
total_entries: 2,
_embedded: {
services: [
{
id: 48320,
category_id: 1,
name: "Football 3G Astroturf Pitch Hire",
description: "Book a 3G Astroturf pitch",
durations: [60, 90, 120, 150, 180],
prices: [0, 0, 0, 0, 0],
detail_group_id: 18528,
listed_durations: [],
extra: {},
booking_time_step: 30,
can_refund_automatically: false,
is_event_group: false,
type: "service",
group_id: 1,
deleted: false,
queuing_disabled: true,
company_id: 37001,
min_advance_period: 172800, // seconds (2 days)
max_advance_period: 7776000, // seconds (90 days)
min_cancel_period: 172800, // seconds (2 days)
booking_type_public: "booking",
booking_type: 3,
mbooking_type: 5,
min_bookings: 1,
max_bookings: 1,
method_of_appointment: "In-person",
groups: [],
order: 48320,
child_level_service: false,
global_id: 48320,
availability: 0,
prices_in_major_units: [0.0, 0.0, 0.0, 0.0, 0.0],
combine_resource_and_staff: false,
disabled: false,
_links: {
self: { href: "https://api.jrni.com/api/v5/37001/services/48320" },
items: { href: "https://api.jrni.com/api/v5/37001/services/48320/items" }
}
}
]
},
_links: {
self: { href: "https://api.jrni.com/api/v5/37001/services" }
}
}JrniService Type Definition
interface JrniService {
id: number; // Unique service identifier
name: string; // Display name
description: string; // Service description
durations: number[]; // Available durations in minutes
prices: number[]; // Prices corresponding to durations (in minor units)
detail_group_id: number; // Associated detail group ID
listed_durations: any[]; // Publicly listed durations
extra: Record<string, any>; // Custom extra fields
booking_time_step: number; // Booking time interval in minutes
can_refund_automatically: boolean; // Auto-refund capability
is_event_group: boolean; // Whether this is an event group
type: string; // Service type (e.g., "service")
group_id: number | null; // Parent group ID
deleted: boolean; // Soft delete flag
queuing_disabled: boolean; // Queue functionality flag
company_id: number; // Owning company ID
min_advance_period: number; // Minimum advance booking time (seconds)
max_advance_period: number; // Maximum advance booking time (seconds)
min_cancel_period: number; // Minimum cancellation notice (seconds)
booking_type_public: string; // Public booking type label
booking_type: number; // Internal booking type code
mbooking_type: number; // Mobile booking type code
min_bookings: number; // Minimum bookings required
max_bookings: number; // Maximum bookings allowed
method_of_appointment: string; // Appointment method (e.g., "In-person")
groups: any[]; // Associated groups
order: number; // Display order
child_level_service: boolean; // Child company service flag
global_id: number; // Global service identifier
availability: number; // Availability status
prices_in_major_units: number[]; // Prices in major currency units
combine_resource_and_staff: boolean; // Resource/staff combination flag
disabled: boolean; // Service disabled flag
_links: Record<string, any>; // HAL links
}License
MIT
