Generate your Apollo DataSources

Using GraphQL as your aggregation layer for your microservices is great, but writing all the code for connecting to them is not. Not only that part is boring, error-prone, it also gives you untyped code which could (and will!) result in runtime errors.

Our vision at is to

"eliminate the problematic and mundane so the interesting can flourish."

Let me show you how you can use Chimp DataSources generator to achieve just that.

Continue reading or watch the video version here:


If you want to follow along, please clone this repository and start the monorepo app: Our example is straightforward so you should be able to get the idea by watching the video or just reading through the article. If you are in hurry you can also skip directly to "Generate" section

We have a standard java spring microservice. We will use two of its endpoints. One adds a list, another returns all lists.

Let's first hit them manually, first we can see that the list is empty to start with:

curl http://localhost:8090/api/lists

We can add a list:

curl --request POST  --url http://localhost:8090/api/lists \
--header 'cache-control: no-cache' \
--header 'content-type: application/json' \
--data '{"text": "First List"}'

And verify that it was added:

curl http://localhost:8090/api/lists
{"id":"b781fdbb-96ba-4057-b41d-4bf6dc5750c9","name":"List one","createdAt":"2020-09-29T08:58:37.225276"}

Now let's create our GraphQL app.

npx chimp gql:create list-graphql

Then enter that directory and install dependencies.

cd list-graphql
npm install

Now let's install our generator and the apollo-datasource-rest package

npm install --save apollo-datasource-rest
npm install --save-dev chimp-datasources-generator 


With the tooling in place we can finally generate the data sources:

npx chimp-datasources-generator create generated/api http://localhost:8090/v3/api-docs.yaml

We need to add our newly generated DataSource to our app, in our scaffold the function that is responsible for that is in src/dataSources.ts

import { Controllers } from "@generated/api";

export const dataSources = () => ({
  listsApi: new Controllers("http://localhost:8090/"),

Now let's create a schema with our Query for getting all the lists:

type List {
  name: String!

extend type Query {
  GetAllLists: [List!]!

Now we run the generator to create all the code necessary for implementing that new Query:

npm run graphql:generateAll

You should see a new directory called "queries" with two files GetAllListsQuery.ts which is the resolver for our Query, and GetAllListsQuery.spec.ts which is a specification for it.

In the spirit of TDD let's start with the test. In-line comments explain

test("GetAllLists", async () => {
  // Mock that will simulate the GqlContext
  const context = td.object<GqlContext>();

  // An array with one example list.
  const lists = [{ id: "id", name: "someName", createdAt: new Date() }];

  // Now the interesting part
  // We tell our mock to resolve with that list,
  // when the controller getLists method is executed without arguments 

  // Now we execute our Query, injecting the context with our mock.
  const result = await testGetAllLists(context);
  // And make sure that what we got matches the response from the controller.


That test will fail with "Error: not implemented yet" which comes from our resolver, let's implement it then.

Easy. Being guided by the test we know that we should call the controller getLists method without any arguments, and pass the data through.

export const GetAllListsQuery: QueryResolvers["GetAllLists"] = (
) => context.dataSources.listsApi.TodoListControllerApi.getLists();

Now for the slightly more complicated, mutation.

Let's add it in our schema

# ...
extend type Mutation {
  AddList(name: String!): List

And generate the necessary code:

npm run graphql:generateAll

Again, we will start with the test for the mutation:

test("AddList", async () => {
  const context = td.object<GqlContext>();

  // Name for our new list
  const name = "New Name";
  // And the object that we will expect to get back from our mutation
  const newList: ToDoList = { id: "", createdAt: new Date(), name };

  // We set up our mock to resolve to that list when createList is called with
  // an object having text field set to name
      text: name,

  // We set our variables to be an object with the name.
  const variables: MutationAddListArgs = { name };

  // Execute the mutation and verify the result
  const result = await testAddList(variables, context);


And now for the implementation. This time we are using args to pass the name to the controllers createList method.

export const AddListMutation: MutationResolvers["AddList"] = (
) =>

Note - everything is typed. You can try to use args.text instead of, mistake the createList method, or just call it without returning. Same goes for the test - you can try to create the variables object that doesn't match the GraphQL Schema, or resolve an object with a different shape than the one defined by createList method - you could make tests like that pass ignoring the types, but the mutation would fail run-time.

With everything setup let's make sure that our code actually works!

$ PORT=1234 npm start
🚀 Server ready at http://localhost:1234/graphql

Let's open the graphql playground and run our query. The combination of tests, types, and generated scaffolding gives us a very high confidence that things will work as expected.

  GetAllLists {

And mutation:

mutation {
  AddList(name: "Hello") {

You can see the resulting code here:

This is clearly a very simple example, but more complex cases should be as straightforward. The actual complexity will come from your business logic, not from setting things up and glueing them together.

We've effectively removed the mundane. It's your turn to make the interesting (complex, valuable from business standpoint) flourish!

Let me know if you have any questions or thoughts in the comments below.

Let us help you on your journey to Quality Faster

We at specialize in helping our clients get more for less. We can get you to the holy grail of continuous deployment where every commit can go to production — and yes, even for large enterprises.

Feel free to schedule a call or send us a message below to see how we can help.

User icon
Envelope icon


Schedule a 30m call
  • Better way to use useReducer with TypeScript

    Introducing useComplexState hook, - a small wrapper combining Redux Toolkit, useReducer with some extra syntax sugar to make things even simpler and more to-the-point.

  • Dealing with randomness in tests

    A few patterns to help you deal with randomness in your codebase.

  • How to deal with the yarn link "There's already a package called x registered" error

    Short discussion about helpful error messages, and a solution to the problem