Sending a POST request to a REST service
Connect a REST API as a data source using the @rest
directive.
Setting the HTTP Method to use for a REST API
Sending an HTTP POST request to an API endpoint is as simple as adding the
method
attribute to the @rest
directive and setting the value to
POST
, as shown in the following example:
type Query {
anything(message: String): JSON
@rest (
endpoint: "https://httpbin.org/anything"
method: POST
headers: [
{name: "User-Agent", value: "Essentials"}
{name: "X-Api-Key", value: "12345"}
]
)
}
If you deploy this schema and request the anything
field from a query operation,
supplying "Hello World" for the message
argument, you will see that AAPI Connect for GraphQL sent a POST request with the
request body as json, like this:
{"message":"Hello World"}
Generating the request body
API Connect for GraphQL automatically generates the HTTP request body using all query or mutation arguments. For example, consider a query defined as follows:
type Query {
formPost(firstName: String! lastName: String!): JSON
@rest(
method: POST
endpoint: "https://httpbin.org/post"
)
}
This query accepts two string values and returns a JSON scalar. The contenttype
has not been set, so defaults to application/json
. The postbody
has not been set, so API Connect Essentials will automatically generate a simple JSON body using the
query argument names and values:
{
"firstName":"value",
"lastName":"value"
}
If we change the contenttype
to
application/x-www-form-urlencoded
, API Connect Essentials will generate a
form-encoded request body.
type Query {
formPost(firstName: String! lastName: String!): JSON
@rest(
method: POST
contenttype: "application/x-www-form-urlencoded"
endpoint: "https://httpbin.org/post"
)
}
The preceding SDL results in a body of:
firstName=value&lastName=value
If you need to change the field names in the automatically generated bodies, you can do that
using the arguments
attribute for the directive. In the following example we have
changed the names from camelCase to snake_case:
type Query {
formPost(firstName: String! lastName: String!): JSON
@rest(
method: POST
endpoint: "https://httpbin.org/post"
arguments: [
{argument:"firstName", name:"first_name"},
{argument:"lastName", name:"last_name"}
]
)
}
This approach works for both application/json
and
x-www-form-urlencoded
bodies.
We only need to use postbody
when we need to customize the request body more
than simply renaming fields. For example, if we need to add fields, or generate a more complex
structure. When performing variable substitution in the postbody
, we need to use
the Go language template syntax.
For example, consider a query that uses the Client Credentials flow to obtain an OAuth token.
Since the grant_type
argument is not included in the query definition, we need to
add it to the request body.
type Query {
token(client_id: String! client_secret: String!): Token
@rest(
endpoint: "<oauth token endpoint"
method: POST
contenttype: "x-www-form-urlencoded"
postbody: """
grant_type=client_credentials&client_id={{ .Get "client_id" }}&client_secret={{ .Get "client_secret"}}
"""
)
}
As shown in the preceding example, API Connect Essentials provides a special function for use in
the postbody template, Get
. Get("client_id")
retrieves the
argument client_id
as a string and substitutes it into the postbody. API Connect for GraphQL also provides a function
named GetJSON
, invoked in the template as
.GetJSON("<variable>")
, which returns the JSON value of the variable.
Get
is limited to returning scalar values; values that can be represented as a
string (String, Float, Integer, Boolean). API Connect for GraphQL returns ""
if a value is null. GetJSON
can be used to return any JSON value.
Get
and GetJSON
will search for variables along a search path
so you can substitute in more than just query arguments. First the functions check any configuration
values configured for the @rest
directive, then they will check any JWT private
claims, and finally they will check the operation arguments.
Consider a JSON payload from our earlier example, but our JSON payload needs to be more complex:
{
"user":{
"firstName": "first name",
"lastName": "last name"
}
}
type Query {
logUser(firstName: String! lastName: String!): JSON
@rest(
endpoint: "https://httbin.org/post"
postbody: """
{
"user": {
"firstName": "{{ .Get "firstName" }}",
"lastName": "{{ .Get "lastName" }}"
}
}
"""
)
}
Since we are not automatically generating the body of the request, the GraphQL query arguments
will be automatically appended to the request URL as parameters in a query string; for example,
https://httpbin.org/post?firstName=<value>&lastName=<value>
. We can
tell API Connect for GraphQL not to append
GraphQL query arguments as query string parameters by setting the
stepzen.queryextensionguard
attribute to true in the config.yaml
file as follows:
configurationset:
- configuration:
name: "config"
stepzen.queryextensionguard: true
Our final query schema is:
type Query {
logUser(firstName: String! lastName: String!): JSON
@rest(
endpoint: "https://httbin.org/post"
postbody: """
{
"user": {
"firstName": "{{ .Get "firstName" }}",
"lastName": "{{ .Get "lastName" }}"
}
}
"""
configuration: "config"
)
}
Example: PATCH and DELETE
To run a PATCH
or DELETE
HTTP method rather than the default
POST
method, add the method
argument to the @rest
directive in the schema.
type Query {
logUser(firstName: String lastName: String): JSON
@rest(
endpoint: "https://httpbin.org/patch"
method: PATCH
arguments: [
{argument:"firstName", name:"first_name"},
{argument:"lastName", name:"last_name"}
]
)
}
type Query {
deleteUser(userID: ID!): JSON
@rest(
endpoint: "https://httpbin.org/delete"
method: DELETE
)
}