Was this helpful?

Managing a Curated List of Locations

This guide provides developers with the knowledge to create and read from curated lists on the Alpaca Travel Platform. By following this guide, developers can build integrations that manage and access lists of locations on the platform.

This guide is intended to provide developers with a comprehensive understanding of how to use the Alpaca Travel GraphQL API to create and manage lists of places. The guide covers the complete lifecycle of creating and managing lists, including step-by-step instructions for common operations such as creating lists, adding locations, and working with place providers.

The guide covers both GraphQL queries and mutations, providing developers with the complete set of tools to create and manage lists with the Alpaca Travel platform. The guide is intended to be a simple reference that developers can use when they are first getting started with the Alpaca Travel API and are working on their projects, so they can easily understand the different GraphQL operations they need to create a list and manage it.

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 a List

In this section, we will go over the steps to create a new itinerary using the Alpaca Travel GraphQL API. We will cover the following topics:

  • Defining the basic structure of an itinerary

  • Setting the title and default locale

  • Adding attributes to the itinerary

  • Retrieving the created itinerary

Defining the Basic Structure of an itinerary

An itinerary is the main structure that contains the list of locations that the user wishes to visit. The itinerary can have different types, such as a simple list, a trip, or a trail. To create an itinerary, we need to define the basic structure of the itinerary.

The following GraphQL mutation can be used to create an itinerary:

1mutation CreateItinerary {
2  createItinerary(
3    itinerary: {
4      # Initial content
5      title: "List of Recommended Places"
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: "list" }
12      ]
13    }
14  ) {
15    itinerary {
16      id
17      created
18    }
19  }
20}

Sandbox: Configure | Try Operation

If successful, the expected response will be:

1{
2  "data": {
3    "createItinerary": {
4      "itinerary": {
5        "id": "itinerary/ABC123",
6        "created": "2022-10-15T12:00:00Z"
7      }
8    }
9  }
10}

In this example, the createItinerary mutation has been successfully executed, and the server has returned the ID and creation date of the newly created itinerary. The id field will be used to reference this itinerary in future operations, such as adding locations or modifying its attributes. The created field indicates the date and time when the itinerary was first created.

Setting the Title and Default Locale

When creating an itinerary, it is important to provide a title and default locale for the itinerary. The title is a brief summary of the itinerary and it will be used to identify the itinerary for users. The default locale is the language that the itinerary is written in and it will be used as the default language for the itinerary.

1mutation CreateItinerary {
2  createItinerary(
3    itinerary: {
4      title: "List of Recommended Places"
5      defaultLocale: "en"
6      attrs: [
7        # Provide hints about the type of itinerary (e.g. list, trip, trail)
8        # Can be omitted or added later
9        { id: "itinerary/type", value: "list" }
10      ]
11    }
12  ) {
13    itinerary {
14      id
15      created
16    }
17  }
18}

Sandbox: Configure | Try Operation

The above mutation will create an itinerary with the title "List of Recommended Places" and the default locale "en". The response will return the id and created timestamp of the newly created itinerary.

You can also use the updateItinerary mutation operation to update the title and defaultLocale of an existing itinerary.

1mutation UpdateItinerary {
2  updateItinerary(
3    id: "itinerary/ABC123"
4    itinerary: { title: "Liste des lieux recommandés", defaultLocale: "fr" }
5  ) {
6    itinerary {
7      id
8      title
9      defaultLocale
10    }
11  }
12}

Sandbox: Configure | Try Operation

Indicating if a List is Ordered or Unordered

When creating a list, you may want to indicate whether the list is ordered or unordered. An ordered list implies that there is a specific order to the items on the list, while an unordered list implies that the items on the list have no specific order.

To indicate if a list is ordered or unordered, you can use the itinerary/list-presentation attribute when creating the list. The attribute can be set to either "ordered" or "unordered", depending on the desired presentation of the list.

For example, the following GraphQL mutation sets the itinerary/list-presentation attribute to "ordered" when creating a new list:

1mutation CreateOrderedList {
2  createItinerary(
3    itinerary: {
4      title: "My Top 10 Destinations"
5      attrs: [{ id: "itinerary/list-presentation", value: "ordered" }]
6    }
7  ) {
8    itinerary {
9      id
10    }
11  }
12}

Sandbox: Configure | Try Operation

Similarly, the following GraphQL mutation sets the itinerary/list-presentation attribute to "unordered" when creating a new list:

1mutation CreateUnorderedList {
2  createItinerary(
3    itinerary: {
4      title: "My Favorite Places"
5      attrs: [{ id: "itinerary/list-presentation", value: "unordered" }]
6    }
7  ) {
8    itinerary {
9      id
10    }
11  }
12}

Sandbox: Configure | Try Operation

It is important to note that when creating a list, you should consider whether the order of the items on the list is important or not. If the order is important, such as in a top 10 list where the order may imply significance, you would want to indicate the list as ordered. On the other hand, if the order is not important, such as in a list of bookmarks or suggestions without implying any hierarchy, you would want to indicate the list as unordered.

By indicating whether a list is ordered or unordered, you can ensure that the list is presented in the most appropriate way for your users.

Storing your own References

You may also want to store your own identifiers against the list using the attributes custom/external-ref and custom/external-source as well as other custom data attributes.

This could be useful to store an important identifier that relates to your application.

See More

Retrieving the List

To retrieve the basic structure of a list, you can use the itinerary query and provide the list ID. This will return the list's ID, title, default locale, created and modified timestamps, as well as the number of items in the list.

1query GetList {
2  itinerary(id: "itinerary/ABC123") {
3    id
4    title
5    defaultLocale
6    created
7    modified
8    type: attrValue(id: "itinerary/type")
9    presentation: attrValue(id: "itinerary/list-presentation")
10    listItems: children(first: 0) {
11      totalCount
12    }
13  }
14}

Sandbox: Configure | Try Operation

Expected Result:

1{
2  "data": {
3    "itinerary": {
4      "id": "itinerary/ABC123",
5      "title": "My Favorite Places",
6      "defaultLocale": "en",
7      "created": "2022-09-18T21:45:13.640Z",
8      "modified": "2022-11-30T11:40:53.103Z",
9      "type": "list",
10      "presentation": "unordered",
11      "listItems": {
12        "totalCount": 0
13      }
14    }
15  }
16}

You can also retrieve the list items by using the children field of the itinerary query and providing a limit to the number of items to retrieve. The totalCount field in the response will indicate the total number of items in the list, even if not all of them were retrieved in the current query.

Adding and Removing Locations to an List

Managing the locations within an itinerary is an important aspect of creating an engaging and useful itinerary for your users. The Alpaca Travel API provides a number of GraphQL operations that allow you to easily add, remove, and reorder locations within your itineraries.

In this section, we will cover the following:

  • Adding a location directly to an itinerary

  • Adding a location using a place provider

  • Removing a location from an itinerary

Adding a Location Directly to an Itinerary

When you want to add a location to your itinerary that does not have an associated place provider, you can add the location directly to the itinerary. In this case, you need to provide the title and longitude and latitude of the location to the createItineraryLocation mutation.

Here is an example of how you can add a location directly to an itinerary:

1mutation {
2  createItineraryLocation(
3    itineraryId: "itinerary/ABC123"
4    location: {
5      title: "Eiffel Tower"
6      synopsis: "An iconic place of interest"
7      place: { position: { lon: 2.2944, lat: 48.8584 } }
8    }
9  ) {
10    location {
11      id
12    }
13  }
14}

Sandbox: Configure | Try Operation

In the above example, we are adding the location "Eiffel Tower" to the itinerary with the ID "itinerary/ABC123". The longitude and latitude of the location is provided in the position field of the place object.

If the mutation is successful, the server will return the ID of the newly created location, as seen in the location field of the response.

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

Various information about the location can be stored against the location.

It is important to note that when adding a location directly, you will not have access to additional information about the location from a place provider. If you want to add a location with more information, you can use a place provider as described in the next subsection.

Adding a Location using a Place Provider

When adding a location to your itinerary, you have the option to associate the location with a place provider. This allows you to access additional information about the location, such as photos, ratings, contact information, addresses, website and social URL's or other information that is available from the place provider.

Alpaca supports a number of place providers, including OpenStreetMap/WikiData, GeoNames, OpenAddresses, Facebook, and Australian Tourism Data Warehouse. Each provider has their own unique identifier format for a place, so make sure to use the correct format when creating a location.

For example, to add a location to your itinerary and associate it with a place on Facebook, you can use the following mutation:

1mutation CreateItineraryLocationWithFacebookPlace {
2  createItineraryLocation(
3    itineraryId: "itinerary/ABC123"
4    location: {
5      title: "Visit Mavis The Grocer"
6      synopsis: "A great place to grab a breakfast and a coffee"
7      place: {
8        id: "place/facebook:page:mavisthegrocer"
9        position: { lon: 145.0043, lat: -37.8021 }
10      }
11    }
12  ) {
13    location {
14      __typename
15      id
16    }
17  }
18}

Sandbox: Configure | Try Operation

In this example, we are using the Facebook place ID "place/facebook:page:mavisthegrocer" and providing a fallback position of lon: 145.0043, lat: -37.8021. When querying the itinerary, the position from the place provider will be used if available.

It's also worth noting that you can leave the place.id field empty, and instead you'll need to supply all the information about the place directly.

Using a place provider can be especially useful when you want to show additional information about the location, such as photos, ratings, or contact information, to your users. It also allows you to easily update the information about the location in case the place provider updates their data.

Storing your own References

In addition to linking locations to places provided by external providers, you may also want to store your own ID's against a location in your itinerary. To do this, you can use the special custom attributes custom/external-ref and custom/external-source.

The custom/external-ref attribute accepts a string value, which should be a unique identifier per record. The custom/external-source attribute is used to attribute the identifiers to a source. This attribute should be common across all your locations and differentiate between different source locations you may have.

Here's an example of creating an itinerary location with external references:

1mutation CreateItineraryLocationWithExternalReferences {
2  createItineraryLocation(
3    itineraryId: "itinerary/ABC123"
4    location: {
5      title: "Eiffel Tower"
6      place: { position: { lon: 2.2944, lat: 48.8584 } }
7      attrs: [
8        # Use the special custom attributes for linking to your identifiers
9        { id: "custom/external-ref", value: "12345" }
10        { id: "custom/external-source", value: "site" }
11      ]
12      # Other extended fields can be stored against a location
13      websiteUrl: "https://mywebsite.com/places/eiffel-tower"
14    }
15  ) {
16    location {
17      id
18
19      externalRef: attrValue(id: "custom/external-ref")
20      externalSource: attrValue(id: "custom/external-source")
21
22      websiteUrl
23    }
24  }
25}

Sandbox: Configure | Try Operation

With this example, the Alpaca platform will return the external reference ID's that you can then use to map your own place information outside of the Alpaca Travel GraphQL API call.

The Alpaca Data Structure enables a wide range of predefined fields for you to provide and store with your location. You can also store extended data outside of this set.

See More

Testing whether a place is present in a list

Once you have added locations to your list, you may want to check if a particular place is already present in the list before adding it again. This can be useful for creating a "Add to List" button with different states, such as "Added" or "Remove" depending on whether the place is already present in the list.

The following GraphQL query can be used to check if a place is present in a list:

1query CheckItineraryPlacePresent {
2  itinerary(id: "itinerary/ABC123") {
3    descendants(placeIds: ["place/123"], first: 1) {
4      nodes {
5        id
6      }
7      totalCount
8    }
9  }
10}

Sandbox: Configure | Try Operation

The itinerary query is used, supplying the id of the list to check. The descendants field is then queried, providing the placeIds to check and the first parameter, which limits the number of returned results to 1.

In the response, the nodes field will contain an array of itinerary location objects, each representing a location in the list that matches the provided placeIds. The id field of each itinerary location object can be used to refer to the location in future operations. The totalCount field will indicate the total number of matching locations in the list, which can be used to check if the place is already present in the list.

Example response:

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

If the place is already present in the list, the totalCount field will be greater than 0.

Listing the Locations in a List

Once you have added locations to your list, you may want to display the list of locations to the user. You can use the itinerary query and the children selector to list the locations in a list. 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 a curated list:

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            title
10            synopsis
11            preferredMedia {
12              resource {
13                ... on MediaImage {
14                  url(bestFit: [200, 200])
15                  caption
16                  copyright
17                  altText
18                }
19              }
20            }
21            place {
22              # Use this to draw the list on to a map
23              position {
24                lon
25                lat
26              }
27              # Example drawing basic information from a place provider
28              id
29              name
30              contact {
31                facebookUrl
32                instagramUrl
33              }
34              address {
35                addressLineOne
36                locality
37              }
38            }
39
40            # Othere references you have stored against your list
41            externalRef: attrValue(id: "custom/external-ref")
42            websiteUrl
43          }
44        }
45        position: edgePositionNumber
46        cursor
47      }
48      totalCount
49    }
50  }
51}

Sandbox: Configure | Try Operation

The itinerary query is used, supplying the id of the list to display. The children field is then queried, providing the type of the children to return and the first parameter, which limits the number of returned results.

In the response, the edges field will contain an array of itinerary location objects, each representing a location in the list. 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 the cursor field will return the cursor to pass back as the "after" property in case you want to paginate the result.

You can use this query to display the locations in a list on your application, and pass the cursor as the "after" property to retrieve the next set of locations. This will allow you to paginate through the results and display them in chunks to the user.

Note that the place object contains the position field which is the location of the place on map with lon and lat which could be used to show the places on a map.

Removing a Location from a List

Once a location has been added to a list, you may want to remove it from the list. In order to remove a location from a list, you can use the "deleteItineraryItem" mutation operation.

This operation requires the id of the itinerary location you wish to remove. The id field of each itinerary location object can be obtained when querying for the locations in a list or when checking if a place is present in a list.

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

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

Sandbox: Configure | Try Operation

When 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 list.

Example Successful response:

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

Please be aware that the operation is irreversible.

Reordering the List

After you have added multiple locations to your list, you may want to change the order of those locations. The Alpaca Travel API allows you to reorder the locations within a list by using the "moveItineraryItem" mutation operation.

The moveItineraryItem mutation takes an id parameter, which is the ID of the itinerary location to be moved, and a positionAtStart, positionAfterSibling, positionAtEnd or positionBeforeSibling property that specify the location in the itinerary that the item should be moved to.

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. This can be used to enable drag-and-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.

It is also possible to specify the position of the location when you are initially creating the location. The same position properties are available using the createItineraryLocation method.

Updating Content

Your list can have content attached to both the itinerary as well as the individual locations appearing in your list.

Location Content

Once you have added locations to your list, you may want to update the title of a location. This can be done using the updateItineraryLocation mutation. The mutation takes in the id of the location you wish to update and the new title you want to set.

  • Title

  • Synopsis and Descriptions

  • Tags, Read More URL and Website URLs

  • Media (Contained in a seperate guide)

  • Place information and extended Attributes

You can provide this content when you are initially creating the location using the createItineraryLocation mutation, or alternatively use the updateItineraryLocation mutation to provide or update these values later.

1mutation UpdateItineraryLocationContent {
2  updateItineraryLocation(
3    id: "itinerary/ABC123/location/DEF456"
4    location: {
5      # Various standard fields, see "UpdateItineraryLocationInput" type
6      title: "Eiffel Tower"
7      synopsis: "A short summary for the location"
8      description: "Markdown formatted description for the location"
9      # Extended attributes
10      upsertAttrs: [
11        { id: "place/website-url", value: "https://www.toureiffel.paris" }
12        {
13          id: "place/facebook-url"
14          value: "https://www.facebook.com/EiffelTower.OfficialPage"
15        }
16      ]
17      # You can remove values using the deleteAttrs
18      # example value: [{ id: "place/website-url" }]
19      deleteAttrs: []
20    }
21  ) {
22    location {
23      id
24      # Read back updated values in the response
25      title
26    }
27  }
28}

Sandbox: Configure | Try Operation

The location field contains the new title you want to set for the location.

In the response, the location field will contain the updated location object, including the id and updated title fields.

Example response:

1{
2  "data": {
3    "updateItineraryLocation": {
4      "location": {
5        "id": "itinerary/ABC123/location/DEF456",
6        "title": "New Title"
7      }
8    }
9  }
10}

Please note that you can also update other fields for the location using this mutation.

Itinerary Content

You may want to also want to store content against the list itself. The itinerary data also supports content similar to the itinerary location. This structure provides numerous standard fields as well as supporting extended attributes to store both custom data as well as other elements such as classifications (such as genre, style, audience, etc).

The below example provides a synopsis and description, as well as an example of updating a classification.

1mutation UpdateItineraryContent {
2  updateItinerary(
3    id: "itinerary/ABC123"
4    itinerary: {
5      # Various standard fields, see "UpdateItineraryInput" type
6      title: "My new title"
7      synopsis: "A short summary of the list"
8      description: "A longer markdown formatted description for the list"
9      # Extended attributes
10      upsertAttrs: [
11        { id: "itinerary/genres", value: ["alpaca:genre:FOOD|CULINARY"] }
12      ]
13      # You can remove values using the deleteAttrs
14      # example value: [{ id: "place/website-url" }]
15      deleteAttrs: []
16    }
17  ) {
18    itinerary {
19      id
20      # Read back any updated values
21      title
22    }
23  }
24}

Sandbox: Configure | Try Operation

Like itinerary location, you are able to assign image media.

Sharing a List

After you have created a list, you may want to share the list with others. You may choose to present your own shareable list, but you can also share an interactive map from the Alpaca Travel platform.

For this to work, you will need to have the id for the itinerary and substitute the id value in place of the <ITINERARY_ID> in the following URL:

1https://made.withalpaca.com/view/<ITINERARY_ID>

The user will be presented with an interactive version of your content by following the URL.

alpaca.tech

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