Was this helpful?

Managing a Trip

The Alpaca Travel GraphQL API is a powerful tool for developers to create and share interactive travel itineraries with their customers. This developer reference guide will show you how to use the API to create, manipulate, and display itineraries, as well as other related functionality.

What this guide covers:

  • This reference guide covers the complete lifecycle of creating and managing itineraries using the Alpaca Travel GraphQL API.

  • It includes step-by-step instructions for common operations such as creating itineraries, adding locations, working with routing and other services.

  • The reference covers both GraphQL queries and mutations, providing developers with the complete set of tools to create and manage itineraries with the Alpaca Travel platform.

This article is intended to be a simple guide that developers can reference when they are first getting started with the Alpaca Travel and are working on their projects, so they can easily understand the different GraphQL operations they need to create an itinerary and manage it. The guide provides a clear and step by step instructions, code examples and other resources that could help developers to be more productive and efficient.

What this guide does not cover:

  • Numerous alternative use cases; This is provided to give a developer an overview of calling the GraphQL API but there are numerous other operations and use cases covered outside of this guide.

  • This guide does not cover how to make API calls or connect your application to the GraphQL API using a specific client. It is technology agnostic and allows developers to use any GraphQL client to make the calls.

  • It does not cover how to obtain or use an API key to access the Alpaca Travel GraphQL API. Developers must have a valid API key to make calls to the API.

  • It does not cover implementation details such as handling errors or rate limiting, which are important but are out of scope of this guide.

  • It does not cover all the possible options and variations of the GraphQL operations, but it covers the most common and useful ones for managing itineraries.

Additional Resources

  • Alpaca Travel GraphQL API Detailed Schema Reference

  • Apollo Sandbox for testing queries and seeing what other operations/fields are available

Creating an Itinerary

The first step in creating an itinerary is to use the "createItinerary" mutation. This operation allows you to create an itinerary with some initial content, such as a title.

Here is an example of how to use the "createItinerary" mutation:

1mutation CreateItinerary {
2  createItinerary(
3    itinerary: {
4      # Initial content
5      title: "Basic Itinerary Example"
6      # Some additional recommended but not required fields
7      defaultLocale: "en"
8      attrs: [
9        # Provide hints about the type of itinerary (e.g. list, trip, trail)
10        # Can be omitted or added later
11        { id: "itinerary/type", value: "trip" }
12      ]
13    }
14  ) {
15    itinerary {
16      id
17    }
18  }
19}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return the id of the created itinerary. This id is required for all subsequent queries or mutations that you will make to the itinerary.

Example Successful response:

1{
2  "data": {
3    "createItinerary": {
4      "itinerary": {
5        "id": "itinerary/123"
6      }
7    }
8  }
9}

It is important to note that the ID's returned are not a real id and it is just an example, your response will have unique id.

  • You can also add additional fields such as description and cover images at the time of creation

  • Additional attributes that can store ID's or other references can be stored

  • Can be associated to a specific profile ID if correct permissions are in place

Adding Locations

Once you have created an itinerary, you can start adding locations to it using the "createItineraryLocation" mutation.

The following mutation adds a simple place to the itinerary.

1mutation CreateItineraryLocation {
2  createItineraryLocation(
3    # Supply our Itinerary to add the item to
4    itineraryId: "itinerary/ABC123"
5    # Describe the location
6    location: {
7      # Provide some optional content to personalise the itinerary
8      title: "A Cafe"
9      place: {
10        # Providing the position lon/lat
11        position: { lon: 145.0043, lat: -37.8021 }
12      }
13    }
14  ) {
15    # Read back the location just created
16    location {
17      __typename
18      id
19    }
20  }
21}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return the id of the created location, as well as the **typename. The id can be used for queries and mutations related to that specific location, and the **typename can be used to determine the type of object returned.

Example Successful response:

1{
2  "data": {
3    "createItineraryLocation": {
4      "location": {
5        "__typename": "ItineraryLocation",
6        "id": "itinerary/ABC123/location/DEF456"
7      }
8    }
9  }
10}

You can also provide various information to store against your place.

  • Place information embedded within the location

  • Reference to a place provider where information can be kept up to date such as ATDW, Facebook, etc.

  • Provide an 'external ref' attribute that can store the ID of a place within your platform that can be used to map on information from your platform

Adding a Location from a Place Provider

Alternatively, you can associate the created location with a specific place provided by various supported place provider (such as ATDW, Facebook etc). The benefit to associating a created location with a place provider is that when you query the itinerary, you can also load in information such as opening hours that are kept up to date based on the place provider information.

This operation allows you to associate a place to a location in the itinerary. In this example, we will be linking the location to The Farm Cafe using the ATDW Product identifier.

1mutation CreateItineraryLocationWithAtdwPlace {
2  createItineraryLocation(
3    # Supply our Itinerary to add the item to
4    itineraryId: "itinerary/ABC123"
5    # Describe the location
6    location: {
7      # Provide some optional content to personalise the itinerary
8      title: "Grab a coffee"
9      synopsis: "Nearby, we can find The Farm Cafe open most days"
10      # Link the location to a known place
11      place: {
12        # Referencing the place from ATDW
13        id: "place/atdw:product:5f115dfde8f9b57738878350"
14        # Providing the position lon/lat
15        position: { lon: 145.0043, lat: -37.8021 }
16      }
17    }
18  ) {
19    # Read back the location just created
20    location {
21      __typename
22      id
23    }
24  }
25}

Sandbox: Configure | Try Operation

The created location will be associated with the ATDW product "5f115dfde8f9b57738878350" which is "The Farm Cafe". When you query the itinerary you are able to build queries that can query various place information such as addresses, categories, photos and more as provided by the provider.

Testing Whether a Place is Present in an Itinerary

Before adding a location to an itinerary, you may wish to first check whether a place is already present within the itinerary. This can be useful for creating a button state on an "Add to Itinerary" button. If a result is returned, you can indicate to the user that the place is already present in their itinerary, or offer them the option to remove it.

You can use the "itinerary" query to check whether a place has been added to an itinerary, and obtain the matching itinerary location ID. This can then be used with the "deleteItineraryItem" operation to remove the location from the itinerary.

Here is an example of how to use the "itinerary" query to check whether a place is present in an itinerary:

1query CheckItineraryPlacePresent {
2  itinerary(id: "itinerary/ABC123") {
3    descendants(
4      placeIds: ["place/atdw:product:5cae80be57a096cd7084b6ab"]
5      first: 1
6    ) {
7      nodes {
8        id
9      }
10      totalCount
11    }
12  }
13}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return the id of the location that has the specified place associated with it, as well as the totalCount of how many times the place is present in the itinerary.

Example Successful response:

1{
2  "data": {
3    "itinerary": {
4      "descendants": {
5        "nodes": [
6          {
7            "id": "itinerary/ABC123/location/DEF456"
8          }
9        ],
10        "totalCount": 1
11      }
12    }
13  }
14}

Listing the Itinerary Locations

In order to list the locations that have been added to an itinerary, you can use the "itinerary" query and select the associated itinerary locations using the "children" selector. The query can also be paginated using the relay "cursor connection" specification.

Here is an example of how to use the "itinerary" query to list the locations in an itinerary:

1query QueryItineraryLocationsAsSimpleList {
2  itinerary(id: "itinerary/ABC123") {
3    children(type: ItineraryLocation, first: 10) {
4      edges {
5        node {
6          id
7          __typename
8          ... on ItineraryLocation {
9            place {
10              id
11              name
12              address {
13                locality
14              }
15              layers {
16                name
17              }
18            }
19          }
20        }
21        position: edgePositionNumber(type: ItineraryLocation)
22        cursor
23      }
24      totalCount
25    }
26  }
27}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return a list of itinerary locations, in the structure of a GraphQL connection. Each location will include information such as the place's ID, name, address and categories. The position field will show the numbering of the result 1...X and cursor field will return the cursor to pass back as the "after" property.

Example Successful response:

1{
2  "data": {
3    "itinerary": {
4      "children": {
5        "edges": [
6          {
7            "node": {
8              "id": "itinerary/ABC123/location/DEF456",
9              "__typename": "ItineraryLocation",
10              "place": {
11                "id": "place/atdw:product:56b23f9cb042386245d47ddb",
12                "name": "Tallaringa Views",
13                "address": {
14                  "locality": "Alstonville"
15                },
16                "layers": [
17                  {
18                    "name": "Accommodation"
19                  },
20                  {
21                    "name": "Apartments"
22                  },
23                  {
24                    "name": "Cottages"
25                  }
26                ]
27              }
28            },
29            "position": 1,
30            "cursor": "eyJvZmZzZXQiOjB9"
31          },
32          {
33            "node": {
34              "id": "itinerary/ABC123/location/GHI789",
35              "__typename": "ItineraryLocation",
36              "place": {
37                "id": "place/atdw:product:5f115e78abc0d44d5a0cd076",
38                "name": "The Farm Cafe at the Collingwood Children's Farm",
39                "address": {
40                  "locality": "Abbotsford"
41                },
42                "layers": [
43                  {
44                    "name": "Food and Drink"
45                  },
46                  {
47                    "name": "Produce"
48                  },
49                  {
50                    "name": "Restaurant and Cafe"
51                  }
52                ]
53              }
54            },
55            "position": 2,
56            "cursor": "eyJvZmZzZXQiOjF9"
57          }
58        ],
59        "totalCount": 2
60      }
61    }
62  }
63}

Removing a Location

In order to remove a location from an itinerary, you can use the "deleteItineraryItem" mutation operation. This operation requires the ID of the itinerary location you wish to remove.

Here is an example of how to use the "deleteItineraryItem" operation to remove a location from an itinerary:

1mutation DeleteItineraryLocation {
2  deleteItineraryItem(id: "itinerary/ABC123/item/DEF456") {
3    id
4  }
5}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return the ID of the itinerary location that was removed. Please note that there is no undo option for this operation and the location will be permanently removed from the itinerary.

Example Successful response:

1{
2  "data": {
3    "deleteItineraryItem": {
4      "id": "itinerary/ABC123/item/DEF456"
5    }
6  }
7}

If you are not planning on querying back the itinerary list or maintaining a local representation, you can also read back the cascaded changes which provides the option of reading back any other create/updated or deleted nodes that have been affected by mutations.

Changing the Title of a Location

To change the title of a location, you can use the updateItineraryLocation mutation. The mutation takes in the ID of the location you wish to update and the new title you want to set.

  • You can also update a wide range of fields for the location.
1mutation UpdateItineraryLocationTitle {
2  updateItineraryLocation(
3    id: "itinerary/ABC123/location/DEF456"
4    location: { title: "New Title" }
5  ) {
6    location {
7      id
8      title
9    }
10  }
11}

Sandbox: Configure | Try Operation

Reordering Itinerary Locations

The API allows you to reorder the itinerary locations by using the "moveItineraryItem" mutation operation.

To move an itinerary location to the start of the sequence, you can use the following mutation:

1mutation {
2  moveItineraryItem(
3    id: "itinerary/ABC123/location/DEF456"
4    positionAtStart: {}
5  ) {
6    item {
7      id
8    }
9  }
10}

Sandbox: Configure | Try Operation

Alternatively, you can move an itinerary location to a relative position to another item in the itinerary by using the positionAfterSibling, positionAtEnd or positionBeforeSibling properties in the mutation:

1mutation {
2  moveItineraryItem(
3    id: "itinerary/ABC123/location/DEF456"
4    positionAfterSibling: { siblingId: "itinerary/ABC123/location/GHI789" }
5  ) {
6    item {
7      id
8    }
9  }
10}

Sandbox: Configure | Try Operation

These mutations allow you to reorder the itinerary locations in a flexible way, whether it be moving them to a specific position or relative to other locations. You can leverage this functionality to enable drag-drop functionality in your application, making it easy for users to rearrange their itinerary as they see fit.

Like other mutations, you can also read back cascaded changes to identify what has been affected by your mutation.

Turning on Auto-Routing

To turn on auto-routing for an itinerary, you can use the "updateItinerary" mutation. This mutation allows you to modify an existing itinerary and specify the autoRoute property. In this example, we are using the defaultMode property of autoRoute to assign the default mode of transportation as Car.

1mutation UpdateItineraryAssignCarAutoRoute {
2  updateItinerary(
3    id: "itinerary/ABC123"
4    itinerary: { autoRoute: { defaultMode: Car } }
5  ) {
6    itinerary {
7      id
8      autoRoute {
9        defaultMode
10      }
11    }
12  }
13}

Sandbox: Configure | Try Operation

When this mutation is successful, the response will include the id of the itinerary and the defaultMode that was set. This means that the auto-routing feature has been turned on for this itinerary and the default mode of transportation for the directions will be Car.

1{
2  "data": {
3    "updateItinerary": {
4      "itinerary": {
5        "id": "itinerary/ABC123",
6        "autoRoute": {
7          "defaultMode": "Car"
8        }
9      }
10    }
11  }
12}

You can also specify other modes of transportation such as Foot, Bike, Hike etc. or use advanced profiles like MountainBike, Bus, Motorcycle, Scooter etc.

Once auto-routing is turned on, the API will automatically generate directions between the locations in the itinerary. The directions will include information such as duration and distance estimates as well as providing map data for rendering.

There are numerous other options available to customise the auto routing behaviour, including:

  • Specifying alternative modes of transportation between locations

  • Adding waypoints to directions

It is also possible to change the mode of transport for the entire itinerary or specify it at the point of creation.

Displaying an Itinerary with Matched Directions

Once you have enabled auto-routing on an itinerary, the itinerary will have directions available for querying. You can use the "itinerary" query to access the associated itinerary locations and the directions between each of the locations.

Here is an example of how to query the itinerary locations with information about the directions between each of the locations:

1query QueryItineraryLocationsWithDirections {
2  itinerary(id: "itinerary/ABC123") {
3    children(type: ItineraryLocation, first: 10) {
4      edges {
5        edgePositionNumber
6
7        node {
8          id
9          __typename
10          ... on ItineraryLocation {
11            place {
12              name
13              address {
14                locality
15              }
16              layers {
17                name
18              }
19            }
20          }
21        }
22        directions(first: 1, direction: Inbound) {
23          nodes {
24            durationMin
25            route {
26              segments {
27                mode
28              }
29            }
30          }
31        }
32        cursor
33      }
34      totalCount
35    }
36  }
37}

Sandbox: Configure | Try Operation

If the operation is successful, the response will return a list of itinerary locations, with information about the directions between each of the locations. The response will also include the total number of locations in the itinerary, as well as a cursor that can be used for pagination.

In the response, you will get the name, address and the type of the place you added to the itinerary. And also the duration and the mode of transport you will use to travel between the locations.

It is also possible to search for directions inbound and outbound against each of the locations. There are numerous ways that you can query in directions and has an entire topic dedicated to understanding querying itineraries and stops/directions.

Making a Stop Optional

In order to update an itinerary location and mark it as an optional stop, you can use the "updateItineraryLocation" mutation operation. This operation requires the ID of the itinerary location you wish to update and the new value of the "optional" field.

Optional will remove the location from the path on auto-routing, and instead directions will link to a prior non-optional stop.

Here is an example of how to use the "updateItineraryLocation" operation to mark a location as an optional stop:

1mutation UpdateItineraryLocationAsOptional {
2  updateItineraryLocation(
3    id: "itinerary/ABC123"
4    location: { optional: true }
5  ) {
6    location {
7      optional
8    }
9  }
10}

Sandbox: Configure | Try Operation

This operation accepts variables for the ID and optional field, where you can pass in the actual values for the location ID and whether the location is optional or not (True/False).

If the operation is successful, the response will return the updated location, with the "optional" field now set to the new value. You may also see what else has been affected, such as a result of the autoroute behaviour updating new and existing ItineraryDirections when using this feature, this is returned in the cascaded field.

Adding a Place of Interest for a Location

Once you have created an itinerary, you can start adding locations to it. One way to structure locations is to create a hierarchy of associations between them. For example, you can create a top-level location and then add places of interest under it.

To add a location as a place of interest, you can use the "createItineraryLocation" mutation. This mutation allows you to create a location within an itinerary and specify its position.

Here is an example of how to use the "createItineraryLocation" mutation to create a place of interest under a top-level location:

1mutation CreatePlaceOfInterestLocation {
2  createItineraryLocation(
3    itineraryId: "itinerary/ABC123"
4    location: {
5      title: "My Place of Interest"
6      place: { position: { lon: 144, lat: -37 } }
7      positionAtEnd: { parentId: "itinerary/ABC123/location/DEF123" }
8    }
9  ) {
10    location {
11      id
12      __typename
13      parent {
14        id
15        __typename
16      }
17    }
18  }
19}

Sandbox: Configure | Try Operation

The parentId field in the positionAtEnd argument specifies the top-level location under which the place of interest will be created. The response will include the id and **typename of the created location, as well as the id and **typename of its parent location.

Displaying an Itinerary on a Map

Alpaca uses the Web Mercator projection (EPSG:3857/WGS 84) as the default coordinate reference system. This is a popular projection used by most web mapping technology.

As a core focus of the Alpaca Itinerary structures is to support content to be presented on maps, Alpaca provides a wide range of geographic information that can be stored and accessed via the GraphQL API. The data can be used with various mapping clients, such as desktop mapping software like QGis, or with popular website mapping clients like Mapbox, Google Maps, Leaflet, or Pigeon Maps.

Alpaca also provides various mapping services, which are documented here: https://github.com/AlpacaTravel/mapping-docs.

To display an itinerary on a map, you can use the "listItineraryLocationsWithItineraryDirections" query. This query allows you to obtain the longitude and latitude for each stop, as well as the polyline for directions to each stop. Here is an example of how to use the query:

1query listItineraryLocationsWithItineraryDirections {
2  itinerary(id: "itinerary/ABC123") {
3    children(type: ItineraryLocation, first: 2, after: null) {
4      edges {
5        edgePositionNumber
6        node {
7          id
8          __typename
9          ... on ItineraryLocation {
10            position {
11              lon
12              lat
13            }
14            place {
15              id
16              __typename
17              name
18            }
19          }
20        }
21        directions(first: 1, direction: Inbound) {
22          nodes {
23            id
24            __typename
25            durationMin
26            route {
27              segments {
28                mode
29                # Polyline, Geojson etc
30                polyline
31              }
32            }
33          }
34        }
35      }
36      totalCount
37      pageInfo {
38        hasNextPage
39        endCursor
40      }
41    }
42  }
43}

Sandbox: Configure | Try Operation

If the query is successful, the response will be similar to the following:

1{
2  "data": {
3    "itinerary": {
4      "children": {
5        "edges": [
6          {
7            "edgePositionNumber": 1,
8            "node": {
9              "id": "itinerary/ABC123/location/DEF456",
10              "__typename": "ItineraryLocation",
11              "position": {
12                "lon": 144.9970825017,
13                "lat": -37.803058481
14              },
15              "place": {
16                "id": "place/atdw:product:56b23f9cb042386245d47ddb",
17                "__typename": "Place",
18                "name": "Tallaringa Views"
19              }
20            },
21            "directions": {
22              "nodes": []
23            }
24          },
25          {
26            "edgePositionNumber": 2,
27            "node": {
28              "id": "itinerary/4JhglLgOoo8zlx2yTQK3fq/location/71KzPu21YqJETG5RVnEQ0g",
29              "__typename": "ItineraryLocation",
30              "position": {
31                "lon": 145.0043,
32                "lat": -37.8021
33              },
34              "place": {
35                "id": "place/atdw:product:5f115e78abc0d44d5a0cd076",
36                "__typename": "Place",
37                "name": "The Farm Cafe at the Collingwood Children's Farm"
38              }
39            },
40            "directions": {
41              "nodes": [
42                {
43                  "id": "itinerary/4JhglLgOoo8zlx2yTQK3fq/directions/6AcdRsWojtzzZTDbRcewrb",
44                  "__typename": "ItineraryDirections",
45                  "durationMin": 2.8942833333333335,
46                  "route": {
47                    "segments": [
48                      {
49                        "mode": "Car",
50                        "polyline": "flveFiw~sZ}Ec@eEUiCSl@_Nj@{LdD^bC{L"
51                      }
52                    ]
53                  }
54                }
55              ]
56            }
57          }
58        ],
59        "totalCount": 2,
60        "pageInfo": {
61          "hasNextPage": false,
62          "endCursor": "eyJvZmZzZXQiOjF9"
63        }
64      }
65    }
66  }
67}

Advanced Topics

The Alpaca Travel GraphQL API offers a wide range of capabilities for developers to build interactive travel itineraries for their customers. While the above guide covers many of the most common use cases, there are a number of additional features and capabilities that may be of interest to advanced users.

  • Storing Custom Data Learn how to store and retrieve custom data fields for itinerary locations or places.

  • Working with ATDW Learn how to associate itineraries with products from the Australian Tourism data warehouse

  • Working with Drafts Understand how to create and manage drafts of itineraries before publishing changes

  • Advanced Routing Learn how to use advanced routing features, such as avoiding tolls or ferries, or optimizing for different modes of transportation.

alpaca.tech

Copyright © 2024 - Made with love ❤️ in Australia.