I have been working on a customer project to build a new eCommerce platform with GraphQL API. We ended up using GraphQL API instead of REST API, because there were multiple backend services that needed to be used and compose the actual API for the frontend. We also needed to reduce loading times, as the frontend would only send one request instead of multiple requests for different REST endpoints. We could have used custom endpoints for the frontend, but that would have been a maintenance burden. It would have required updates for each change, when the frontend needs new data or because of some other reasons.
We started to build the API using Apollo GraphQL server and Typescript. We had the first versions running in no time to support frontend development. We started to add features and data structures one at a time.
After a while, as we were adding new features and data structures, it was obvious that we needed to keep the code base modular and split the main types into their own components. It was difficult to find a good way to split the code and schemas, and not to introduce a custom solution and complexity when using the Apollo server.
I tried to split the code so that we had resolvers and schemas in their own folder for each main type and merging them at a higher level, but there were some modules whose dependencies were too tight. I also tried to use schema stitching, which introduced its own problems, like resolvers were not executed when another schema called this one.
One issue was also with the request context, that it was defined in one place in the Apollo server configuration when the server was created. We had multiple things that we needed to pass to modules via the context object, like getting user data and language selection etc. All those were coming via the HTTP headers, so we needed to parse it and pass it along as context. There were things that only one module needed, so defining them at the main application level didn’t seem to be the correct solution.
Also testing in both solutions was a little bit difficult. You could do unit tests for resolvers, but couldn’t really test the schema itself, unless you loaded the whole application. So the module couldn’t really test its own schema.
One day I came across a post about a new library called GraphQL modules. The name already told me that this was something that I had been looking for, so I went ahead and started to learn more about it.
GraphQL modules, as the name implies, was just the thing we were looking for. Its purpose is to make it possible to split your GraphQL server into modules and also have clear dependencies between modules.
A basic application with GraphQL modules can be defined like this:
Not that much different compared to creating a schema for an Apollo server.
The power of GraphQL modules comes with splitting the application into modules, and defining dependencies between the modules and providers (more about providers in the next chapter). Importing a module as a dependency can be done like this with the imports property:
A module can import other modules and schemas that will be automatically merged. A common way is to define one application module, which only imports all the needed higher level modules for the application:
GraphQL modules introduce dependency injection, and providers are classes, functions, configuration or other values that can be injected to resolvers or other providers. Providers are defined in the module and modules can also get them from the imported module, so you can create a module with shared services, like a library to be imported.
It is possible to manage provider lifetime with the decorator configuration option `scope`, and there are three options available:
- Application – Singleton, created once for the application.
- Session – Created for each GraphQL request.
- Request – Created for each injector request.
This helps to manage things like connection pools for databases or create ids for logging to group by client request etc.
With the plain Apollo server, testing was mostly unit tests that tested / called single resolver functions. If you wanted to test resolvers through the GraphQL schema, most of the time you ended up loading the whole application.
With GraphQL modules it is easier to test at the module level and not only at the function level. Each module is a complete application, so tests can load them easily and run queries against the module. Dependency injection also makes testing easier as tests can mock services that the injector will return. Below is an example of how to load a module mock services, and execute a query in a test case for a module that is in the providers section.
With the ability to test at the module level, you can have a better view of what data is coming in, as well as its format. GraphQL schema validation is done before resolvers are executed, and by running the queries in test cases, you can make sure that the resolvers are executed as expected and with the “real” inputs from the query.
After finding the GraphQL modules, it has been my number one choice when starting to build new GraphQL servers. Coding is much more straightforward when code can be split into its own modules.
Code reusability is also higher when you can create modules that are just libraries that can be imported. There are many common things that different applications have, like getting user tokens from headers or getting user language settings etc. You can have common modules for those things that different applications share.
One caveat when starting with GraphQL modules and Typescript is the need to setup the ‘reflect-metadata’ library. It must first be imported into your application to ensure that type definitions work with GraphQL modules. It also needs to be in your test files. Nevertheless, it is just one line that needs to be added as the first thing in the file.
To make type definitions easier to split, I have also used a library called `graphql-import`. It makes it possible to import `.graphql` files and use type imports in the schema definitions.
The documentation of the GraphQL modules is really good with clear examples, so you should definitely read more from there.
I also created an example project, based on my colleague’s demo application built on AWS AppSync, where you can see these in action: https://github.com/KariHe/demo-graphql-modules-in-gcp