Tutorial: Transform a REST API to GraphQL
Create a GraphQL API for REST interfaces by letting API Connect for GraphQL introspect the REST endpoint and generate a GraphQL schema.
This tutorial shows how to use the stepzen import curl command on an existing REST
API. This command sends a curl request to API Connect for GraphQL and parses the GraphQL types
from the JSON response.
Working with GET requests
Use the API Connect for GraphQL CLI to create
a GraphQL schema for your new GraphQL API. Complete the following steps to perform this process
using our example customers API:
Generate your schemas and resolvers using the following API Connect for GraphQL CLI command:
stepzen import curl https://sample-api.us-east-a.apiconnect.automation.ibm.com/api/customers --query-name "customers" --query-type "Customer" --name "customers"
Let's take a look at what the stepzen import curl command does:
- API Connect for GraphQL issues the
curlcall (we support most, but not all,curlflags, as explained in the following section). - It converts the returned JSON into a set of GraphQL types.
The
rootof the returned JSON was given a special name, in thisCustomerEntry, based on thequery-typeflag. This flag is optional, but it is good to give a meaningful name for therootbecause otherwise, API Connect for GraphQL will name itRootEntry.If you execute two different
stepzen import curlcommands, theRootEntrywill be a type name clash. Other types get fairly good names based on thenamein the JSON structure. If you are worried about type name clashes on them, you can use a--prefixflag (more on that later). - It then generates a query with the value of the
--query-nameflag so that when you execute the query, it will parse the returned JSON into the correct GraphQL structure.While this flag is optional, it is highly recommended because otherwise, the default name would be
myQuery, and not only is it not a great name, it will clash with any subsequentstepzen import curlcommands that you run. - It stores the generated schema in a directory with the name given in
nameflag. It is optional, and it will create a directory that starts withcurl(for the first one, andcurl-01etc., for each subsequentstepzen import curlcommand). - It creates an
index.graphqlin the root of your working directory containing the path to which the schema file is generated.
Just one command did all this for you!
- Using
curlwith headers -
API Connect for GraphQL stores all headers in a
config.yamlfile. The followingcurlcommand contains a header, so API Connect for GraphQL creates a file namedconfig.yaml:stepzen import curl https://api.foo.com/bar -H 'Authorization: Bearer 347ack988dkey'The
config.yamlcontains an entry for the header, which looks like the following example (there might be other entries in it as well):configurationset: - configuration: name: some-auto-generated-name Authorization: Bearer 347ack988dkeyAPI Connect for GraphQL also modifies the generated schema to create a query like the following example:
type Query { myQuery: RootEntry @rest( endpoint: "https://api.foo.com/bar" configuration: "some-auto-generated-name" ) }This ensures that your secrets (typically contained in headers) are stored separately from your code (your schema file).
Even if you do not have headers but are sending an
apikeyor something else in theendpoint, you can move it to theconfig.yamlfile. - Using
curlwith query parameters API Connect for GraphQL automatically converts all
https://api.foo.com/bar?name=anant&country=UScurlquery parameters to arguments in the GraphQL query that is generated. For example, the request:generates the following query:
type Query { myQuery(name: String!, country: String!): RootEntry @rest(endpoint: "https://api.foo.com/bar") }Where did the
queryarguments go? In API Connect for GraphQL, GraphQL query arguments are appended to the endpoint URL as query string parameters. The generated query has the same effect as the following query that includes the arguments, but gives you more control:type Query { myQuery(username: String!, country: String!): RootEntry @rest(endpoint: "https://api.foo.com/bar?name=$username&country=$country") }- Preventing query parameters and arguments from being appended to the endpoint
-
You can specify rules to prevent API Connect for GraphQL from auto-appending the GraphQL query arguments and query string parameters. For example, you might want to suppress the parameters or arguments in the following situations:
- When there is already a query parameter.
For example, you can run the following command:
stepzen import curl "https://api.foo.com/bar?name=anant&country=US"`and then edit the SDL to only allow queries on
country=US:type Query { myQuery(name: String!): RootEntry @rest(endpoint: "https://api.foo.com/bar?country=US") }The
namewill not automatically be added as a query parameter because the endpoint URL does not use the required format of&name=$nametype Query { myQuery(name: String!): RootEntry @rest(endpoint: "https://api.foo.com/bar?country=US&name=$name") } - You want to suppress parameters because they might be used in
pathorheaders.In this case, edit the
config.yamlas shown in the following example. Add the attributestepzen.queryextensionguardand set it totrue, which instructs API Connect for GraphQL not to automatically append query parameters to the request.configurationset: - configuration: name: "config" stepzen.queryextensionguard: true
- When there is already a query parameter.
- Using
curlwith path parameters -
API Connect for GraphQL can automatically convert path parameters into arguments. For example,
stepzen import curl https://example.com/users/jane/posts/12 --path-params '/users/$userId/posts/$postId'generates a query in GraphQL like the following example:
type Query { myQuery(userId: String!, postId: Int!): RootEntry @rest(endpoint: "https://example.com/users/$userId/posts/$postId") } - Adding arguments to the generated schema
-
The
@restdirective that is generated fromstepzen import curlcan be customized with additional arguments to help you shape the REST response to the requested GraphQL type:transformsto modify the responsefilterto filter the outputsettersandresultrootto change names of fields, and so on.
For more information on the configuration options, see "Shaping the HTTP response" in Connecting a REST service.
Working with POST requests
When using stepzen import curl for a POST request, parameter
substitution gets applied to the body of your request.
- Treating all fields in the
POSTbody as query arguments -
Consider a simple case:
stepzen import curl 'http://dummy.restapiexample.com/api/v1/create' -d '{"key1":"value1", "key2":"value2"}' -H 'Content-Type: application/json'The
curlrequest uses a default header that contains-d(or--data-raw) isContent-Type: application/x-www-form-urlencoded.Because GraphQL APIs rely on JSON, you must append the header
-H 'Content-Type: application/json'to use JSON in your post body.The example
stepzen import curlcommand generates a query in GraphQL like this:type Query { myQuery(key1: String, key2: String): RootEntry @rest( method: POST endpoint: "http://dummy.restapiexample.com/api/v1/create" ) }The argument
methodwith the valuePOSTis included in the@restdirective.The JSON keys from the body are transformed into query arguments.
- Generating a mutation from a POST request
When using
stepzen import curl, the default GraphQL operation type isQuery. To change the import into aMutation, manually update the schema. For example, modify the following schema:
by changing thetype Query { myQuery(key1: String, key2: String): RootEntry @rest( method: POST endpoint: "http://dummy.restapiexample.com/api/v1/create" ) }Querytype to aMutationtype:type Mutation { myQuery(key1: String, key2: String): RootEntry @rest( method: POST endpoint: "http://dummy.restapiexample.com/api/v1/create" ) }The
stepzen import curlforPOSTdoes not handle more complicated parameter substitutions. For an overview of all the configuration options, see "Setting the HTTP request body" in Connecting a REST Service.
Best practices
- Working with multiple
curlrequests - You can issue as many
curlrequests as you want. As a matter of best practice, use the following parameters to ensure that each request uses unique values:- Use
--nameto specify a unique directories for each importedcurlrequest. - Use
--query-nameto assign unique names to the queries so that you avoid query name clashes. - Use
--query-typeto give theRootunique names to avoid root name clashes. - Use
--prefixto prefix all non-root types.For example,
--prefix Custmakes all types have aCustprefix, and this avoids type conflicts with other schemas.
- Use
Undo a stepzen import curl command
To a stepzen import curl command, you should:
- Delete the generated directory.
The directory has the name you specified with
--nameflag, or withcurlorcurl-xx. - Edit the
index.graphqlfile in the root of your working directory, and remove the entry for the directory that you deleted in step 1.
That's it. Then run your stepzen import curl command again.