Where to host GraphQL
A GraphQL API can be served in many ways. As part of a monolith application, through a serverless function, as a standalone server, running as a function on a CDN, as federated services, or straight up on your client.
I will go through each of these alternatives with some pros and cons you should consider before starting.
This of course is based on the notion that you are running your application from a monolith. By monolith in this case I mean something like a Ruby on Rails or a Laravel server.
- It is easier to make optimizations when it comes to data fetching. A call to a database is faster than an HTTP request, which reduces problems like waterfall while resolving.
- While developing new features, you can straight up implement the GraphQL API instead of first developing a REST endpoint, and then the GraphQL API.
- Backend developers are more aware of what data actually is requested. This makes it easier to refactor the monolith.
- You are locked into the monolith further, making you less flexible to changing architecture as needs arise. A GraphQL proxy could otherwise be a great tool to increase flexibility during rewrites.
- You need to learn how the monolith works in order to work on the GraphQL API.
Consider this if you need to make very specialized queries to fetch your data efficiently. Otherwise I do not recommend this.
A serverless function in this case is being referred to as a deployed unit of code, with a single entrypoint that is being run on demand. Usually they scale great.
- Since GraphQL is being served through a single entrypoint anyway, you don't have to deal with some of the problems you otherwise have to deal with when running serverless functions.
- More resilient to memory leaks and other potentially fatal errors.
- Having a connection to a caching server like Redis is hard. You might run out of connections, or struggle with setting up the proper access in AWS.
- Subscriptions is a pain. Since subscriptions require a long running connection it is much harder to get working when you are basing your GraphQL architecture around serverless functions. If you need GraphQL subscriptions in your application I would recommend not using serverless functions.
Use this if you are familiar with serverless functions and you don't need GraphQL subscriptions.
This is probably the most obvious and in most cases easy way to go. This means serving your GraphQL API through a long-running process which is always ready to respond to requests. The typical example is something like
- Can be more expensive to run compared to other ways.
- Harder to scale than serverless functions
- Not that resilient to failures, and since all data go through the GraphQL proxy the impact of failures are usually severe.
If you have a small budget, are not yet clear on your scaling needs (or if they are low), or are not familiar with serverless functions, or if you need subscriptions: this should probably be your go-to.
Running as a function on your CDN
Many CDN providers like Cloudflare or Cloudfront (AWS) lets you run a function when users request data on the Edge. That is before it hits a central server. It is possible to run your entire GraphQL API in such a function.
- The potential response time is great.
- Resilience to data centers having issues.
- Longer response times from your origin than if the GraphQL server and underlying services were in the same data center.
- More limitations when it comes to places to cache data.
- CDN functions usually have more limitations on them compared to other serverless functions. Such limitations could be in aggregated logging or configuring environment variables.
Consider this if you have found a good caching mechanism from your provider, if your data is mostly served from different servers spread across the world.
If you are serving people from mostly the same location in the world, you should avoid this as the main benefit would be gone.
Federated services or Stitching
Apollo has come up with something they called Federation. It means that you implement many GraphQL servers, which can extend other types from other services with their own fields, or provide their own types. This is like Schema Stitching, but declarative. They are similar enough in my book to have the same recommendation.
- Can make it easier to work on the same schema from multiple different teams.
- Increased flexibility in design of GraphQL services. If a team wanted to write their GraphQL service in Ruby and another in Typescript, both can have their ways.
- Harder to debug performance and/or bugs.
- More expensive to run
- At the time of writing it does not support subscriptions.
- Distributing a system increases complexity.
Consider this if you have a large organization which is already distributed among many cross-functional teams. Otherwise try to avoid as it provides few other benefits.
Straight up in your client
It is possible to run resolver logic inside of your application. This will provide very little technical benefits compared to just firing off REST calls, but if you are not allowed to create a server for some reason it could be considered.
- Possible to use the GraphQL type system even when you don't have a server
- Less obvious than firing REST calls.
- You have the same amount of REST calls as you would otherwise have, with the same waterfall problems as GraphQL was invented to solve.
Don't use this approach. If you can't use a server odds are you are better off using your energy to making the REST calls as good as they can be.