← All @molecule/* packages · App templates
@molecule/api-hotelsCore interface · hotels · API (Node) · v1.0.1 · Apache-2.0
Provider-agnostic hotel search/offer/book core interface.
npm install @molecule/api-hotels@molecule/api-hotels is the hotels core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-hotels-amadeus.
import { setProvider, searchHotels, getHotelOffers } from '@molecule/api-hotels'
import { provider as amadeus } from '@molecule/api-hotels-amadeus'
setProvider(amadeus)
const hits = await searchHotels({
cityCode: 'PAR',
checkInDate: '2026-06-01',
checkOutDate: '2026-06-04',
adults: 2,
})
const offers = await getHotelOffers(hits[0].hotelId, {
checkInDate: '2026-06-01',
checkOutDate: '2026-06-04',
adults: 2,
})Providers (1): @molecule/api-hotels-amadeus
Works with: @molecule/api-bond, @molecule/api-i18n
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Provider-agnostic hotels interface for molecule.dev.
Defines the HotelsProvider interface for hotel inventory aggregators —
search, priced offers, and booking confirmation. Bond packages (Amadeus,
Booking.com Affiliate, Expedia Rapid, etc.) implement this interface.
Application code uses the convenience functions (searchHotels,
getHotelOffers, bookHotel) which delegate to the bonded provider.
Prices carry an explicit ISO 4217 currency so multi-market callers can reconcile across providers. Hotel and offer IDs are kept as plain strings so providers can use whatever opaque catalogue identifier they expose.
import { setProvider, searchHotels, getHotelOffers } from '@molecule/api-hotels'
import { provider as amadeus } from '@molecule/api-hotels-amadeus'
setProvider(amadeus)
const hits = await searchHotels({
cityCode: 'PAR',
checkInDate: '2026-06-01',
checkOutDate: '2026-06-04',
adults: 2,
})
const offers = await getHotelOffers(hits[0].hotelId, {
checkInDate: '2026-06-01',
checkOutDate: '2026-06-04',
adults: 2,
})
core
npm install @molecule/api-hotels @molecule/api-bond @molecule/api-i18n
GeoLocationGeographic point used for radius-based hotel search.
interface GeoLocation {
/**
* Latitude in decimal degrees, WGS-84.
*/
lat: number
/**
* Longitude in decimal degrees, WGS-84.
*/
lon: number
/**
* Search radius around the point. Units are provider-defined but should
* default to kilometres if not otherwise specified by the provider.
* Optional — providers MAY apply a sensible default (e.g. 5 km).
*/
radius?: number
}
HotelAddressGeocoded address block returned with hotel search hits.
interface HotelAddress {
/**
* ISO 3166-1 alpha-2 country code (e.g. `'US'`, `'FR'`), if known.
*/
countryCode?: string
/**
* Free-form city name as the provider returns it (e.g. `'Paris'`).
*/
cityName?: string
/**
* Free-form street / locality line, if the provider exposes one.
*/
line?: string
/**
* Postal / ZIP code, if known.
*/
postalCode?: string
}
HotelBookingConfirmation record returned after a successful booking.
Note: many hotel providers only support "price the offer" + "redirect
the guest to the provider's checkout" flows rather than direct
bookings via API. Implementations that cannot complete a booking
server-side MAY surface that as an error and expose a separate
priceOffer flow; in that case {@link HotelsProvider.bookHotel} can
throw with a cause.code === 'BOOKING_NOT_SUPPORTED'.
interface HotelBooking {
/**
* Provider-issued booking / reservation identifier.
*/
bookingId: string
/**
* Hotel that was booked.
*/
hotelId: HotelId
/**
* Offer that was confirmed.
*/
offerId: HotelOfferId
/**
* Final price charged.
*/
price: HotelPrice
/**
* Check-in date as `YYYY-MM-DD`.
*/
checkInDate: IsoDate
/**
* Check-out date as `YYYY-MM-DD`.
*/
checkOutDate: IsoDate
/**
* Lead guest information echoed back from the booking call.
*/
guest: HotelGuestInfo
/**
* Provider-issued confirmation / itinerary number, if distinct from
* {@link bookingId}.
*/
confirmationNumber?: string
}
HotelGuestInfoGuest information supplied to a booking call. Kept intentionally minimal — providers that require additional fields (loyalty number, etc.) can extend via interface merging in their own bond package.
interface HotelGuestInfo {
/**
* Guest first / given name.
*/
firstName: string
/**
* Guest last / family name.
*/
lastName: string
/**
* Contact email for the booking confirmation.
*/
email: string
/**
* Optional contact phone (E.164 format recommended).
*/
phone?: string
}
HotelOfferA single bookable room / rate combination for a specific hotel.
Offers are short-lived: the {@link offerId} is provider-issued and typically expires within minutes. Consumers should re-fetch offers immediately before initiating a booking flow.
interface HotelOffer {
/**
* Opaque, provider-issued offer identifier. Pass to
* {@link HotelsProvider.bookHotel} (or its equivalent) to confirm.
*/
offerId: HotelOfferId
/**
* Hotel this offer belongs to.
*/
hotelId: HotelId
/**
* Check-in date as `YYYY-MM-DD`.
*/
checkInDate: IsoDate
/**
* Check-out date as `YYYY-MM-DD`.
*/
checkOutDate: IsoDate
/**
* Total price for the entire stay (all nights).
*/
price: HotelPrice
/**
* Free-form room name / type description (e.g. `'Deluxe King Room'`).
* Optional — not every provider exposes one.
*/
roomDescription?: string
/**
* Number of adults the offer is priced for.
*/
adults?: number
/**
* Cancellation / refundability hint. `true` = explicitly refundable,
* `false` = explicitly non-refundable, `undefined` = unknown.
*/
refundable?: boolean
/**
* Rate code / class identifier (e.g. `'BAR'`, `'AAA'`), if exposed.
*/
rateCode?: string
}
HotelOffersCriteriaFilter / refinement parameters when fetching priced offers for a specific hotel.
interface HotelOffersCriteria {
/**
* Check-in date as `YYYY-MM-DD`.
*/
checkInDate: IsoDate
/**
* Check-out date as `YYYY-MM-DD`.
*/
checkOutDate: IsoDate
/**
* Number of adult guests per room. Defaults to `1` when omitted.
*/
adults?: number
}
HotelPriceMonetary price block. Always carries an explicit currency.
interface HotelPrice {
/**
* Total amount in major units of {@link currency} (e.g. dollars, not
* cents).
*/
total: number
/**
* ISO 4217 currency code the {@link total} is denominated in.
*/
currency: CurrencyCode
}
HotelSearchCriteriaSearch criteria for {@link HotelsProvider.searchHotels}.
Either {@link cityCode} or {@link location} MUST be supplied (callers that supply both leave provider-specific behaviour up to the implementation, which SHOULD prefer {@link location} when both are present).
interface HotelSearchCriteria {
/**
* IATA / city code to search within. Mutually exclusive with
* {@link location} in the typical case.
*/
cityCode?: CityCode
/**
* Geographic point + optional radius. Mutually exclusive with
* {@link cityCode} in the typical case.
*/
location?: GeoLocation
/**
* Check-in date as `YYYY-MM-DD`.
*/
checkInDate: IsoDate
/**
* Check-out date as `YYYY-MM-DD`. MUST be strictly after
* {@link checkInDate}.
*/
checkOutDate: IsoDate
/**
* Number of adult guests per room. Defaults to `1` when omitted.
* Providers MAY clamp very large values.
*/
adults?: number
/**
* Number of rooms required. Defaults to `1` when omitted.
*/
rooms?: number
/**
* Optional star-rating filter (e.g. `[4, 5]` to only return 4- and
* 5-star properties). Providers without rating data MAY ignore this.
*/
ratings?: number[]
}
HotelSearchResultA single hotel returned by {@link HotelsProvider.searchHotels}.
Search hits are summary records — they identify the property and may include a representative price snippet, but consumers should call {@link HotelsProvider.getHotelOffers} to enumerate bookable rooms / rates.
interface HotelSearchResult {
/**
* Provider-specific hotel identifier (opaque to callers).
*/
hotelId: HotelId
/**
* Human-readable hotel name (e.g. `'Hotel de Paris'`).
*/
name: string
/**
* IATA / city code the hotel is located in, if known.
*/
cityCode?: CityCode
/**
* Star rating as an integer (1..5), if the provider exposes it.
*/
rating?: number
/**
* Latitude in decimal degrees, if the provider exposes geocoordinates.
*/
latitude?: number
/**
* Longitude in decimal degrees, if the provider exposes geocoordinates.
*/
longitude?: number
/**
* Geocoded address block, if the provider exposes one.
*/
address?: HotelAddress
/**
* Distance from the search anchor (city centre or {@link GeoLocation}),
* if the provider returns one. Units are provider-defined.
*/
distance?: number
/**
* "From" / starting price as a snippet so list views can render a price
* without a second round-trip. Optional — not every provider returns a
* price in the search response.
*/
fromPrice?: HotelPrice
}
HotelsProviderHotel inventory aggregator provider interface.
All providers (Amadeus, Booking.com Affiliate, Expedia Rapid, fixtures, etc.) implement this interface. The interface is deliberately minimal so providers with very different upstream APIs can satisfy it identically.
interface HotelsProvider {
/**
* Searches available hotels matching {@link criteria}.
*
* @param criteria - Search criteria (location + dates + room demand).
* @returns Array of {@link HotelSearchResult}, possibly empty.
*/
searchHotels(criteria: HotelSearchCriteria): Promise<HotelSearchResult[]>
/**
* Returns priced offers (room / rate combinations) for {@link hotelId}.
* Offers are short-lived and should be re-fetched immediately before a
* booking flow.
*
* @param hotelId - Provider-issued hotel identifier.
* @param criteria - Date + occupancy filter.
* @returns Array of {@link HotelOffer}, possibly empty.
*/
getHotelOffers(hotelId: HotelId, criteria: HotelOffersCriteria): Promise<HotelOffer[]>
/**
* Confirms a booking against {@link offerId} for {@link guestInfo}.
*
* Providers that only support priced offers + provider-hosted checkout
* (rather than direct API booking) SHOULD throw with `cause.code ===
* 'BOOKING_NOT_SUPPORTED'`; callers can detect that and fall back to
* the provider's redirect flow.
*
* @param offerId - Offer to confirm. Must be fresh (not expired).
* @param guestInfo - Lead guest contact / identity details.
* @returns Confirmed {@link HotelBooking} record.
*/
bookHotel(offerId: HotelOfferId, guestInfo: HotelGuestInfo): Promise<HotelBooking>
}
CityCodeIATA / city code (e.g. 'PAR', 'NYC', 'LON'). Plain string —
providers differ on which catalogues they expose.
type CityCode = string
CurrencyCodeISO 4217 three-letter currency code (e.g. 'USD', 'EUR').
type CurrencyCode = string
HotelIdProvider-specific identifier for a hotel property (e.g. an Amadeus
hotelId such as 'MCLONGHM'). Kept as a plain string so providers
can use whatever opaque catalogue ID they expose.
type HotelId = string
HotelOfferIdProvider-specific identifier for a priced hotel offer (room / rate combination). Offers are typically short-lived; consumers should treat IDs as opaque and refresh them via {@link HotelsProvider.getHotelOffers} before booking.
type HotelOfferId = string
IsoDateCalendar date in ISO YYYY-MM-DD form (no time, no timezone). Hotel
inventory is typically priced per night, not per minute, so a plain
date string is the canonical contract.
type IsoDate = string
bookHotel(offerId, guestInfo)Confirms a hotel booking against {@link offerId} using the bonded provider.
Providers that only support priced offers + provider-hosted checkout
MAY throw with cause.code === 'BOOKING_NOT_SUPPORTED'; callers can
detect that and fall back to a redirect flow.
function bookHotel(offerId: string, guestInfo: HotelGuestInfo): Promise<HotelBooking>
offerId — Offer to confirm. Must be fresh (not expired).guestInfo — Lead guest contact / identity details.Returns: Confirmed {@link HotelBooking} record.
getHotelOffers(hotelId, criteria)Returns priced offers for {@link hotelId} using the bonded provider. Offers are short-lived and should be re-fetched immediately before a booking flow.
function getHotelOffers(hotelId: string, criteria: HotelOffersCriteria): Promise<HotelOffer[]>
hotelId — Provider-issued hotel identifier.criteria — Date + occupancy filter.Returns: Array of {@link HotelOffer}, possibly empty.
getProvider()Retrieves the bonded hotels provider, throwing if none is configured.
function getProvider(): HotelsProvider
Returns: The bonded hotels provider.
hasProvider()Checks whether a hotels provider is currently bonded.
function hasProvider(): boolean
Returns: true if a hotels provider is bonded.
searchHotels(criteria)Searches available hotels matching {@link criteria} using the bonded provider.
function searchHotels(criteria: HotelSearchCriteria): Promise<HotelSearchResult[]>
criteria — Search criteria (location + dates + room demand).Returns: Array of {@link HotelSearchResult}, possibly empty.
setProvider(provider)Registers a hotels provider as the active singleton. Called by bond
packages (e.g. @molecule/api-hotels-amadeus) during application
startup.
function setProvider(provider: HotelsProvider): void
provider — The hotels provider implementation to bond.| Provider | Package |
|---|---|
| Hotels (Amadeus) | @molecule/api-hotels-amadeus |
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-bond
@molecule/api-i18n
Not every provider can complete a booking server-side. Many hotel APIs
only support search + priced offers and hand the guest to the provider's
own checkout. Such bonds throw from bookHotel with
cause.code === 'BOOKING_NOT_SUPPORTED' — handle that path (link out /
deep-link to the provider) instead of assuming an in-app booking form works
for every bond.
Offers are short-lived quotes, not reservations. An offerId from
getHotelOffers expires; book promptly after selection, and on a booking
failure re-fetch offers and re-confirm the price with the user — never
retry a stale offer id or present a cached price as bookable.
Booking is a real-money, PII-bearing call: keep it SERVER-SIDE behind an
authenticated endpoint (provider API keys live in the bond's config, never
in app code), validate guestInfo server-side, and persist the returned
HotelBooking.bookingId before reporting success to the user.
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
searchHotels with a cityCode or location) returns REAL
HotelSearchResults rendered in the UI — each with a name, its city /
address, and a fromPrice for the stay — never an empty list, a stuck
spinner, or placeholder cards. Results match the query: the right city, and
the dates / occupancy you entered are reflected in the prices shown.rating, amenities) actually
narrows / reorders the rendered list — e.g. a price sort puts the lowest
HotelPrice.total first; a 4–5 star filter drops lower-rated properties.checkOutDate not strictly after checkInDate) shows a visible "no
availability" empty state — never a crash, a blank screen, or fabricated
results.HotelOffer.price.total equals nights × nightly rate + any fees, in its
ISO 4217 HotelPrice.currency (no bare "123" with no symbol or code).getHotelOffers and shows its real rooms / rates (roomDescription +
price); selecting one records the chosen offerId in the app. Booking
itself goes out-of-band to the vendor (or bookHotel throws
BOOKING_NOT_SUPPORTED → a redirect) — verify the app's RECORDED
selection, not a fake in-app confirmation.