GraphQL - Use case and Architecture
This blog is a part of a series on GraphQL where we will dive deep into GraphQL and its ecosystem one piece at a time
- Part 1: Diving Deep
- Part 2: The Usecase & Architecture
- Part 3: The Stack #1
- Part 4: The Stack #2
- Part 5: The Stack #3
- Part 6: The Workflow
In the last blog post, we explored the various questions one might have when starting off or working with the GraphQL ecosystem and answered them. Now that justice has been done to clear the clouded thoughts you might have, let's dive into the next important step in this blog.
In this blog, we will start looking at how your architecture can look like when working with GraphQL and its ecosystem.
Your architecture hugely revolves around your usecase, and you have to be very careful in getting it right and take proper consultation if needed from experts. While it is very important to get it right before you start, mistakes can happen, and with a lot of research happening these days, you can often find any revolution happen any day which can make your old way of thinking obsolete.
That is why, I would highly recommend you to Architect for Change and make your architecture as Modular as possible so that you have the flexibility to do incremental changes in the future if needed. Let's just talk about architecture in context with GraphQL here. We will explore more deeper into the rest of the architecture in an another blog post.
There are some things you would have to think of before starting your journey.
- Am I building a monolith or am I working on microservices? Remember that monoliths still have a huge place in today's world given the complexity which comes with Microservices as long as your project is small.
- What does my deployment target going to look like? VM, Containers or Bare Metal?
- What is going to be my orchestration layer? Kubernetes, Mesos, Swarm or OpenStack?
- What are my scaling needs?
- What is the performance that I expect?
- Do I need Offline support?
- Cloud or On-Premise?
- What is the programming language which makes sense for my usecase?
This list is incomplete. There are more questions like these which you might want to answer yourself and answering this can give you a lot of clarity as you start building your architecture.
The Ingress / Load Balancer
This is the first layer that any client would typically hit before making requests to your GraphQL service. This acts as the single entry point for all traffic (it can be regional as well depending on your use case).
This would be the first thing you would have to setup before getting started and this is also the layer which handles things like SSL termination, caching (in case you have a CDN setup) and so on.
If you are in the Kubernetes world, you also have a lot of ingress controllers like Nginx Ingress (opens in a new tab), Ambassador (opens in a new tab), Kong (opens in a new tab), Contour (opens in a new tab) and so on which can help.
The API Gateway
The first thing would be the entry point of all your GraphQL requests. Since GraphQL exposes a
single endpoint e.g.
/graphql this becomes the single entry point for all your operations.
But, I highly wouldn't recommend directly exposing your service to client since it can be unsecure, difficult to manage things like rate-limiting, load balancing and so on.
Rather, it is always recommended to expose it via an API Gateway of your choice. Be it Ambassador, Kong, WSO2, Apigee or anything else for that matter. This can also act as sort of kill switch or can also be used for things like filtering and moderating traffic whenever needed.
The GraphQL Gateway
As you evolve, you might end up having multiple services or might even move to the microservices world to enable scale. Now, this means multiple services with its own GraphQL schema, logic and so on.
But unlike REST, GraphQL exposes a single endpoint irrespective of the underlying services. This is where a Gateway plays a major role and comes in at the next layer of our architecture. The role of orchestrating or composing (both are different) multiple services and schemas together, delegating queries and mutations to the respective microservices and all of this without the client having to worry about the complexity underneath.
While you may choose to go for different architectures like Schema Stitching (opens in a new tab) or Federation (opens in a new tab) depending on your use case, do remember that sometimes, this may be an overkill. You might not even need a GraphQL Gateway to start with if you are building something small and this can reduce a lot of complexity.
The GraphQL Service
The next thing to think of would be the GraphQL service itself (be it a monolith or microservice). Each service would be responsible for a part of the complete data graph as seen in Federated Implementation (opens in a new tab) and this will make things easier to scale. Note that the way you implement it can be different as discussed (Schema Stitching or Federation).
You might also want to modularize your project structure and code within the service and this is applicable irrespective of whether you use a monolith or microservice to maintain clear separation of concerns, make everything composable and modular as possible.
While you can end up discovering your own way to do it (I initially went down this path), but what is the use of re-inventing the wheel when you have something like GraphQL Modules (opens in a new tab) which can help you with this.
You might also want to get your tooling right to reduce as much work you do as possible. Be it linting and validation, code generation, testing, and so on so that you automate most of your workflow, and you stay productive while working on any part of the service.
The Mode of Communication
Now that you have thought about the service(s), you might also want to think about the mode of communication in between them which is essential to pass data to and fro, synchronously and asynchronously. This also presents some questions which you might want to answer first before starting.
- https (1.1 (opens in a new tab), 2 (opens in a new tab) or 3 (opens in a new tab)) or grpc (opens in a new tab) (over http/2) or Thrift (opens in a new tab) or Websockets (opens in a new tab)?
- Do you need a Service Mesh (opens in a new tab)?
- Is GraphQL going to be used for communicating between services?
- Do I need something like MTLS (opens in a new tab) for securing inter-service communication?
- How do I do asynchronous communication? Do I use event queues like Kafka (opens in a new tab), RabbitMQ (opens in a new tab) or NATS (opens in a new tab) ?
Again, all of these depend on your use case and hence, there is no definite answer to this. But, try to go for a protocol which offers you less latency, great compatibility with built-in support for things like compression, encryption and so on.
These matters cause while all the clients would communicate with the GraphQL endpoint you expose, you still would have to have some sort of efficient way to do inter-service communication.
Even if you are going to communicate between your service with GraphQL (which is what I do), you still have to decide how you transmit the GraphQL queries and mutations in between them.
Authentication & Control
Like we discussed in the previous blog post, there are various ways to do authentication and authorization. You might want to consider them as well while architecting cause this will decide how chatty your services will be when doing operations, how secure will it be, and so on. There are various ways as we spoke about, both stateful and stateless. While stateless would be better for scalability, you might want to choose what works best for you.
Depending on your use case, you might also want to decide if you need something like persisted queries or not. This can prevent clients from sending queries which are not authorized, prevent huge amounts of GraphQL data from being passed over the wire, and so on.
And then comes the backend which you are going to use to store/retrieve data from. There are a huge number of options out there and to be honest, there is no one database which fits all use-cases. And they even come with different variants — SQL, NoSQL, Search, Time Series and even Graph Databases. You can refer DBEngines (opens in a new tab) for a complete list.
And you can even put a GraphQL layer or ORM on top of all of them if you want and take the complexity away from the services (e.g. with Prisma 2 (opens in a new tab) or GraphQL Mesh (opens in a new tab)).
You might also want to look at how you minimize the amount of calls you make to the main database. Do you need caching and have it setup? Have you addressed the N+1 problem with Dataloader (opens in a new tab)?
Now, there are a lot of other things you might want to have in your architecture like Hybrid Cloud support, CI/CD pipelines, caching and so on. We will probably explore them in future blog posts as we go along.
Remember to keep your stack as simple as possible, and you can incrementally have them setup as you go along.
- When architecting applications, I try to use the Black Box model (opens in a new tab) as much as possible. This simplifies a lot of things for me.
- I try to go for the Zero Trust Security Model when building my architecture popularized by Beyondcorp (opens in a new tab) from Google and while this will create a lot of friction at start, this makes life a lot better for you in the future.
- There are some questions I ask based on the principles like YAGNI (opens in a new tab), DRY (opens in a new tab), KISS (opens in a new tab), and they play a huge role in making sure that you don't overwhelm yourself with things you don't want to do right now and prioritize things right.
- I try to refer case studies and see how others are already solving the same problem and this can help me save a lot of my time. Avoiding to re-invent the wheel. For GraphQL, you may find them here (opens in a new tab)
Deciding the “Right” Stack for “You”
Before I pick any tool or technology as part of my tech stack, I do ask a set of questions which help me better judge and make an informed decision on what I want. Probably it might help you too. This applies not just to the GraphQL ecosystem, but anything you choose for that matter.
- Does this tool/library solve my problem well?
- What is the Licensing model? Is it Open Source? If so, is it MIT/Apache/BSD/GPL
- Does it have community support or backed by a Foundation/Enterprise? When was the last commit? How many contributors? Does it have a clear path to becoming contributors?
- How many people use it in production? What are their experiences? At what scale are they using it?
- What do the stats look like? Stars, Forks, Downloads?
- Is it bloated? Or does it do just one thing well?
- Does it have a clear roadmap for the future? If so, what are the milestones?
- What are the other alternatives? How does it compare to them?
- How is the documentation? Does it have tests? Does it have examples which I can refer to?
- Does it follow standards and is free of Vendor Lockin?
- Are there any security concerns which this tool or library might create?
While not all of these questions might have been addressed by the library or tool well, what I see is at least the intent to address them in near-time.
While most of the things in this blog may not be related to GraphQL itself, these are some things which you need to keep in mind before starting your journey with GraphQL. In the next blog, I will show you how my GraphQL Tech Stack looks like as I use it to build Timecampus (opens in a new tab), and we will dive deeper into each layer of the stack, one piece at a time.
Hope this was informative. Do let us know how you prefer to architect with GraphQL in the comments below, and we will be happy to know more about it.
If you have any questions or are looking for help, feel free to reach out to me @techahoy (opens in a new tab) anytime.
And if this helped, do share this across with your friends, do hang around and follow us for more like this every week. See you all soon.
Join our newsletter
Want to hear from us when there's something new? Sign up and stay up to date!
Scalable APIs with GraphQL Server Codegen Preset
Structuring GraphQL server the right way enables many teams to work in harmony while minimising runtime risks.
Introducing Schema Policy in Hive
New GraphQL-Hive feature for enfocring best-practices and schema-design styles.
GraphQL Cursor Pagination with PostgreSQL
An SQL based approach for understanding and implementing GraphQL Cursor Pagination.
Using @defer Directive with GraphQL Code Generator
Learn how to boost GraphQL performance using the @defer directive and GraphQL Code Generator for deferred fragment field resolution.