56 comments

[ 2.6 ms ] story [ 125 ms ] thread
What are the downsides for using GraphQL over REST?
Can it be cached yet? I didn't think any http caches work with graphql?
who caches their API though
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.

is this built-in browser caching? how do you set it up so it gets invalidated by POST/DELETE/PUT?
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.
Last time I used GraphQL seriously, caching was suppose to work. I never really saw it work well. Not like you can predictably cache REST.
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.
Also the lazy approach that nobody should ever do but we know everybody does at some point of connecting it directly to databases will leak data
But if you needed data from so many resources, don't you need to hit them up one way or another?
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.
Genuine question: what are healthy long term implementations? gRPC?
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...
In short, it burns through your runway faster because it's more complex and expensive (to setup, maintain and host).
I’m already seeing articles about people ditching GraphQL and going with tRPC.
I've just started with tRPC and for internal dashboards, there's definitely no going back.

The speed of development is unmatched. Not having to do any codegen is mind blowing.

Only catch really is you need to have a typescript backend

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

tRPC doesn't actually need typescript, you can use it with normal javascript just fine.
Is trpc a javascript only protocol?
The t in trpc is for typescript
Still targeting nodejs backends then.
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.
you don't need a monorepo, you just need to be able to import the types, they don't need to be in the same package.
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.
Use a GraphQL-native or graph-with-GraphQL-adapter datastore like Dgraph Community Edition, ArangoDB, EdgeDB, Neo4j, Apache AGE for PostgreSQL, etc.

For extra kicks, select one that supports GraphQL federation.

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.
This is a really stellar idea, thank you very much for sharing!
(comment deleted)
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.

What if your database directly supported GraphQL?
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.

Caching would be easier since BFFs and frontends would now be able to cache at entity level instead of request level.
It is weird to see GraphQL described as "young and fast growing"; it started to pick up steam in early 2016.

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.

Seriously. It's so much easier to ditch all the half baked and buggy JS ecosystem abstractions and tooling and just deal in html.
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.
N+1 issues do not exist when using a graph datastore which supports GraphQL.
Many/most people don’t though. Dataloader libraries exist for a reason.
This statement is false. N+1 issues may exist even in that case. It may vary what caused it and what are the options to solve it.
Sure, they will exist if someone intentionally codes them in. But not when fetching data is done in one pass.
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.

GraphQL isn`t an alternative for REST. GraphQL is a solution for very specific cases and even in this cases it has his own drawbacks.
I actually wrote on Twitter about exactly this post: https://twitter.com/jensneuse_de/status/1634678574274813952?...

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

Just read though graphql spec at https://spec.graphql.org/draft

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.