Skip to content

Steps to Add New API Functionality and Expose as GraphQL

jkester1986 edited this page Sep 7, 2018 · 2 revisions

Steps to add and use new API functionality with graphql

To add new API functionality, you need to:

  1. Add the api call to our batch client api javascript library
  2. Update our graphql schema with the query/mutation that the api represents, and update the schema appropriately to handle the request and response data structures
  3. Make your new schema definitions executable by tying the query/mutation schema definitions to their implementing API calls in the batch client

For the purpose of an example, we'll pretend we are adding the GetTask SOAP command from the Zimbra API as a new graphql query

1. Add the api call to our batch-client api javascript library

You can use the javascript library as an auto-batching library without using graphql at all by using the batch-client library which is located at src/batch-client/index.js. Our GraphQL implementation uses this as well as the backing function for resolving graphql queries/mutations.

Naming Convention: We use the camel-cased version of the SOAP command as the name for the batch-client function and the graphql query/mutation to make it easy to go from the SOAP docs to the api client, e.g. the GetTask SOAP command would be the getTask api function/graphql query.

For our example, the implementation in batch-client would look something like:

const getTask = ({ id }: GetTaskOptions) =>  //we use typescript in the api client, so define GetTaskOptions in the types file
  this.jsonRequest({ 
	name: 'GetTask', //The name of the SOAP command request; 'Request' will automatically get appended
	options: {id }, // variables to pass to the command, denormalized to match name that SOAP command expects
	normalizeResponse: ({ task }) => normalize(task, CalendarItem) //function to normalize response before returning to caller
});

A note on normalization/denormalization: Rather than using the terse variable names that come back from the SOAP commands, we like to use more human readable variable names in our request/response objects to the API js client, which you'll see reflected in the graphql schema. Rather than hand-normalize/denormalize the request and response objects, please use the denormalize function for requests and normalize function for responses with the appropriate entity definitions from src/normalize/entities to do it, which will be more re-usable and maintainable.

Once you've done this, the API function is easily callable by a non-graphql

2. Update our graphql schema

To work with graphql, we need a schema that defines queries and mutations we can make, and the data structures for the request and responses of those queries/mutations. Our graphql schema is located in src/schema/schema.graphql

Start by adding the query/mutation to the type Query { or type Mutation { blocks that your api represents, and then build out any types, enums, etc. that are necessary to create the request/response data for your query/mutation that don't already exist.

For full details on graphql schema definitions, see graphql.org

For our example, assuming the CalendarItem definition already exists in the schema (which represents a response type for a Task), we would add a new query called getTask that takes in a required variable called id of built-in type ID and returns our internally defined type of CalendarItem

type Query {
	... # other queries
	getTask(id: ID!): CalendarItem  # our new query takes a required argument called "id" of type ID and returns a CalendarItem

3. Make your new schema queries/mutations executable

The types/queries/mutations you created in step 3 are just descriptions/definitions right now. They are not useful/executable yet. We need to tell the library how to actually get results/make something happen when someone runs a query of getTask. To do that, update src/schema/schema.ts.

You need to add an entry in the Query resolvers for your new query or in the Mutation resolvers for mutations.

For our example, we would modify the Query resolvers to call our new batch-api method. Read the graphql-tools documentation on the resolver function signature for full details, but in this case we only use the second argument which are the arguments passed to the function as defined in the query we created earlier

resolvers: {
  Query: {
    ..., //other query resolvers
    getTask: (_, variables) => client.getTask(variables as GetTaskOptions), // tell typescript that the variables are of type GetTaskOptions

That's It! You are now ready to use the getTask graphql query to fetch task details from the API