Building a great API program takes time. You've probably made a large investment in REST, SOAP, and other HTTP-based APIs already. When a new HTTP-based protocol comes on the scene, your first reaction definitely isn't to rush out and try it. GraphQL is relatively new - only a decade old, and we're hearing of more developers who want to try it out but are loathe to leave their existing REST APIs behind. It's not just a case of swapping out your API. You must also refactor all your client applications that access that API at the same time.
Enter Synthetic GraphQL - a new feature of Azure API Management. Synthetic GraphQL allows you to build a GraphQL API from your existing HTTP-based APIs (including REST and SOAP). It allows your APIs to coexist while you migrate your client applications to GraphQL. You can then slowly migrate your existing APIs to a GraphQL service over time or as your needs evolve. You don't have to take the big bang approach to updating your APIs and client applications at the same time. In fact, you may not even want to have everything in a GraphQL service. You can also augment a GraphQL service with some information from another API, like Microsoft Graph or Dataverse. It's easy, for example, to store the user ID or email address of a user in GraphQL and then pull the other information you want to present in your client applications (like the users name or title) from Azure Active Directory via Microsoft Graph.
An example: The todo list demo
Most developers create a todo list application as part of their learning experience, so let's see how we can use such an API to create a GraphQL service. I have an existing todo list REST API that supplies a CRUD (create, read, update, delete) interface to the data:
- GET /api/todoitems retrieves the list of items
- GET /api/todoitems/{id} retrieves a single item
- POST /api/todoitems creates a new item
- DELETE /api/todoitems/{id} removes an existing item
- PUT /api/todoitems/{id} replaces an existing item
I can model this interface in GraphQL:
type TodoItem {
id: ID!
title: String!
completed: Boolean!
}
input CreateTodoItemInput {
title: String!
}
input ReplaceTodoItemInput {
id: ID!
title: String!
completed: Boolean!
}
type Query {
todoItems: [TodoItem]
todoItem(id: ID!): TodoItem
}
type Mutation {
createTodoItem(input: CreateTodoItemInput!): TodoItem!
replaceTodoItem(input: ReplaceTodoItemInput!): TodoItem!
deleteTodoItem(id: ID!): Boolean
}
To create this GraphQL API within the Azure API Management service:
- Open the Azure portal in your browser.
- Select your existing Azure API Management service (or create a new one).
- Select the APIs blade.
- Select GraphQL to create a new GraphQL API.
- Fill in the form:
- Choose a suitable display name (I chose Todo List),
- The name will fill automatically with a suitable name,
- Select Synthetic GraphQL,
- Select the file that holds the GraphQL schema,
- Enter graphql as the API URL suffix.
- Select Create to create the API.
Create a resolver
Once you have created the API, you will be taken to the Design tab of the GraphQL API. Since the GraphQL schema is central to the understanding of the API, we've made the schema the first tab you see.We've also added some features to the editor to make navigating large schemas easier. For example, you can search or go to a specific line. You can also right-click on any type and use Go to definition to scroll to where it is defined and Peek definition to look at the definition in line.
The next step is to create resolvers. Resolvers are how GraphQL requests are fulfilled. You can (and should) add resolvers for each query and mutation in your schema. In addition, you may want to add resolvers to fields or types elsewhere in the schema. For example, let's say you have a REST API that returns a list of IDs, and you have to make a further call to get the details of the type that the ID refers to. You can add a resolver to the type to gather the rest of the information that is needed to respond to the client.
You can add a resolver in two ways:
- Select the Resolvers tab, then select + Create. Select the name, type, and field to establish a target for the resolver.
- Roll over the gutter of the editor at the line where the query or mutation is defined, then select the + that appears in the gutter. The name, type, and field will be populated automatically.
Whichever way you get to the resolver, you will be asked to create the resolver policy - an XML document that defines how to call the HTTP API and how to change the response to match the requirements of the GraphQL response. For example, the createTodoItem resolver uses the following policy:
<http-data-source>
<http-request>
<set-method>POST</set-method>
<set-url>{{todorestapi}}/api/todoitems</set-url>
<set-body>@(Context.GraphQL.Arguments["input"].ToString())</set-body>
</http-request>
</http-data-source>
This is a fairly basic resolver. I've used a named value (called todorestapi) to hold the base URL of my API, and I've converted what I received from the GraphQL request (as JSON) to be the (JSON) body of the request. You can also add authorization headers, select certificates, or do whatever else is needed to communicate with the REST API. What comes back from the REST API matches what I want to send back to the client. However, if it were different, I could add a <http-response> section to this policy and use <set-body> to set the response explicitly.
Now that I've created one resolver, I can easily create the others I need - one for each query and mutation.
It's common to have several resolvers that are similar to one another. They may only differ by the name of the API, for example. You can use the Clone feature from the Resolvers list to quickly create several similar resolvers that target different types and fields.
Validation, authorization, and other policies
Synthetic GraphQL APIs must have an API policy to validate the GraphQL request against the schema. In addition, you may want to add authorization rules, rate limiting, and policies for other API level concerns. To update the API policy, select the API Policies tab, then select the </> symbol to reach the XML editor. Here is the minimal policy:
<policies>
<inbound>
<base/>
<validate-graphql-request />
</inbound>
<backend><base /></backend>
<outbound><base /></outbound>
<on-error><base /></on-error>
</policies>
The only required addition is the <validate-graphql-request/> policy. This policy also handles field-level authorization rules for GraphQL. You can add either a <validate-azure-ad-token /> or <validate-jwt /> policy to decode an Authorization header, then use that information to implement authorization rules. Check out the examples in the policy documentation for more information.
Need a sample?
Our GitHub sample repository has quite a few example APIs for you to deploy via the Azure Developer CLI, including a Synthetic GraphQL API. This is a terrific way to spin up a set of coordinated samples for learning Azure API Management and Synthetic GraphQL.
Synthetic GraphQL is available now; we hope you'll take it for a spin!