Azure Functions with ComsosDB and GraphQL

If you’re looking to try out GraphQL with Azure Functions and CosmosDB, look no further.  I wanted to take the time to put together a fully functioning sandbox project which you can download and tinker with as there are a few snippets online, but nothing that pulled together all of the pieces, especially with the data loaders.

Why Am I Here?

If you are here, I presume you have already heard the gospel of GraphQL and you just want to figure out how to make it work with Functions and Cosmos.

For the rest of the readers, GraphQL is a “query language for your API”.  I like to think of it as an application layer that allows you to specify the types of objects you want by prototype.  In other words, tell your API the shape of the data you want, and your GQL layer converts that into a series of queries to whatever data store you are using and you get back what you asked for in the same shape.

GQL is not a magic bullet; it requires a lot of rather tedious work, in fact, to implement.  If you are familiar with FluentNHibernate and class mapping, then you already have the gist of what needs to be done to get GQL to work.  The essence is creating a set of mappings that describe how to satisfy the prototypes submitted as queries.

So Why Would I Want That?

There are three key benefits in my mind.

  1. Flexibility – You get a query entry point which is quite flexible and you can execute multiple types of queries from a single entry point.  In some cases, this can be very useful compared to having discrete, single purpose REST API endpoints.  In other cases?  I think it can actually be quite verbose and be somewhat more challenging to use than a simple set of parameters.
  2. Data Shaping – You get results which match the shape of the prototype.  For example, if you only ask for the id and name properties for an entity, then you only get those properties back.  If you are familiar with OData, there are a lot of similarities (though it turns out that there are folks with some very strong opinions on this topic).  There are many benefits including masking server side domain model properties from the front-end (which you can also do with a simple view model) and of course, reduced network throughput by only returning the properties that are needed at the UI.
  3. Reducing Roundtrips – The Data Loader concept allows batching of nested queries to reduce round trips to query for related objects which you want to display in a single view.  In general, its even more efficient to write a back-end query that performs the relational joins or additional lookups, but you lose the flexibility aspect since your front-end API is now tightly coupled to a specific back-end operation rather than a generalized batch load operation.

Depending on the nature of your application and your team, implementing GQL for your application can actually represent a significant overhead with only minimal benefit.  This is especially true, in my opinion, when you don’t really need the flexibility and your front-end teams are working closely with your back-end teams (or they’re the same team!) as you can build even more efficient mechanisms.  However, its easy to see how this could be an incredibly powerful framework to teams where the separation of responsibilities is very discrete as it allows the back-end teams, the application layer teams, and the front-end teams to provide a very flexible mechanism to build a variety of use cases using only a small number of interfaces between them.

In general, I think GQL seems to be a better fit for bigger organizations with more discrete teams and responsibilities producing Internet facing applications.

There are additional benefits as well and other patterns which can be built on top of GQL, but that is a topic for another day.

Running the Sample

If you’re following along at home, you can grab the full source code from here: https://github.com/CharlieDigital/AzFunctionsGraphQL (I strongly recommend doing so because there is a lot of code which I’m simply going to summarize here).  I assume you already have the CosmosDB emulator installed.

The example models a simple system where there are Stores which have Tools which are available to rent.  Each Tool belongs to exactly one Store.  In Cosmos, we could embed the Tool into the Store, but this could be problematic as the number of Tools in a Store is unbounded (which could lead to massive documents) and updating an individual Tool’s status would require a large quantity of RU/s.

To run the sample, create a ToolStore database in Cosmos and using Postman or another HTTP utility, issue the following query:

This will initialize the database with some sample data.

To query the database, try the following:

On to the Code!

Start by creating a new Functions project and installing the following packages:

We’re using the GraphQL.NET library.

Next, write your data access layer more or less how you would normally write it.  In this case, we’re using a repository pattern to encapsulate the actual queries to Cosmos.

Now on top of this, we layer GQL.  This requires that we create a set of artifacts:

  1. View model types (/Core/Model/GraphViewModels).  This is not strictly necessary; it is perfectly fine to use your domain model types if you’d like, but you will find a disconnect between your GQL layer and your domain layer entities without a set of view models.  For example, in your domain model, you may have an entity called Store which does not have a property Tools
  2. Mapping types (/Core/Model/GraphTypes).  If you’ve worked with FluentNHibernate, then you are familiar with mapping classes which describe to the ORM how the shape of the data maps from one layer to another.  This is precisely what the graph type definition classes do.  It’s important to understand that GQL is not an ORM like Hibernate or Entity Framework in the sense that it has no intelligence on how to look up relationships; such actions are always explicitly implemented in the mapping types.
  3. Query types (/Core/Model/Queries).  These classes define the universe of queries which can be satisfied by the GQL layer by defining incoming query parameters, mutations, and resolvers (e.g. functions which perform lookups).
  4. Schema types (/Core/Model/Schemas).  The schema represents the universe of operations and entities that the GQL layer operates on.  Our queries will be executed against a schema.

In the Functions startup, we register our dependencies:

It is tedious without some helper classes as you will need to add each mapping type class.

The Function itself is quite simple.  First our constructor accepts the injected dependencies that we’ll need:

Then we write a simple Function endpoint to accept our queries:

For service level authentication and authorization, check out my other article on Azure Functions and JWT.

The first piece of the GQL implementation of interest is the query:

This is a very barebones implementation and in fact, does not even provide the mechanism to load all stores.

Now that we have the mechanism to load a store, the question is how we load the tools.  I suppose that there are number of ways that this could work and differs by your underlying database and ORM.  If your ORM, for example, already has its own mapping layer that can lazy load the tools property, it could conceivably be done using your ORM directly rather than writing another query.  However, in this case, we need to add an implementation to our mapping class for stores:

This class and the function perform a batch lookup of Tools by Stores using the data loader pattern.  It’s simply a mechanism for batching the retrieval of nested dependencies to reduce the number of roundtrips to the backend.

Wrap Up

This sample isn’t exhaustive, but you should be able to clone, compile, and get it running to experiment with whether GQL is right for your project.  A big reason why I wanted to push this out there is because there is no complete implementation of Functions with GQL; hopefully, you found this useful! I think that teams have to evaluate carefully whether the benefits that GQL brings align with the structure of the team and the nature of the system.

You may also like...

1 Response

  1. September 21, 2020

    […] If you liked this article, you might also be interested in Azure Functions with CosmosDB and GraphQL. […]