← All @molecule/* packages · App templates

@molecule/api-hotels-amadeus

Provider bond · hotels · API (Node) · v1.0.1 · Apache-2.0

Amadeus self-service Hotel Search API provider bond.

npm install @molecule/api-hotels-amadeus

npm · Source on GitHub · Implements @molecule/api-hotels

How it works

@molecule/api-hotels-amadeus is a provider bond on the API (Node) side: it implements the hotels core interface (@molecule/api-hotels) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'

setProvider(provider)

Works with: @molecule/api-hotels, @molecule/api-secrets

Secrets: AMADEUS_CLIENT_ID, AMADEUS_CLIENT_SECRET

Reference

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.ts JSDoc, not this file.

Amadeus hotels provider for molecule.dev.

Implements the HotelsProvider interface against the Amadeus Self-Service hotels APIs. Provides hotel search (city or geo), priced offer lookup (/v3/shopping/hotel-offers), and a booking stub that explicitly surfaces "use the hosted checkout flow" (Amadeus's direct hotel-booking endpoint requires PCI-compliant card capture and is not safely callable from a generic bond).

Authentication uses Amadeus's OAuth2 client-credentials flow, with the same AMADEUS_CLIENT_ID / AMADEUS_CLIENT_SECRET env vars shared with @molecule/api-flights-amadeus. The bond mints and caches tokens per-provider-instance.

The OAuth secret NEVER appears in error messages. URLs do not carry authentication in the query string — the bearer token is sent via the Authorization header — so URL-redaction is unnecessary.

Quick Start

import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'

setProvider(provider)

Type

provider

Installation

npm install @molecule/api-hotels-amadeus @molecule/api-hotels @molecule/api-secrets

API

Interfaces

AmadeusHotelsConfig

Configuration options for the Amadeus hotels provider.

Amadeus exposes a single OAuth2 client-credentials flow that is shared across all of its product APIs (flights, hotels, points-of-interest, etc.). The same AMADEUS_CLIENT_ID / AMADEUS_CLIENT_SECRET env vars therefore drive both @molecule/api-flights-amadeus and @molecule/api-hotels-amadeus. Token caching is per-provider-instance — there is no cross-bond shared cache, but the OAuth pattern is identical so credentials work interchangeably.

Amadeus exposes a free Self-Service "test" environment at https://test.api.amadeus.com (sandbox data, generous rate limits) and a paid production environment at https://api.amadeus.com. This bond defaults to the TEST host — identical to @molecule/api-flights-amadeus — so flights + hotels wired with one key pair hit the same host and a token minted on one is accepted by the other. Set {@link useProduction} (or the AMADEUS_USE_PRODUCTION=true env var, which both bonds honor) to switch; a single env setting flips both together.

interface AmadeusHotelsConfig {
  /**
   * OAuth2 client ID, sent as the `client_id` form field when minting a
   * fresh access token.
   *
   * If omitted, the provider falls back to the `AMADEUS_CLIENT_ID`
   * environment variable. Requests fail with a sanitized error if
   * neither is set.
   */
  clientId?: string

  /**
   * OAuth2 client secret, sent as the `client_secret` form field when
   * minting a fresh access token.
   *
   * If omitted, the provider falls back to the `AMADEUS_CLIENT_SECRET`
   * environment variable. The secret is NEVER included in error
   * messages — token-mint failures redact it before bubbling up.
   */
  clientSecret?: string

  /**
   * When `true`, routes requests to the production endpoint
   * (`https://api.amadeus.com`). When `false` or omitted, uses the
   * Self-Service test sandbox (`https://test.api.amadeus.com`). Falls back
   * to the `AMADEUS_USE_PRODUCTION=true` env var, which
   * `@molecule/api-flights-amadeus` reads too — so one env setting flips
   * both bonds to production together.
   */
  useProduction?: boolean

  /**
   * Base URL override. Takes precedence over {@link useProduction}. When
   * neither is set, defaults to the Self-Service test sandbox
   * (`'https://test.api.amadeus.com'`). Pass `'https://api.amadeus.com'`
   * for production, or any proxy URL for self-hosted setups.
   */
  baseUrl?: string

  /**
   * Request timeout in milliseconds for both the token-mint call and the
   * data-API calls. Defaults to `10000`.
   */
  timeout?: number

  /**
   * Number of seconds to subtract from the upstream `expires_in` value
   * before treating a cached token as stale. Defaults to `30`. Lower
   * values reduce wasted token mints; higher values reduce the chance
   * of using a token that expires mid-request.
   */
  tokenSkewSeconds?: number
}

Functions

createProvider(config)

Creates an Amadeus hotels provider.

function createProvider(config?: AmadeusHotelsConfig): HotelsProvider
  • config — Provider configuration. Credentials may be supplied here directly or via the AMADEUS_CLIENT_ID and AMADEUS_CLIENT_SECRET environment variables. Defaults to the Self-Service TEST host; set useProduction (or AMADEUS_USE_PRODUCTION) to route to production.

Returns: A {@link HotelsProvider} backed by Amadeus.

sanitizeErrorMessage(message)

Returns a sanitized copy of an error message body. Currently a no-op passthrough for the common "errors[].detail" shape — exposed for symmetry with the flights bond and to keep the redaction surface documented in one place.

function sanitizeErrorMessage(message: string): string
  • message — A free-form upstream error message.

Returns: The same message, with any future-redacted patterns scrubbed.

Constants

BOOKING_NOT_SUPPORTED

Error code raised when {@link HotelsProvider.bookHotel} is called. Amadeus's hotel-booking endpoint requires PCI-compliant card capture and is not safely callable from a generic backend bond — callers should use Amadeus's hosted checkout / "price the offer" flow instead.

const BOOKING_NOT_SUPPORTED: 'BOOKING_NOT_SUPPORTED'

hotelsAmadeusSecretDefinitions

Secret definitions required by the Amadeus hotels bond.

const hotelsAmadeusSecretDefinitions: SecretDefinition[]

MISSING_CREDENTIALS

Error code raised when a hotels-provider call is attempted with no AMADEUS_CLIENT_ID and/or AMADEUS_CLIENT_SECRET configured (neither via the config object nor via environment variables). Surfaced via Error.cause so callers can distinguish it from upstream errors.

const MISSING_CREDENTIALS: 'MISSING_CREDENTIALS'

provider

The default provider implementation, lazily initialized on first use.

Reads AMADEUS_CLIENT_ID, AMADEUS_CLIENT_SECRET, AMADEUS_USE_PRODUCTION, and AMADEUS_BASE_URL from environment variables — the same set @molecule/api-flights-amadeus reads, so a single AMADEUS_USE_PRODUCTION=true flips BOTH bonds to production together. Defaults to the Self-Service TEST host. Use {@link createProvider} directly if you need to supply configuration programmatically.

const provider: HotelsProvider

TOKEN_MINT_FAILED

Error code raised when the OAuth2 token-mint call fails (e.g. invalid credentials, network error, non-2xx status). The error message NEVER includes the raw client_secret.

const TOKEN_MINT_FAILED: 'TOKEN_MINT_FAILED'

UPSTREAM_ERROR

Error code raised when an Amadeus hotels data API call fails (e.g. a non-2xx HTTP status, a structured errors[] body, etc.).

const UPSTREAM_ERROR: 'UPSTREAM_ERROR'

Core Interface

Implements @molecule/api-hotels interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/api-hotels'
import { provider } from '@molecule/api-hotels-amadeus'

export function setupHotelsAmadeus(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-hotels ^1.0.1
  • @molecule/api-secrets ^1.0.1

Environment Variables

Runtime Dependencies

  • @molecule/api-hotels

  • @molecule/api-secrets

  • Defaults to the TEST sandbox host (test.api.amadeus.com) — identical to @molecule/api-flights-amadeus. Amadeus issues Self-Service TEST keys first (production needs approval), and a token is host-specific, so the safe default is TEST. Set AMADEUS_USE_PRODUCTION=true (or useProduction/baseUrl on createProvider()) to route to production. Because BOTH the flights and hotels bonds read the same AMADEUS_USE_PRODUCTION env var, one setting flips them together — so a travel app wiring flights + hotels with one key pair never has one bond 401ing on the wrong host.

  • bookHotel() ALWAYS throws (see the core's remarks) — implement checkout on the vendor's hosted flow; search and priced offers are fully supported.

E2E Tests

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:

  • A search for a real location + check-in/check-out dates + guest count (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.
  • Any exposed filter or sort (price, star 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.
  • Availability is respected: a sold-out or invalid-date search (e.g. checkOutDate not strictly after checkInDate) shows a visible "no availability" empty state — never a crash, a blank screen, or fabricated results.
  • Prices total correctly and every amount shows its currency: a shown HotelOffer.price.total equals nights × nightly rate + any fees, in its ISO 4217 HotelPrice.currency (no bare "123" with no symbol or code).
  • If hotel detail / booking is exposed, opening a hotel calls 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.
  • A provider error (upstream down / rate-limited) surfaces as a graceful, visible message — not a blank page or an unhandled rejection — and the provider API key stays server-side: search / offers / booking all run on the server, and no key appears in network responses or page source.