I do. It helps with latency when someone hits the "back" button and your queries are page/view based. Instead of seeing the delay of the query, it's instant. Because, well, it probably should be.
Cache times are usually short, < 5 minutes, and are cleared whenever a POST/DELETE/PUT are issued. Works pretty well in my experience.
You’d be surprised how far you can get with even the dumbest possible caching strategy. I’m talking about something like a server side memcache that just blows everything away on any non-log write.
If your baseline is so a new fetch every single time anyway, it doesn’t take much to achieve some pretty impressive wins.
The API calls shouldn't be stored in the browser. That would just be a mess. A refresh should predictably fetch fresh data, because that matches user intent. There are also times I want to turn caching off. The understanding of what is reasonable to cache and what isn't is better understood on the front end, where the data is actually used/consumed. Browser caching is out for my use case.
I create a JavaScript object that matches the interface of my request library, axios in my case. The cache is basically a middleware to it created by function wrapping. Kind of like how memoize works but with a bit of extra smarts.
I have my own .get(), .post(), .delete() and .put() functions. So when a .get() is called, it's a cache check and return the stored value. If not found call axios.get() with the passed in args and store in cache before returning.
The post() function clears the cache then passes through the arguments to axios.post().
Etc.
The 5 minutes is accomplished with an entry on the event loop to remove said item.
It's only like 30 lines of code.
The real wins for this is in basic navigation. Clicking around site exploring, makes it snappy. Another advantage is lowering the total request count a bit to the backend, lowering the cloud bill.
I believe requests are usually a POST, so HTTP caches must ignore it to work properly. I imagine there are caches that understand GraphQL which could be used.
The GraphQL clients I've seen normalize GraphQL responses and cache them in the client, i.e. browser cache, IndexedDB or whatever native App ecosystem you're using. They usually work as read-through cache but depending on the App, this might not be the right strategy.
One downside is that depending on how your backend is structured, nested GraphQL queries can hit an arbitrary number of different endpoints, tables, services, or databases behind the scenes. And some of those queries could take a long time to resolve and not cache as cleanly as individual endpoints.
Possibly. As mentioned, it's very easy to cache individual endpoints both locally on the client and on the server side with REST. Since GraphQL is so flexible, it doesn't make caching responses very easy. It's a trade-off.
Extra client dependencies, for one. REST support is baked into pretty much every web browser and OS. But for GraphQL I’ve seen some hideous deployments with dependencies on giant libraries as well as bundles containing dozens of GraphQL queries as long strings, jacking up the memory usage of the webapp.
Coming from a saas API startup using REST for everything to a more internal product facing company using Apollo graphql for its core services is weird. On one hand it’s a lot more flexible to return whatever you need on the frontend but on the other hand you get some really heated queries and if you don’t map out your objects correctly you get lots of queries that don’t objectively make sense in what the return. You also just get devs adding new queries for stuff that probably shouldn’t be for the resource they used
Dependency issues will worsen over time. You’re just building a monolith with it. API interfaces should be dumb and simple. Neither REST (Swagger rules) or GraphQL are healthy long-term implementations.
UI needs data which can be represented in a read model. Read models can be represented however the receiver needs it. UI’s should rarely need to make complex or large queries. So if I’m displaying data for one subject (customer) I should have a read model that delivers everything I need to display. If I need an Orders or Inventory read model, that’s fine.
Sending data to an api for creation or updating should be simple business commands and not based on resources or transport.
My mantra is to think like the business, not like an API or data store.
Complexity of the backend is much higher with GraphQL. Caching is much more difficult (not impossible) compared to REST. The client query system is bigger and more complex. So... Complexity generally is higher with GraphQL.
Some data flows end up with dependencies you don't know when you build a query.
Such as a auth token, credentials, etc. So you end up with several round trips anyway for these cases.
A lot of the advantages to GraphQL don't materialize due to how front ends are built. It encourages the use of a global store to shove all data into from the large query you just completed. Except every front end I've ever worked on that used GraphQL basically used it just like REST with a few extra features because typical front end architecture encourages this. Taking little advantage of what GraphQL is suppose to offer.
In my experience versioning was more painful with GraphQL than it was with REST. With REST you know you just need to rev the path or some other obvious marker like a header, which allows you to run both versions side by side. In GraphQL this isn't so obvious that you need to do the same kind of separation for braking changes. And breaking changes are occasionally needed contrary to what some people believe.
So when I make a backend now, it's vanilla REST without anything fancy. I favor simplicity.
Backend complexity as mentioned above, as well as potential performance issues. You really want to avoid N+1 issues by batching all the fields and relationships you want to query at once. It's too easy to realize you're running n queries for each field of every entity you are retrieving...
I was excited to hear about tRPC until I realized that there seems to be the need for a monorepo. can't just keep the backend and let clients talk to me.
These people only use JavaScript and TypeScript? Being restricted to one programming language is a huge difference compared to rest, GraphQL, soap, grpc, CORBA…
It sounds a bit like Java RMI, something that cannot succeed.
Anybody have any advice for query optimization in GraphQL? Seems to me so far that it’s pretty nice for flexibility with small datasets, but once your data gets really big you need precise control over how things are queried in order to take best advantage of indexes, and I’m finding it hard to manage that with GraphQL since queries are so dispersed. It often for me is hard to even figure out when queries might be triggered or why, which makes it harder to ensure they’re not called too much and/or not called in an ineffective way.
Between the request's query parameters and the query building, I added an optional SqlQueryOptimizer interface and injectable that's used for models/tables that need them. To minimize scope of writing each one, I made a sequential adapter that just tries each listed one until one applies. Each implementation first checks if the query params qualifies to use it, then makes any index hints or other query changes. Ended up doing this twice, once for ActiveRecord and once for an internal AST query builder.
I think arguments against REST are more like arguments against one way how to implement REST API.
When you look at PostgREST REST API, you will see you can select related data, you can pick what fields, so you don't underfetch or overfetch, you don't have to create new API endpoints…
You need to add your schema to check for data validity, but that's something you'd have to do anyway.
Our team chose REST for a new rebuild of a huge project, mainly because of complexity, performance and maintainability issues. If we where to choose GraphQL, i don’t think we’d be even near finished yet.
And as you say, implementation matters a lot, we don’t feel constrained by rest at all.
We would still choose REST, the reason for the complexity is mostly implementation of the system design, which affects every part of the API and how it functions. We'd need to re-think and re-design the entire back-end (Especially caching/scaling) and front-end.
I was the one to suggest GraphQL during planning, but after considering all of the parts it was clear that it would be months of additional work with too many unknowns, which isn't in the budget.
Had to double-check this post's date since it feels like it was from five years ago with barely anything new. But good choice, IMHO!
I'm a fan of GraphQL. It serves as an excellent BFF framework for performant frontends. You get types, tooling, conventions for free and if you use something like Apollo GraphQL Client, it normalizes GraphQL responses and caches the individual entities.
In addition, I realized in the last 5 years how it helped managing product teams. There were teams with die hard frontend devs that practically annoyed me every day with requests for new/changed endpoints; once I got very pi*ed, implemented a GraphQL backend and gave them the GraphiQL UI such that they could discover and query the data themselves. Saved me a lot of frustration and was fun for them.
You can theoretically do the same with REST but if you dogmatically stick to Entities-Resources, it will not be performant if you have lists with nested sub-fetch (e.g. a list of blogposts with their author and her name/avatar). If you start creating endpoints which return a whole ViewModel, you're IMHO starting to leave RESTland and if you're doing it right, will eventually re-create something GraphQL-ish.
All of those problems and solutions are not unique to either tech choice. You can use OpenApi for documentation, a variety of libraries like React-query for caching, and N+1 issues exist (and are arguably more complicated to solve) in graphql.
I think the complexity of GraphQL and the simplicity of REST are both a bit overstated.
In my experience, GraphQL reduces total complexity when there are a lot of backend services. That is, you are trying to stitch together a bunch of disparate things. Tools like Postgraphile really help in these cases. It’s just another server.
In other cases, GraphQL can make things much more complicated. The benefit of REST is it’s simplicity (especially if - let’s face it - you forget about all that inconvenient “representational state transfer” stuff and really it’s just GET JSON)
Like most things in life and software, there is not one true answer. The goal should be to reduce the complexity of the system as a whole, not to reject one technology because it adds complexity to one specific aspect of a system.
The tldr is that most comparisons of GraphQL, REST tRPC and whatnot don't mention Fragments and Relay because a lot of developers don't understand the purpose and value of Fragments. Per-component data dependencies, data masking, persisted operations just to name a few.
Not every application needs these features, which is fine, but if you don't leverage GraphQL to the fullest, why add the complexity and not just go with something simpler like tRPC.
On the other hand, there are backend for frontend frameworks like WunderGraph (https://WunderGraph.com) that take the advantage of GraphQL, but combine it with the simplicity of RPC. (Shameless plug, I'm the founder)
That said, I think a lot of people overuse or misuse GraphQL in situations where it doesn't really add much value. E.g. when exposing an API to third parties, our API is probably consumed by a backend. What really is the benefit of using GraphQL in this case? Wouldn't an OpenAPI/REST API be much easier to adopt for your users?
GraphQL really shines when used as a first party API from the frontend, with a client like Relay that embraces Fragments. Everything else to me is hype driven development. my2c
It focuses a lot of attention on result part, not so much on query input part.
Contrast it with MongoDB-style querying which can be mapped to SQL as well allowing client to construct arbitrary where clauses.
Trivial in jsonrpc, not possible in graphql.
Input object type doesn't allow union types, which is quite bad.
It means MongoDB style queries can't be constructed - query input is rigid.
Jsonrpc allows it.
There is no concept of parametric polymorphism.
Jsonrpc on the other hand allows you to take advantage of type system in your programming language.
Optionality at input position is odd:
- nullable types are implicitly optional
- non-nullable types are implicitly required
This doesn't make sense.
There are apis where you'd want to:
- have parameter that is required, however may be null
- optional parameter that cannot be null - is not provided (ignore) or is provided (use)
This creates odd api where you need to allow querying by field with null value even though it's invalid (ie. optional query parameter for non nullable property).
I also can see why people criticize it for encouraging over-fetching and creating n+1 problems on the backend.
56 comments
[ 2.6 ms ] story [ 125 ms ] threadCache times are usually short, < 5 minutes, and are cleared whenever a POST/DELETE/PUT are issued. Works pretty well in my experience.
If your baseline is so a new fetch every single time anyway, it doesn’t take much to achieve some pretty impressive wins.
I create a JavaScript object that matches the interface of my request library, axios in my case. The cache is basically a middleware to it created by function wrapping. Kind of like how memoize works but with a bit of extra smarts.
I have my own .get(), .post(), .delete() and .put() functions. So when a .get() is called, it's a cache check and return the stored value. If not found call axios.get() with the passed in args and store in cache before returning.
The post() function clears the cache then passes through the arguments to axios.post().
Etc.
The 5 minutes is accomplished with an entry on the event loop to remove said item.
It's only like 30 lines of code.
The real wins for this is in basic navigation. Clicking around site exploring, makes it snappy. Another advantage is lowering the total request count a bit to the backend, lowering the cloud bill.
Sending data to an api for creation or updating should be simple business commands and not based on resources or transport.
My mantra is to think like the business, not like an API or data store.
Some data flows end up with dependencies you don't know when you build a query. Such as a auth token, credentials, etc. So you end up with several round trips anyway for these cases.
A lot of the advantages to GraphQL don't materialize due to how front ends are built. It encourages the use of a global store to shove all data into from the large query you just completed. Except every front end I've ever worked on that used GraphQL basically used it just like REST with a few extra features because typical front end architecture encourages this. Taking little advantage of what GraphQL is suppose to offer.
In my experience versioning was more painful with GraphQL than it was with REST. With REST you know you just need to rev the path or some other obvious marker like a header, which allows you to run both versions side by side. In GraphQL this isn't so obvious that you need to do the same kind of separation for braking changes. And breaking changes are occasionally needed contrary to what some people believe.
So when I make a backend now, it's vanilla REST without anything fancy. I favor simplicity.
The speed of development is unmatched. Not having to do any codegen is mind blowing.
I'm looking forward to more tRPC style things that can integrate across language boundaries. I'm okay with some simple codegen to support it
It sounds a bit like Java RMI, something that cannot succeed.
[0] https://github.com/graphql/dataloader
For extra kicks, select one that supports GraphQL federation.
When you look at PostgREST REST API, you will see you can select related data, you can pick what fields, so you don't underfetch or overfetch, you don't have to create new API endpoints…
You need to add your schema to check for data validity, but that's something you'd have to do anyway.
And as you say, implementation matters a lot, we don’t feel constrained by rest at all.
I was the one to suggest GraphQL during planning, but after considering all of the parts it was clear that it would be months of additional work with too many unknowns, which isn't in the budget.
Pushing HTML fragment updates from the server over Web Sockets and updating with morphdom seems a lot closer to young and fast growing to me.
The fastest complex JSON structure is no JSON at all.
I'm a fan of GraphQL. It serves as an excellent BFF framework for performant frontends. You get types, tooling, conventions for free and if you use something like Apollo GraphQL Client, it normalizes GraphQL responses and caches the individual entities.
In addition, I realized in the last 5 years how it helped managing product teams. There were teams with die hard frontend devs that practically annoyed me every day with requests for new/changed endpoints; once I got very pi*ed, implemented a GraphQL backend and gave them the GraphiQL UI such that they could discover and query the data themselves. Saved me a lot of frustration and was fun for them.
You can theoretically do the same with REST but if you dogmatically stick to Entities-Resources, it will not be performant if you have lists with nested sub-fetch (e.g. a list of blogposts with their author and her name/avatar). If you start creating endpoints which return a whole ViewModel, you're IMHO starting to leave RESTland and if you're doing it right, will eventually re-create something GraphQL-ish.
In my experience, GraphQL reduces total complexity when there are a lot of backend services. That is, you are trying to stitch together a bunch of disparate things. Tools like Postgraphile really help in these cases. It’s just another server.
In other cases, GraphQL can make things much more complicated. The benefit of REST is it’s simplicity (especially if - let’s face it - you forget about all that inconvenient “representational state transfer” stuff and really it’s just GET JSON)
Like most things in life and software, there is not one true answer. The goal should be to reduce the complexity of the system as a whole, not to reject one technology because it adds complexity to one specific aspect of a system.
The tldr is that most comparisons of GraphQL, REST tRPC and whatnot don't mention Fragments and Relay because a lot of developers don't understand the purpose and value of Fragments. Per-component data dependencies, data masking, persisted operations just to name a few.
Not every application needs these features, which is fine, but if you don't leverage GraphQL to the fullest, why add the complexity and not just go with something simpler like tRPC.
On the other hand, there are backend for frontend frameworks like WunderGraph (https://WunderGraph.com) that take the advantage of GraphQL, but combine it with the simplicity of RPC. (Shameless plug, I'm the founder)
That said, I think a lot of people overuse or misuse GraphQL in situations where it doesn't really add much value. E.g. when exposing an API to third parties, our API is probably consumed by a backend. What really is the benefit of using GraphQL in this case? Wouldn't an OpenAPI/REST API be much easier to adopt for your users?
GraphQL really shines when used as a first party API from the frontend, with a client like Relay that embraces Fragments. Everything else to me is hype driven development. my2c
It's very complex. Contrast it with simplicity of jsonrpc https://www.jsonrpc.org/specification
It focuses a lot of attention on result part, not so much on query input part. Contrast it with MongoDB-style querying which can be mapped to SQL as well allowing client to construct arbitrary where clauses. Trivial in jsonrpc, not possible in graphql.
Input object type doesn't allow union types, which is quite bad. It means MongoDB style queries can't be constructed - query input is rigid. Jsonrpc allows it.
There is no concept of parametric polymorphism. Jsonrpc on the other hand allows you to take advantage of type system in your programming language.
Optionality at input position is odd:
- nullable types are implicitly optional
- non-nullable types are implicitly required
This doesn't make sense.
There are apis where you'd want to:
- have parameter that is required, however may be null
- optional parameter that cannot be null - is not provided (ignore) or is provided (use)
This creates odd api where you need to allow querying by field with null value even though it's invalid (ie. optional query parameter for non nullable property).
I also can see why people criticize it for encouraging over-fetching and creating n+1 problems on the backend.