Creating GraphQL Serverless Microservices

This tutorial shows a new approach to create a GraphQL API using a microservices architecture. The API is designed using GraphQL official language and is implemented using a library of @directives. The backend is implemented in Node.js but the same approach could be used in any other backend development technology such as Java, Python, and C#. An Express.js server is used to run the microservices in localhost for development purposes and the Serverless framework is used to deploy the microservices to production at AWS Lambda.

We have often used this approach to build microservices and APIs on the top of existing databases and legacy systems. Recently we decided to package this architecture into a new open-source and unopinionated project called Ant Framework which aims to make microservices development easy. The next section shows how to get started with Ant Framework.

Getting Started with Ant Framework

Ant Framework mainly consists of a CLI to speed up the development of microservices. It was written in Node.js (it can be used to create microservices with any backend technology though). Therefore the easiest way to install Ant Framework is through npm. Make sure you have the latest version of npm and Node.js installed in your machine and run the following command:

npm install -g @back4app/ant-cli

You can check if it was correctly installed just typing the ant command. The CLI help will be printed:

Once you have it installed in your machine, you can create a new microservice using the following command:

ant create MyService

Ant Framework will create a new GraphQL API microservice in the folder ./MyService. It brings a sample query called hello which receives a string parameter called name and returns a hello message. Use the following commands to start and play with your brand new GraphQL API:

cd MyService/
ant start

By default, Ant Framework will start a http server in http://localhost:3000 that will respond to queries and mutations. It will also start a web socket server in ws://localhost:3000/subscriptions that will respond to subscriptions. The Ctrl-C command can be used at any time to stop the servers.

Ant Framework will also automatically open GraphiQL in your default browser. Use the following query to test your new API:

query {
hello(name: "Luke Skywalker")
}

You will receive a result like the one in the picture below:

Nice! You have now a GraphQL microservice up and running in your localhost. The next section will show how this sample query actually works and how you can design your own GraphQL API.

Designing a GraphQL API

First, let’s try to understand how the previous section’s sample query actually works. Notice that Ant Framework automatically created two files in your microservice folder.

The ant.yml file contains all the parameters that Ant Framework needs to know in order to have your microservice up and running. This tutorial will not concentrate efforts in explaining all the details of it, but you can have in your mind that in this file you can customize the default behavior of your microservice and change settings such as the server port, the local server bin and the plugins that are installed.

The model.graphql file contains your GraphQL API model and it is there that you will start designing your own queries, mutations, and subscriptions. This file has to be written according to the GraphQL Official Language Specification which can be easily learned in GraphQL Official Web-Site. Ant Framework created for you an initial file whose lines are mostly commented to give you a sense of where you need to implement your queries, mutations, and subscriptions. It also brings a working version of the sample query hello that we tested in the last section.

So let’s focus only on the lines of code that makes the sample query actually work:

schema {
query: Query
}
type Query {
hello(name: String = "World"): String @mock(with: "Hello {{name}}!!!")
}

This schema model defines a GraphQL API that contains a single query called hello. This query can receive a single parameter called name of type String whose default value is “World”. Finally, this query will return a String value that will turn out to be the hello message.

Note that we are also using a directive called @mock and that is what really makes the sample query work. Ant Framework provides a library of directives to make the backend implementation easier for any GraphQL API you design. The mock directive is used to easily create fake values for your API. It receives a parameter called with that can contain any Mustache valid template. The directive will render this template with the query parameters and return a fake value as the result of the API call. It means that in minutes you can design your whole GraphQL API and have it up and running with fake values for the frontend development team start working while the backend development team is still implementing the functionalities.

Now using this same idea let’s create some other operations for this API. Use the following code in model.graphql file and restart the server:

schema {
mutation: Mutation
query: Query
}
type Mutation {
addTopic(title: String): String @mock(with: "FakeId")
}
type Query {
hello(name: String = "World"): String @mock(with: "Hello {{name}}!!!")
getTopic(id: String) : String @mock(with: "Some Topic")
}

Use the following code to test the addTopic mutation:

mutation {
addTopic(title: "Some Topic")
}

Test your queries with the following code:

query {
hello(name: "Luke Skywalker")
getTopic(id: "SomeId")
}

Now that you have learned how to design your GraphQL API and create mock values for each of its mutations, queries, and subscriptions, the next section will show how to implement each of these operations.

Implementing GraphQL Operations with Serverless Functions

Let’s now create a real implementation for the hello query. The easiest way to do that is using the following command:

ant function add queryHello

Ant Framework has created a queryHello.js file in your current folder and added this new function to your ant.yml file. By default Ant Framework uses Node.js as the runtime for your functions, but you can also use any other backend development technology using (or creating your own) specific runtimes. The new file comes with a sample code. Let’s take a look.

Note that it is a regular Node.js function that receives a parameter called name and returns a hello message String. There is no special requirements for the function handlers what makes the code that you write when using Ant Framework to be agnostic of cloud providers and API servers.

Now we will modify the model.graphql file to make it call this function instead of having a mock value. This task can be done by adding the @resolve directive which receives a parameter called with containing the name of the function that is responsible for resolving this operation. The hello query in the Graphql model now should be like this:

hello(name: String = "World"): String @resolve(to: "queryHello") @mock(with: "Hello {{name}}!!!")

Let’s restart the server and test the queries again. You will notice that now it returns a new hello message indicating that it is coming from the queryHello function implementation.

We can use the same idea to implement the addTopic mutation and the getTopic query. As an example, we will save each of the added topics on a JSON file and get the topics from there as well.

First, we will update the model.graphql file that will now be like this:

schema {
mutation: Mutation
query: Query
}
type Mutation {
addTopic(title: String): String @resolve(to: "mutationAddTopic") @mock(with: "FakeId")
}
type Query {
hello(name: String = "World"): String @resolve(to: "queryHello") @mock(with: "Hello {{name}}!!!")
getTopic(id: String) : String @resolve(to: "queryGetTopic") @mock(with: "Some Topic")
}

Let’s create the new functions we need with the following commands:

ant function add mutationAddTopic
ant function add queryGetTopic

We can now modify the mutationAddTopic.js file and use the following code:

const path = require('path');
const fs = require('fs');
module.exports = ({ title }) => {
let topics = {};

const topicsPath = path.resolve(process.cwd(), './topics.json');
if (fs.existsSync(topicsPath)) {
topics = require(topicsPath);
}
  const id = Math.random().toString(36).substring(2,6);
topics[id] = title;

fs.writeFileSync(topicsPath, JSON.stringify(topics));

return id;
};

Finally we will modify the queryGetTopic.js file and use the following code:

const path = require('path');
const fs = require('fs');
module.exports = ({ id }) => {
let topics = {};
  const topicsPath = path.resolve(process.cwd(), './topics.json');
if (fs.existsSync(topicsPath)) {
topics = require(topicsPath);
}
  return topics.hasOwnProperty(id) ? topics[id] : null;
};

Restart the server and play with your new operations.

Now that you have learned how to implement your GraphQL API operations with Serverless functions, the next section will show you how to deploy it at AWS Lambda using the Serverless framework.

Deploying the Microservice to AWS Lambda using Serverless Framework

First of all, make sure that you have the AWS CLI installed in your machine. Then set up your AWS account credentials using the aws configure command.

aws configure

Use the following command to deploy the microservice:

ant deploy

That’s all! Ant Framework will use Serverless Framework to deploy your functions to AWS Lambda and print both your GraphQL and GraphiQL endpoints.

Note that Ant Framework automatically deploys each of your serverless functions as a separate AWS Lambda function. In this example, you should turn out having 5 functions as shown in the picture below.

Giving your Feedback

We are working on this new approach for creating GraphQL Serverless Microservices and we would love to receive your feedback. Feel free to leave your comments here and please fill out the form below. Let us know what we need to improve to make microservices development easy with Ant Framework.

Leave your Feedback Here