Setting the HTTP Method to Use for a REST API
Changing the HTTP method StepZen uses to call a REST API is as simple as setting the method
attribute of the @rest
directive. The supported methods are:
GET
: the default methodPOST
: allows setting a request body using thepostbody
attribute.PUT
: allows setting a request body using thepostbody
attribute.PATCH
: allows setting a request body using thepostbody
attribute.DELETE
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
.
Generation of Request Body
StepZen will automatically generate 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 StepZen 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
, StepZen 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 above 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 example below 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 will work 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 above example, StepZen provides a special function for use in the postbody template, Get
. Get("client_id") will retrieve the argument client_id as a string and substitute it into the postbody. StepZen 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 (e.g. String, Float, Integer, Boolean). It will return "" 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, for example. 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, e.g. https://httpbin.org/post?firstName=<value>&lastName=<value>
. We can tell StepZen 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 of 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 ) }