Subscriptions and Streaming
On rare occations, some API operations can take up to 30 seconds to complete. Instead of showing a blank loading screen, the API streams results as they become available. This lets you build better user experiences with progressive updates and real-time feedback.
Why Streaming Matters
Train operators sometimes suffer from long loading times. When you request pricing or create a booking, the API needs to:
- Contact multiple train operators
- Check real-time availability
- Calculate prices
- Pre-book tickets
This can take up to 30 seconds, too long for users to stare at a loading spinner. Streaming solves this by sending data as soon as it's available.
How Streaming Works
The API supports streaming in two ways:
- WebSocket connections: Keep a connection open and receive response without worrying about conneciton timeout
- GraphQL subscriptions: Subscribe to specific queries and receive incremental updates
When to Use Streaming
Use WebSockets or subscriptions for operations that can be slow:
- Finding journeys: Itineraries are streamed as the routing engine finds the optimal route
- Getting journey offers: Prices stream in as each segment is priced
- Creating bookings, payments, or orders: These operations do not stream updates, but doing them over WebSockets is a good way to avoid the risk of connection timeout
For fast operations (like fetching passes), regular HTTP requests are fine.
Using Subscriptions
Subscriptions are GraphQL queries that return multiple results over time. The most common use case is getting journey offers, where prices arrive incrementally.
When you subscribe to getJourneyOffer, you receive updates as:
- Journey segments are found
- Prices are calculated for each segment
- The complete offer is assembled
Each update includes a status field showing what's complete and what's still loading.
Example: Subscribe to journey offers
js
import { createClient } from 'graphql-ws'
const client = createClient({
url: 'wss://api-gateway.allaboard.eu',
connectionParams: {
'api-key': 'AGENT_API_KEY'
}
})
const query = `
subscription GetJourneyOffer(
$journey: ID!
$passengers: [PassengerPlaceholderInput!]!
) {
getJourneyOffer(journey: $journey, passengers: $passengers) {
status
itinerary {
__typename
... on SegmentCollection {
status
segments {
departureAt
origin {
name
countryCode
}
}
offers {
price {
amount
currency
}
parts {
__typename
... on AdmissionPart {
flexibility
serviceClass
comfortClass
}
... on ReservationPart {
flexibility
comfortClass
accommodation {
type
}
}
}
}
}
... on Stopover {
location {
name
countryCode
}
}
}
}
}
`
const variables = {
journey: 'e18aec5a-7986-4d89-b381-f0a7f283d41e',
passengers: [{ type: 'ADULT' }]
}
const subscription = client.iterate({ query, variables })
for await (const data of subscription) {
console.log(data)
// Update your UI with the latest data
}import { createClient } from 'graphql-ws'
const client = createClient({
url: 'wss://api-gateway.allaboard.eu',
connectionParams: {
'api-key': 'AGENT_API_KEY'
}
})
const query = `
subscription GetJourneyOffer(
$journey: ID!
$passengers: [PassengerPlaceholderInput!]!
) {
getJourneyOffer(journey: $journey, passengers: $passengers) {
status
itinerary {
__typename
... on SegmentCollection {
status
segments {
departureAt
origin {
name
countryCode
}
}
offers {
price {
amount
currency
}
parts {
__typename
... on AdmissionPart {
flexibility
serviceClass
comfortClass
}
... on ReservationPart {
flexibility
comfortClass
accommodation {
type
}
}
}
}
}
... on Stopover {
location {
name
countryCode
}
}
}
}
}
`
const variables = {
journey: 'ba47500e-2e11-4487-8bae-cfc1d98714bd',
passengers: [{ type: 'ADULT' }]
}
const subscription = client.iterate({ query, variables })
for await (const data of subscription) {
console.log(data)
// Update your UI with the latest data
}Understanding Status Updates
As data streams in, you'll see status updates that tell you what's happening:
First message - Journey structure is known, but prices aren't ready yet:
json
{
"status": "LOADING",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "LOADING",
"segments": [
{
"departureAt": "2023-08-01T09:42:00",
"origin": { "name": "Amsterdam", "countryCode": "NL" }
}
]
}
]
}{
"status": "LOADING",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "LOADING",
"segments": [
{
"departureAt": "2023-08-01T09:42:00",
"origin": { "name": "Amsterdam", "countryCode": "NL" }
}
]
}
]
}Second message - First segment is priced, others still loading:
json
{
"status": "LOADING",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [
{
"price": { "amount": 13400, "currency": "EUR" },
"parts": [...]
}
]
},
{
"__typename": "SegmentCollection",
"status": "LOADING",
"segments": [...]
}
]
}{
"status": "LOADING",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [
{
"price": { "amount": 13400, "currency": "EUR" },
"parts": [...]
}
]
},
{
"__typename": "SegmentCollection",
"status": "LOADING",
"segments": [...]
}
]
}Final message - All segments priced, complete offer ready:
json
{
"status": "SUCCESS",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [...]
},
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [...]
}
]
}{
"status": "SUCCESS",
"itinerary": [
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [...]
},
{
"__typename": "SegmentCollection",
"status": "SUCCESS",
"segments": [...],
"offers": [...]
}
]
}Using WebSockets for Mutations
Even if you're not using subscriptions, use WebSocket connections for slow mutations to avoid timeouts:
createBooking- Requires verifying availability and pricescreateOrder- Pre-books ticketscreatePayment- Includes order creation and setting up payment
Depending on your server set-up, regular HTTP requests may timeout on these operations. WebSocket connections handle long-running requests better.
The All Aboard API does not impose any connection time limits. As long as the client remains connected, any regular HTTP POST or GET request will continue processing until a result is returned.
Best Practices
Show progress: Use status updates to show what's happening ("Checking prices...", "Booking tickets...")
Display partial results: Show offers as they arrive instead of waiting for everything
Handle errors gracefully: If a segment fails, show which parts succeeded and which didn't
Use WebSockets for slow operations: Even without subscriptions, WebSockets prevent timeouts
Provide feedback: Let users know the operation is in progress, especially for long-running operations
Next Steps
- See Getting Offers for details on using subscriptions for pricing
- See Booking Tickets for WebSocket usage in bookings
- Check the GraphQL WS documentation for client library setup