LearnPromotions

GraphQL concepts

As an integrated partner, you are familiar with our current API formats and standards:

  • XML - Markup language that is used to encode documents in human-readable and extensible format.
  • REST - Architecture used for exchanging structured data that is performant and ubiquitous, which means partners can easily adopt our APIs to send requests and responses that meet performance metrics crucial to the nature of the traveler business.
  • JSON - Lightweight format for structured data that is easy to read and parse. It is used primarily to transmit data between a server and web application as an alternative to XML. (This format will continue to be used. GraphQL queries use the JSON content type and responses are returned in JSON.)

Now, we are offering new capabilities as part of a GraphQL API. Why introduce GraphQL into the mix?

Benefits

GraphQL is a query language that enables you to fetch data efficiently, and it offers these benefits:

  • Single endpoint - GraphQL is typically served over HTTP via a single endpoint, which provides access to the full set of capabilities of the service. You can issue one query to retrieve all of the data that is needed. This differs from REST APIs, which often necessitate multiple queries to multiple endpoints to retrieve the desired data. GraphQL also eliminates over- and underfetching because the structure of the data that’s returned is not fixed. Instead, it is flexible and enables the client to decide what data is needed.
  • Versioning - GraphQL returns only the data that's explicitly requested, so new types and new fields on those types can be added without creating a breaking change. With REST APIs, there's limited control over the data that's returned from an endpoint. Any change can be considered a breaking change, and breaking changes require a new version.
  • GraphQL explorer - Also referred to as a "GraphQL playground," this is a graphical integrated development environment (IDE) that enables you to test requests and view responses. We provide an explorer to test queries GraphQL explorer below, or you can install a third-party IDE (desktop app), such as Prisma or Insomnia, to test queries and mutations. After installing the IDE, be sure to generate your authorization token and set it as an HTTP header.

As a developer using a GraphQL API, how does using this type of API differ from using our existing formats and standards?

  • The verbs are different: To retrieve data, you will issue a query, and to modify data, you will issue a mutation. (If using a client to issue queries, you can use the GET or POST method.)
  • Access is the same: You will continue to issue requests over HTTP and you can continue to use clients such as cURL or Postman.
  • Response format is the same: Responses are returned in JSON.

Variables

GraphQL allows you to pass variables as query arguments (referred to as "parameterizing the query"). Here's how:

  1. In the query, replace the argument's static value with a variable, such as $id.

  2. Declare the variable. In the example below, $id: String!, $idSource: IdSource is the declaration. Each variable declaration must begin with $ and state its type. Declared variables must be scalars, enums, or input object types. Also, if the field you are passing the variable into requires a non-null argument, the variable must be required as well (as noted by the ! in the example declaration).

  3. Pass in values for the variables.

Here's an example that parameterizes the id and idSource arguments:

1query propertyActiveStatus( $id: String!, $idSource: IdSource) {
2 property(id: $id, idSource: $idSource) {
3 name
4 units {
5 ids {
6 id
7 idSource
8 }
9 }
10 }
11}

Here are the query variables and their values:

1{
2 "id": "695896241",
3 "idSource": "EXPEDIA"
4}

This way, you can pass a different variable rather than constructing an entirely new query.

Fragments and batched queries

You can create reusable bits of code called "fragments" that you can include in queries as needed. This gives you a way to avoid repeating sections of a query over and over. When you define a fragment, you provide a name and state the object to which the fields in the fragment belong.

Here is a simple example that creates the PropertyFields fragment that retrieves fields on the Property object:

1query {
2 listing1: property(id: "597812260", idSource: EXPEDIA) {
3 id​
4 ids {
5 id​
6 idSource​
7 }
8 name​
9 address {
10 countryCode ​
11 }​​
12 }
13 listing2: property(id: "695896241", idSource: EXPEDIA) {
14 id​
15 ids {
16 id​
17 idSource​
18 }
19 name​
20 address {
21 countryCode ​
22 }​​
23 }
24}

Using the same concept, you can create code snippets that can be used for more complex calls. This enables you to batch queries, like this:

1{
2 listing_01: property(id: "597812260", idSource: EXPEDIA) {
3 ...details
4 units {
5 ...status
6 }
7 }
9 listing_02: property(id: "695896241", idSource: EXPEDIA) {
10 ...details
11 units {
12 ...status
13 }
14 }
15}
16
17fragment details on Property {
18 id
19 ids {
20 id
21 idSource
22 }
23 name
24}
25
26fragment status on Unit {
27 activeStatus {
28 active
29 statusCheckpoint {
30 name
31 lastEvaluated
32 resolutionStatus
33 checkpoints {
34 name
35 path
36 lastEvaluated
37 resolutionStatus
38 fulfilled
39 }
40 }
41 }
42}

In the response, data for listing_01 (the first query) and data for listing_02 (the second one) would be returned.

Interfaces and __typename meta field

An interface is an abstract type that includes a set of fields that multiple object types can include. If an object type implements an interface, the object type must included all of the interface's fields. This provides an easy way to define types that share the same fields.

For example, any type that implements the Guest interface:

1interface Guest {
2 name: String!
3 age: Int!
4}

must include the name and age fields, such as shown here:

1type Adult implements Guest {
2 name: String!
3 age: Int!
4 workPlace: String
5}
6
7type Child implements Guest {
8 name: String!
9 age: Int!
10 school: String
11}

In addition, a field can have an interface as its return type. When querying a field that returns an interface, you can query for the interface's subfields using an inline fragment, and you can also query for subfields that aren't included in the interface. Here's a snippet that shows inline fragments (... on Adult and ... on Child) and also queries for workPlace and school, which are not fields on the interface:

1query {
2 family(lastname: 'Doe' ) {
3 members {
4 name
5 ... on Adult {
6 workPlace
7 }
8 ... on Child {
9 school
10 }
11 }
12 }
13}

To help identify the actual object type (out of the possible types) that has been returned, you can include the __typename meta field when querying against an interface. In this example, you can determine which type of family member is returned without examining the returned fields:

1query {
2 family(lastname: 'Doe' ) {
3 __typename
4 ... on Adult {
5 workPlace
6 }
7 ... on Child {
8 school
9 }
10 }
11}

This way, if you need to handle an adult differently than a child, you don’t need to code logic based on what you request. __typename provides the information for you.

Pagination

For queries that return large result sets, the API provides a way to paginate results (as defined by the GraphQL spec). When issuing a query, you can specify how many results are included in a page. Several fields are available to enable this functionality:

  • pageSize refers to the number of results returned per page, up to 25 are allowed
  • totalCount specifies the total number of results
  • hasNextPage indicates that at least one more page of results is available (when set to true)
  • endCursor provides a marker for the end of the current page; this marker is specified in the after field in the next query request

​​Here is a promotions query example that sets pageSize to 3, which means three promotions are returned per page. The response indicates that there are 10 promotions (totalCount) in the result set, which means that you must retrieve four pages of results. Note the values of totalCount, hasNextPage, and endCursor in the response.

1query {
2 property(id: "12345", idSource: EXPEDIA){
3 promotions(pageSize: 3) {
4 totalCount
5 pageInfo {
6 hasNextPage
7 endCursor
8 }
9 edges {
10 ...

To retrieve the next page of results, issue a request similar to the following. Take the value of endCursor from the previous response and specify it in the after field of the next request. Continue to issue requests and update the after field with the value of endCursor until the response returns "hasNextPage": false and "endCursor": null.

1query {
2 property(id: "12345", idSource: EXPEDIA){
3 promotions(
4 pageSize: 10,
5 after: "L3Jlc2VydmF0aW9ucy9zZWFyY2gZW50aXR5Q29sbGVjdGlvbj1yZXNlcnZhdGlvbnMmcGFnZXNpemU9MiZwYWdlPTImaHlkcmF0ZT1mYWxzZSZpbm") {
6 totalCount
7 pageInfo {
8 hasNextPage
9 endCursor
10 }
11 edges {
12 ...

GraphQL explorer

The query reference pages for GraphQL APIs provide an interactive GraphQL IDE that lets you run queries offered by the API. The explorer offers these features:

  • A sample query is provided, and a test property ID is passed into the explorer; its test data is returned.
  • You can execute queries in the explorer by clicking Run Query.
  • Launch the full-scale explorer in another tab/window by clicking API Explorer. This explorer provides syntax highlighting, schema introspection, real-time error highlighting, and auto-completion, among other things.
  • Click Graph Viewer on the full explorer to peruse the schema so you can understand object relationships.
  • Click Docs on the full explorer to drill down into the schema and review field-level documentation.
  • To download the schema, click Export Code and then click Download schema.zip on the dialog.

Resources

To fully understand the benefits of GraphQL, check out these learning resources: