Creating a sequence of GraphQL queries
Use the @sequence
custom directive to specify a sequence for API and
database queries and return a single result.
The @sequence
directive is a custom directive that executes multiple queries in
a sequence to return a single result. Each step in the sequence passes its data as arguments to the
next step. This enables complex sequencing of API and database calls to populate a query, without
the need to implement difficult asynchronous call handling.
Configuring the steps in a sequence
The only required argument to create a sequence is the steps
that make up that
sequence. This is an array of objects that the query "steps through". Each object in the array must
contain (at a minimum) the name of the query
that the step will call.
@sequence(
steps:
[
{query: "step1"}
{query: "step2"}
]
)
The value of query
must be a query that is defined on the schema. The result of
the sequence will be the same as the result of the last step (step2
in this
case).
The steps are executed in the order they are listed (from top to bottom). For more details on how to override the steps to include data from prior steps, see the Collecting results section.
In some cases, the name of a property returned by a query does not match the argument name for
the subsequent query. You can use arguments
to map the results to the expected
name.
For example, if the preceding step1
query returns lastName
, but
the step2
query expects an argument of surname
, use
arguments
to map the results to the expected name:
@sequence(
steps:
[
{query: "step1"}
{query: "step2", arguments: [{name: "surname", field: "lastName"}]}
]
)
Collecting results
By default, the result of the final step is the result of the sequence. However, in some scenarios, you might need data from prior steps as part of the result of the entire sequence.
For example, imagine step1
returns user information including the
name
and step2
returns location information including
city
. To have the full sequence return both name
and
city
, use an extra step in the sequence that calls a query utilizing a special
echo
connector.
The sequence would look like the following example:
@sequence(
steps:
[
{query: "step1"}
{query: "step2"}
{query: "collect"}
]
)
The collect
query in step 3 of the sequence will then use the
echo
connector:
collect (name: String!, city: String!): UserWithLocation
@connector (type: "echo")
Authorization headers in the GraphQL schema
When running an @sequence
directive, GraphQL arguments can be passed as header
variables in an HTTP request.
In the following example, an argument of access_token: String
is passed to the
authorization header as a bearer token:
type Query {
id: String
}
type Query {
my_query(access_token: String): Query
@rest (
endpoint: "https://api.example.com/v1/api/"
headers: [{ name: "Authorization", value: "$access_token" }]
)
}
Example query
When running the following GraphQL query, the access_token: String
argument is
"Bearer CMORsM63LxIO..."
, and contains the entire string for the
Authorization
header:
query MyQuery {
my_query(
access_token: "Bearer CMORsM63LxIO_4eBAEBAgAAMAEAAAAYY_MDnCSCXsaQLKM_AFzIUdnIEl9qo7Cwj2t1Z_rNAec5zYls6LgB_b8f_BwyE_wf8_-D_fc4sAAAAYAYY9DwfIBgcgA_gwx8GGQAAOIUnAADgAOBCFBZjpguluSl9IBk0ni7_U1o-pPgjSgNuYTFSAFoA"
) {
id
}
}
If the access_token
argument is not provided in the query, or if it is
explicitly set to null
, API Connect
for GraphQL resolves the Authorization
header to an empty string,
""
.
This is an example query where there is no access_token
argument provided:
query MyQuery {
my_query {
id
}
}
Authorization: "null"
is converted to Authorization: ""
to
prevent server errors and avoid sending unwanted null
arguments as headers.
Combining variables with strings in Authorization headers
API Connect Essentials also supports strings combined with variables in header arguments.
As shown in the following schema, my_query
will combine the GraphQL argument
access_token
with a literal string Bearer
:
my_query(access_token: String): Query
@rest (
endpoint: "https://api.example.com/v1/api/"
headers: [{ name: "Authorization", value: "Bearer $access_token" }]
)
When my_query
is executed, access_token
does not need to
contain the Bearer
string. The schema combines the Bearer
string
with the access_token
.
query MyQuery {
my_query(
access_token: "CMORsM63LxIO_4eBAEBAgAAMAEAAAAYY_MDnCSCXsaQLKM_AFzIUdnIEl9qo7Cwj2t1Z_rNAec5zYls6LgB_b8f_BwyE_wf8_-D_fc4sAAAAYAYY9DwfIBgcgA_gwx8GGQAAOIUnAADgAOBCFBZjpguluSl9IBk0ni7_U1o-pPgjSgNuYTFSAFoA"
) {
id
}
}
The request to the endpoint https://api.example.com/v1/api/
will still send the
Authorization
header the same way it did previously, "Authorization":
"Bearer CMORsM63LxIO..."
.
@Sequence example: passing access_token as a variable
Let's say there is an @sequence
that sends the_access_token
but
does not provide the token type Bearer
in the string.
Here is an example:
type Auth {
access_token: String!
token_type: String!
expires_in: String!
}
type Query {
id: String
}
type Query {
get_auth: Auth
@rest(
method: POST
contenttype: "application/x-www-form-urlencoded"
endpoint: "https://api.example.com/v1/api?username={{.Get \"username\" }}&password={{.Get \"password\" }}"
configuration: "configs"
)
my_query(access_token: String): Query
@rest (
endpoint: "https://api.example.com/v1/api/"
headers: [{ name: "Authorization", value: "Bearer $access_token" }]
)
"""
returns a query with the new access_token
"""
query_with_key: Query
@sequence(
steps: [
{ query: "get_auth" }
{ query: "my_query" }
]
)
}
The type Auth
has the field access_token
. In the
query_with_key
@sequence
, the get_auth
query passes the
access_token
to the my_query
query.
{
"token_type": "bearer",
"refresh_token": "12314-3ee9-4a6b-bc87-134254332",
"access_token": "CMORsM63LxIO_4eBAEBAgAAMAEAAAAYY_MDnCSCXsaQLKM_AFzIUdnIEl9qo7Cwj2t1Z_rNAec5zYls6LgB_b8f_BwyE_wf8_-D_fc4sAAAAYAYY9DwfIBgcgA_gwx8GGQAAOIUnAADgAOBCFBZjpguluSl9IBk0ni7_U1o-pPgjSgNuYTFSAFoA",
"expires_in": 21600
}
As shown in the example:
access_token
(generated by the first step)get_auth
does not haveBearer
preceding the token generated
Therefore my_query
must be written with headers: [{ name:
"Authorization", value: "Bearer $access_token" }]
. API Connect for
GraphQL combines the variable access_token
being passed in the first step with
Bearer
added in the schema, to properly set the Authorization
header.
my_query(access_token: String): Query
@rest (
endpoint: "https://api.example.com/v1/api/"
headers: [{ name: "Authorization", value: "Bearer $access_token" }]
)