One of the features that Azure API Management introduced at BUILD 2022 was Synthetic GraphQL – building a GraphQL service from a set of REST APIs. GraphQL is still relatively new - at least compared to REST and SOAP - but it provides advantages over REST APIs when dealing with client applications. Firstly, the client can fetch all the data it needs to render a page in one request, whereas when the data is modeled for REST, it generally takes multiple requests to get all the data. Secondly, the client can request what data it is interested in for rendering the page and the server returns just that data. RESTful APIs normally have a static set of fields that are returned. A client application rarely uses all the fields, so data is transferred and then thrown away.
Azure API Management makes it easier to build a GraphQL façade over your REST APIs. However, you will need to understand GraphQL, write a schema, and then write a resolver for each piece of data. This article assumes that you have already created an Azure API Management instance (any of the dedicated SKUs will work for this purpose).
GraphQL is a strongly typed protocol, so you need a schema to describe your data. In this example, I’m going to use a “Todo List” application that I happen to have. The schema looks like this:
type TodoItem {
id: ID!
title: string!
notes: string
}
type Query {
getAllTodoItems: [TodoItem!]!
getTodoItem(id: ID!): TodoItem
}
type Mutation {
createTodoItem(title: string!, notes: string): TodoItem
deleteTodoItem(id: ID!): void
}
schema {
query: Query
mutation: Mutation
}
There is one model type here – the TodoItem. The Query and Mutation types are special types that allow me to retrieve the data and modify the data respectively. Finally, the schema entry is a required section that ties the pieces together.
Now that I have a schema in a file, I can create an API on my Azure API Management service (full instructions are located in the documentation)
After a few moments, the API will be generated and you can move onto the next step.
The final step for creating a synthetic GraphQL service is to write resolvers. A resolver is a piece of code (or in our case, policy) that is responsible for generating a piece of the GraphQL response. You can attach resolvers to any field of any type. In the case of my TodoList app, I’m going to attach a resolver to each of the queries and mutations I have defined. Resolvers are defined with the <set-graphql-resolver> policy within the backend section. Here is the policy for a single resolver:
<set-graphql-resolver parent-type="Query" field="getAllTodoItems">
<http-data-source>
<http-request>
<set-method>GET</set-method>
<set-url>https://td.azure-api.net/todoitem</set-url>
</http-request>
</http-data-source>
</set-graphql-resolver>
The getAllTodoItems query will execute a HTTP GET against the specified URL. The response from that request is already in the form I need to return to the user, so I don’t need to do anything else.
In the case of the getTodoItem, I have a variable that I need to pass to the REST endpoint. I can do this with a policy expression:
<set-graphql-resolver parent-type="Query" field="getTodoItem">
<http-data-source>
<http-request>
<set-method>GET</set-method>
<set-url>@{
var body = context.Request.Body.As<JObject>(true);
return "https://td.azure-api.net/todoitem/" +
body["variables"]["id"].ToString();
}</set-url>
</http-request>
</http-data-source>
</set-graphql-resolver>
This constructs a URL that I will use to request the right information. I can do the same thing with the deleteTodoItem mutation to effect the deletion.
When I am creating a TodoItem, I need to construct a POST request with a body. I can do this by adding a <set-body> policy and a little bit of policy expression:
<set-graphql-resolver parent-type="Mutation" field="createTodoItem">
<http-data-source>
<http-request>
<set-method>POST</set-method>
<set-url>https://td.azure-api.net/todoitem</set-url>
<set-body>@{
var body = context.Request.Body.As<JObject>(true);
JObject jsonObject = new JObject();
jsonObject.Add("title", body["variables"]["title"]);
jsonObject.Add("notes", body["variables"]["notes"]);
return jsonObject.ToString();
}</set-body>
</http-request>
</http-data-source>
</set-graphql-resolver>
You can also use the <set-body> policy to modify the response from the service. For example, if you are trying to resolve one field and the service returns an object with a bunch of fields; then you can use a policy expression to extract the one field you need. You can also use the Liquid templating language to set up a body, and use named values to reduce the repetitive URL references. Refer to the API Management Policy Reference for more information about these policies.
The Azure API Management has a GraphQL test console built right into the API blade. You can access it by selecting the Test tab. Try entering the following query in the query editor:
query {
getAllTodoItems { title }
}
Assuming you have data in the backend referenced in the resolvers, this will return the title of each item in a list.
Synthetic GraphQL is in public preview, so let me know what you think! We can’t wait to see what you build with it.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.