With most tech that I screw up I assume that "I wasn't using it right" but with GraphQL I'm not sure how anyone could. The permissions/auth aspect alone is a nightmare. Couple that with potential performance issues (N+1 or just massive amounts of data) and I want nothing to do with GraphQL anymore. Everything we attempted to fix our permissions issues just caused more problems. It would break existing queries and debugging GraphQL sucked so much.
If you only live on the frontend and someone else is responsible for the backend GraphQL then I understand why you might like it. From that perspective it's amazing, you can get as little or as much as you want with the specific fields you want. No waiting on the backend team to write an endpoint. However even then you end up saving queries as files or abstracting them (maybe IDE support has improved but it wasn't great last time I was using it ~5 years ago) and now you just have REST endpoints by another name.
At one point we considered whitelisting specific queries and that's when I knew we had gone too far and made a mess for ourselves. If we had taken the time to just write REST endpoints instead we would have gotten way more done and had way fewer grey hairs.
Now your backend devs aren't bored writing "business logic" and your front end devs aren't bored waiting for your backend devs. You have a new class of inscrutable errors and performance issues, but that's why you pay your backend devs the big bucks! Because some guys from a technical college couldn't possibly solve your issue, you need to pay 250k to people who went to Stanford or Berkeley.
"Maybe implementing CORBA for message passing is over complicating it." "Maybe using OOP and facade classes is over complicating it." "Maybe using XML to store all the config information is over complicating it." "Maybe using this ORM with a strange file based caching mechanism is over complicating it."
And always get drowned out by everyone going with the trend.
The problem I always had with rest was that nobody really follows any patterns, not in the same company, not in the same project. There are a million different decisions you can make with rest and if you let a dev alone to build it themselves you're gonna get yet another variation, whereas GQL is only implemented according to a spec.
The N + 1 problems are pretty much fixed these days & mostly any dev can build something in themselves to fix it if they want to. Permissions/auth is relatively easily achieved with middleware and directives, though it's also totally possible to roll your own by writing something that inspects the querys AST if you really need to have your auth layer separate to your schema.
Of course the documentation on some kinds of query syntax was too sparse, (this is for Shopify) but I could see how it might be nice for certain kinds of cases. If you run a platform it might be a good option to offer in your API. For shopify afaik there are equivalent calls in both REST and graphql so you have options.
It provided an escape hatch that allowed rapid prototyping, and came with a default UI, and then we cleaned up the ones that were worth it.
Though I guess you can do that with REST too.
I'm currently exploring all of this myself. I have a side project in mind that can use a graph db, and I thought a front-end graphql can work well with a graphdb backend. I was not sure why this pattern is not more popular, but reading all of this now, I'm seeing where these problems may arise.
A graph db backend can use efficient graph search algorithms, especially for deeply nested data, but the issue with authorization is still there. If anything, fine-grained authorization is something better represented with graph dbs than with relational databases.
In both these projects the GraphQL had started small. I came in during a more mature phase of these projects (2 and 4 years). That's where the requirements are harder, more specific, and overall complexity has grown. Adoption and demand on the API were growing quickly. Hence you logically spend more time debugging, this is true for any codebase.
But GraphQL has everything in it to make such problems even harder. And both these projects had clear signs of "learning-on-the-go" with loads of bad practices (especially for the N+1 problem). Issue descriptions were much vaguer, harder to find in logs and performance issues popped up in the most random places (code that had been running and untouched for ages).
Fun fact; in both these projects the original devs who set it up were no longer involved. Probably spreading their evangalism further elsewhere.
RPC and REST are just more straightforward to monitor, log, cache, authorize and debug.
REST API's are a proven solution for the problem of other apps, including front-ends, needing data from a data store. Using JSON is much improved over the days of XML and SOAP. Beyond that there haven't been advancements in technology that cause fundamental shifts in that problem space. There have been different opinions about structuring REST calls but those aren't going to cause any real forward progress for the industry and are inconsequential when it comes to business outcomes.
There are so many developers out there that can't stand plugging in proven solutions to problems and just dealing with the trade-offs or minor inconveniences. Nothing is going to be perfect and most likely a lot of the software we write will cease to be running in a decade.
It allows teams to work with less communication overhead. It solves more of a human problems than a technical problems, if someone think there is no value in that and bare metal performance is paramount that person never worked in a big org.
> RPC and REST are just more straightforward to monitor, log, cache, authorize and debug.
In some ways yes, in others no. For example it can be near impossible to see if a deprecated field in a REST API is still being used and by which clients it is being used. With GraphQL this is fairly simple.
Unfortunately GraphQL way of working is very different from normal REST APIs and often requires more complex server-side caching. The N+1 problem needs to be figured out upfront for every data-storage system used in the backend.
Ah this brings up bitter memories. Team I was managing was roped in to become the first graphql framework (nay platform) the ivory tower (central architecture run by the cto) team was building and trying to shove down the rest of the company. This was during the graphql craze around 5 years ago. The principal engineer leading it was even preemptively promoted to distinguished engineer for future contributions.
Fast-forward 5 years project was disbanded due to massive migration, cost and complexity problems. DE still lauded for experimentation heroism and evangelism. I think he is now pushing the latest flavors of the month to go for the next promo!
If I use graphQL again it’ll only be for admin features. Anything where very few users will use it and very infrequently. Preferably in spots where caching works against the workflow. OLAP vs OLTP.
GraphQL is really about reducing friction between teams. High functioning distributed systems all have two qualities in common: work stealing and back pressure. Without back pressure there is nothing to stop the project from running off a cliff.
I think the "learning-on-the-go" symptom, where you can sometimes literally read down a file and watch some developer learn the language as they add more and more functions to the file with a gradual increase in skill (or, to put it less charitably, as they slowly get less bad at writing code) is probably a very common thing, and not just a GraphQL issue.
I think two projects having loads of bad practices is too small a dataset to really assume anything, you sort of need to see widespread "bad practices" in the tech to be able to determine that the bad practice is actually the norm practice and there is perhaps a flaw in the tech that encourages that norm.
The only downside is if one of those unused fields changes due to a mutation then all components using that field will rerender (ofc I'm assuming React here). Again, not the biggest concern if you've got your memoization set up correctly
Overfetching or not, that's a rather big difference.
One thing the author doesn't mention is that you can limit the set of queries clients can call in a GraphQL service, by hash or signature. This mitigates a lot of the gnarly performance and security issues because the attack surface goes from "arbitrary queries" to "queries you've already 'vetted'", where you can look at things like complexity, ACL behavior, etc ahead of time. Since clients (in my experience) aren't usually modifying GQL queries at runtime, this is a pretty good tradeoff.
All that said, I find Buf Connect to be the best set of tradeoffs for most projects: strongly typed servers and clients, strong ecosystem support around protobuf (e.g. to generate an OpenAPI spec), a standard HTTP/JSON interface for easy curl et al compatibility, etc.
OpenAPI as the source of truth is annoying because it's too flexible, and it's rarely possible to generate type-safe servers + clients from an OpenAPI spec without running into one edge case or another.
Then it's just REST with extra steps and none of the benefits
The best practice for GQL is to make frequent, small, queries (Apollo handles batching them) throughout your application. Apollo won't do any extra work to fetch new fields if they're already in the cache
Not to be that person because I understand there's always edge cases, but in general with GQL if your queries are highly complex or too nested "you're doing it wrong™"
using OPENAPI, rpc, GQL types in client, etc to share typing (schema) information between client/server
resolver/dataloader in GQL, eager join in ORM is to handler internal data composition
presenter layer should not care about data composition, so that writing Query at presenter is an anti-pattern.
presenter should fetch schema info & data passively, like what https://github.com/hey-api/openapi-ts did, the job of implementation belongs to backend.
In fact what rest/rpc really need is the resolver and dataloader, to help backend easily extend or composing data together, and immediately transferring the schema & data to clients.
pydantic-resolve is the python binding for this idea.
No. With RPC we can just make a HibernateServer call and be done with it.
It works there because
1. Every user is logged in. Is there anything you can do at FB without giving up something to the Zuck?
2. Because it's all behind a login, you can front load the first login/request with a giant SPA and then run all the custom queries you want.
3. everything at FB is some sort of blended context (my user, someone else's user, a permissions interaction)... security is baked in to every field.
4. Because of permissions, and login requirement it's hard to be a bad actor (FB just locks you out, nothing is public).
If you have a SPA and logged in user requirement and that robust permissions model then GraphQL might make sense... Otherwise its a shining example of conways law and might not be fit for your org... The same can be said for react too.
For most people "security is baked in to every field" is going to be very expensive.
People completely missed the point of GraphQL, which is you TRADE flexibility for the client for added cost on the server.
Which in a app and team as huge as facebooks' made sense, especially since they have the so-called facebook apps that could do... anything.
IMO GraphQL is a technological dead end in much the same way as Mongo is.
They were both conceived to solve a perceived problem with tools widely adopted at the time, but ended up with something which is even worse, while the tools they were trying to replace rapidly matured and improved.
Today OpenAPI REST and Postgres are rightfully considered as the defaults, you even have PostgREST combining them, while most of those who adopted Mongo or GraphQL have either long migrated or are stuck discussing migrations.
At least GraphQL supposedly works for Facebook, and I tried it out before deciding it wasn't a default. I never even bothered with MongoDB. I've had to repeatedly veto using both in projects, cause someone thought it'd be cool and thought that was a good enough reason. Finally it's not cool anymore, but there will be another thing.
* openapi was basically nonexistent when GQL came out. It certainly wasn't "the tool they were trying to replace"
* Postgres and GQL are not in any way mutually exclusive
* Today, openapi is still tiny compared to GQL. At least as measured by StackOverflow question tags:
https://trends.stackoverflow.co/?tags=graphql,openapi,soap,m...
Can you suggest alternatives to graph introspection and related UI tools like GraphiQL, and the subgraph federation systems?
These days, given the freedom to write the backend in TS too, I might look into tRPC instead. One thing's for sure, I won't be going back to OpenAPI unless and until I can fully autogenerate api.yaml and otherwise never have to touch it again (getting there with zod+openapi on another project, but it's nowhere near as easy as graphql-codegen doing all the things with one introspection query).
I found myself spending a large amount of time inventing and trying to patch in solutions that most RPC and REST frameworks solved long ago for both the server AND the client (auth, rate limiting, error handling and validation stick out particularly). Client solutions are comparatively heavy, complicated, and riddled with gotchas (e.g. caching) that trip up new team members more than REST. It’s not impossible to build performant GraphQL solutions, but the solutions feel more like afterthoughts and require more vigilance to ensure your team doesn’t stick their finger in an electrical socket compared to REST. The lack of namespacing or organization results in almost unintelligible query and mutation documentation for large projects. The comparatively large size and complexity of requests can be a headache for ops teams. I loathe that interfaces and inheritance don’t work for mutations. Front end devs just use it like a very heavy REST and the holy grail promised by stuff like Relay never materializes. I could go on.
And at the end of the day, the app’s API usage will stabilize and mature, and the expressiveness becomes less compelling compared to its cost. When I went back to OpenAPI and REST, it was like a breath of fresh air, I felt I was building things much faster. I will grant you that generating clients from OpenAPI still is the worst part.
This is the difference
1. GraphQL was and remains insanely hard to build without an underlying data layer that does the heavy lifting. Without projection push-down, predicate push-down, a data layer that can support heavy parallelism it’s untenable. Exactly the problems the OP highlights - needing to “hoist”…
2. GraphQL on REST is an anti-pattern. The hype takes you there, but the juice is not worth the squeeze. The problem was lack of types? Add openapi. The problem was custom aggregation endpoints? Make it super easy / cheap to build aggregate endpoints in REST. Use an AI copilot to write an aggregate REST endpoint and a openapi schema for it even. But maybe the last thing to do is to build annd mainatian another API layer with a completely different execution model.
It’s not what it was meant for perhaps, but GraphQL’s killer use-case is an API to access / operate on data. Especially when there are multiple consumers of data (services, apps) and they don’t own the underlying data sources or speak the underlying language of the database. This turns out to be the underlying problem behind a lot of api, data modernization / migration / decomposition efforts.
The cost of this approach is implementing Graphql by building a compiler/planner with a robust authz system baked into it. A resolver based approach can never cut it, unless you resolve a query plan - aka build a compiler.
If you want to use graphql to just batch rest endpoints, it’s very likely going to become legacy tech as soon as the team that built it starts to move on.
Yates implements Postgres RLS along with Prisma, which we use as our ORM, and then our GraphQL schema is generated using TypeGraphQL (https://typegraphql.com/). Overall, it's a very nice setup and allows us to be versatile on the client side while still having strong authentication integrity.
While perhaps not as polished as Hasura, this stack does have the nice benefit of being free ;-)
Yes.
- If data already exists in cache, a query will return that data instead of making a network request.
- Everything that has a data dependency on something in the cache will automatically update when the data is updated, e.g. after a mutation.
- Cache data can be optimistically updated before the request completes, UI that queries this data will automatically update.
- Components will automatically refetch data if they need to, e.g. if an object is partially updated.
The pain points are pretty painful though:
- Really hard to debug unexpected refetches.
- Normalizing the data for the cache comes at a cost, it can be pretty slow for big responses.
- You quickly realise you need to really understand how the client works under the hood to be productive with debugging/complex behaviour.
I see it as a case of "this is the worst API/client, except for all the others". I'm curious to hear how people using non-graphql APIs are managing data in the client in web-apps with complex data needs?
[1] https://www.apollographql.com/docs/react/why-apollo [2] https://relay.dev/
It makes UI changes much much easier to deal with.
Reliability:
* Not null fields in a distributed system are a lie. Clients write code assuming something is not null, so a query that fetches data from N sub systems breaks the entire page when one of them fails.
Performance:
* People say you only need to fetch just what the page wants but in practice clients create re-usable fragments for every object in the system and use it everywhere. Any time a new field is added, every api call fetches that new field adding latency everywhere
* clients are unaware of the underlying topology and add fields that are usually harmless but have bad tail latencies.
* The completely generic nature of the API means the backend can't optimize performance for a specific page. E.g. if one API has nasty tails, but it's not that important for this page, the backend could time it out at some reasonable value instead of holding up the entire request
If something is null that's not supposed to be null, then the entire operation should be called into question. It's probably not safe to proceed so it's a good thing he entire page breaks, you don't want users to continue based on wrong information. If you define something in the schema that it's possible that it's null, but then the frontend dev ignores the fact that it can be null, why is it GraphQL's fault then that the page breaks?
* clients create re-usable fragments for every object
As a frontend developer I don't know why you would do that, but if your frontend devs are doing that then yes they are doing it wrong... However switching to REST with statically defined endpoints doesn't solve the over/underfetching problem, but as backend developer you do get to gatekeep that aspect. So yeah the devs should really be just doing it right.
I quite like Relay's pattern where components define their data requirements, and this is passed up the component tree to some query at the root. Avoids a situation where the query defined at the root asks for unnecessary fields because it's so far away from where the data is actually required.
https://relay.dev/docs/principles-and-architecture/thinking-...
GraphQL is a protocol and you define the implementation. Some GraphQL implementations like REST implementations try to generate everything for you. That is not "GraphQL" but one type.
GraphQL is way to query nested APIS. The dataloading N+1 Problems are substantially easier to solve in GraphQL and many frameworks have solutions auto-optimize ORM queries for example.
The backend defines the query, the frontend requests it. Simple. Never understood the hate against GQL that now the frontend has all this power. They have exactly as much power as you would have exposed in a Rest Interface just now the backend can actually see all the data it needs at once and optimize.
If you are worried about a cartesian product from a GraphQL call I have news for you about production REST systems. Ever seen an engineer do a loop and make n+1 REST calls for resources? It happens more often then you think.
REST encourages resource waste. You are getting fields you don't need. Hard to query related objects, etc.
Benefits I have reaped with GraphQL:
1. API analysis - I can statically analyze a typescript program and verify their API calls are valid against backend schema.
2. Usage analysis - Statically analyzing the schema I can see which fields are still in use and safely refactor.
3. Frontend Velocity - The frontend can cange its data needs on the frontend query whatever related objects it needs efficiently without backend changes. You can get all data for a page with 1 call and not have to have page specific endpoints.
4. Less backend engineers are needed as API changes less.
5. N+1 Optimization much easier to solve in GraphQL then REST
There are so many advantages to using GraphQL. The only advantage of REST I can think of is for static external APIs.the triky part of gql is we need to build a Query system for all business requirement in single entry, and we can not predict what query will be issued by client.
this is anti-pattern because presenter layer should not care about how data is composed.
It would be much easy if we treat each rest/rpc endpoint as a GQL entry, use resolver and dataloader to quickly & easily build complicated view data, everything is still under the control of backend.
here is a demo. https://github.com/allmonday/pydantic-resolve/blob/master/ex...
The one positive to come out of working with it (aside from knowing how to spot a tech black hole) is that it informed the design of the API layer in my framework [1][2]. I realized the sweet spot is starting with a basic JSON-RPC type endpoint and then layering things like input validation [3], authorization [4], and selective output [5] (only requesting certain fields back) on as you need them.
[1] https://docs.cheatcode.co/joystick/node/app/api/getters
[2] https://docs.cheatcode.co/joystick/node/app/api/setters
[3] https://docs.cheatcode.co/joystick/node/app/api/validating-i...
[4] https://docs.cheatcode.co/joystick/node/app/api/authorizatio...
[5] https://docs.cheatcode.co/joystick/ui/api/get (see output array in the function options API)
There's plenty of other sites / services that receive almost as close to FB properties in terms of traffic yet you never hear them pushing all those tools. Trading firms, Porn firms etc. Some just use REST + JSON or RPC + JSON.
Wish us an industry would read Joel's Spolsky's Fire and Motion article: While you're fighting tools built by FB you're not making stuff for your customers.
Revenue / Profit >> Tech
I’ve been saying this for years: fetching multiple things in one query is not something normal-scale apps care about. What devs like about it is client generation and type safety. So OpenAPI, for all its flaws, covers that. GraphQL’s schema language is much more beautiful — hopefully Microsoft’s TypeSpec will take off and we can have the best of both worlds.
Another point implicit in the piece but not quite stated is that GraphQL aims to make something nice for the API consumer, but the implementation side is a nightmare. It is not worth the tradeoff because eventually, that nightmare leaks out to the consumer because it is too hard to make improvements to the API.
First, some of these issues--authorization, security, N+1--can be mitigated by using something like Prisma, PostGraphile, or Hasura, instead of crafting GraphQL backends with code.
Second, there are gains to be made by compiling a GraphQL operation to a single operation in the underlying database's query language (e.g. SQL)--when that's possible--rather than by following the "resolver and data-loader" catechism.
Third, as I've written elsewhere recently, I think the N+1 problem is overblown. Not that it doesn't exist, but just that it only sometimes exists and usually isn't as bad as is often claimed. But again, this is eliminated by switching to a compiler approach anyway, so it's not worth making a federal case over it.
Despite all this, like I said I'm still over GraphQL. If you must implement it, try to use one of the tools mentioned above if you can.
I also think there’s an avoidance to simply “translate” a GQL query into an SQL query. Not that it can’t be done, but it allows a lot less flexibility in the backend as far as code patterns that can be adopted. Basically it minimizes the use of ORM models, which may be a pro for some and a con for others.
I haven’t worked with GraphQL in over 4 years since I left my last job. I actively made a choice not to use it at my current job and steered the ship towards REST endpoints, mostly because it would be easier to build authorization middleware. Also like the author of the article discovered, code littered with dataloaders is a pain to maintain.
Transactions were being aggregated with thousands to 10s of thousands of SQL statements like this.
SELECT * from customer
where id = <single id>
The developers had no idea the ORM was doing this under the hood.It's trivially easy to say that if every SQL statement took 1ms to execute, that thousands of round trips can really start to tank whatever process it might be blocking.
Same problem with Prisma Model definitions and other such things.
Please, if you make a new thing and it fits neatly into a data format, use a data format! Let me use all of those well established libraries, schema definitions and programming concepts in order to generate, parse and make sense of your thing. Let me seamlessly write your language with the data structures I already know in my language if that's possible.
Don't make it pretty, make it usable.
Let me explain the problem GraphQL actually solves. FB, as we know, is an incredibly large mobile app, possibly one of the largest in terms of actual code. Here are some realities about mobile apps:
1. Once released into the wild, that app version is out there forever. At app installs in the billions this is a serious problem. So you need something that handles versioning of your API. You need something to force you think about versioning and support old versions as long as possible. Again, I don't see the word "version" anywhere in this post. Versioning doesn't seem to be a problem the author has;
2. There are a lot of common objects in the FB app. Posts, comments, etc. When these objects become sufficiently complex, you don't want individual teams pulling out random fields. This is one thing fragments are for. It allows a specialized team define the object model and allow other teams to pull out a subset of that data.
3. FB uses an in-memory graph database for almost all things so issues like N+1 queries (which are real issues if you're talking to a relational DB) don't really come up. The in-memory database has a layer over it that does things like enforce permissions and privacy policies on reads and writes. GraphQL auth is really a superset of this. So if you're querying an endpoint for a user and their blocked users (one example from the post), you probably won't have auth in your endpoint because that'll be enforced by the data access layer that already exists and be written and maintained by a team responsible for that data.
Some comments here have complained that GraphQL is a technology for an organizational problem. I would counter that by saying all technology ultimately is tied to organization. They affect each other.
The above was all done to avoid things like privacy leaks, which at FB's scale is a real problem. Speaking from experience, a significant amount of computing power and software engineering time is tied to avoid these problems by the teams responsible as well as other teams to try and detect an unintentional leak.
How I see a bunch of people use GraphQL (not necessarily the author of this blog post) is as a 1:1 map to a relational model beneath it. That doesn't make a whole lot of sense. GraphQL probably isn't for you.
I totally agree with the sentiment here, but disagree with the conclusion.
1. The rest of the world doesn't have facebooks data layer. Ergo, GraphQL is very hard for everyone who's not FB.
2. Turns out GraphQL is actually a really good data API. Whether it's a postgres/mongo/graph/REST data source. Because it's a great mid-point between something as flexible as SQL but as controlled as a REST API.
Here are 3 things that GraphQL is absolutely great at:
- Select a 2 fields out of a 100 attributes in a data model? GraphQL is great.
- Gradually deprecate an old field in a database and replace with a new one? GraphQL is great.
- Want a flexible way to filter/join/paginate regardless of the underlying storage layer? GraphQL is great.
Other API formats suck at this.Working with GraphQL ought to feel like you're working entirely with your database and sprinking the right bits of transform/validation/authz business logic and then the API that other teams can use is "free".
GraphQL is dying as a replacement to a REST API layer. I think GraphQL will see its second wind as a data API. Microsoft and Google announced their GraphQL data APIs recently.
GraphQL will probably only make sense when it's as close as a 1:1 map to a DB beneath it. Whether relational or NoSQL or graph.
In all other cases, the GraphQL juice is not worth the squeeze.
Yeah I've had trouble with some devs misunderstanding this, this seems to be the source of "omg you can run arbitrary queries" (especially if they've never used GraphQL.
You can't do anything that the GraphQL schema doesn't specify - it's not open ended SQL and joins.
For example, having nested queries more than 2 levels is a no go for me (just like having nested inheritance is basically anti pattern)
Focus more on your interface. One way to avoid N+1 and nested query is to required parameter for related fields. For example
```
user(id: $userId) { {
id
friends {
id
...
}
```to
```
user(id: $userId) {
id
friends(id: $userId) {
id
...
}
```I wonder if GraphQL would have been as popular if it was a much smaller shop than facebook launching it. Feels like having a name like FB gets you a tech credibility pass.
If you have two smart layers adjacent to each other (and GraphQL is a smart layer) then the interconnection between the two and the conflicting abstractions of data becomes an issue.
By having something dumb (REST endpoints) between the business layer and the complexity of the front end, it is possible to have reasonable abstractions writing to the REST endpoints and an abstraction of the payloads that the endpoints provide into what is needed in the front end application.
It seems that GraphQL works best when the backend has minimal business logic and the thing that is calling GraphQL likewise has minimal logic -- and all the logic is contained in the GraphQL query itself.
But if you're finding complexity in getting things into GraphQL and manage permissions and mutations... or you've got the complexity of the front end where the data model the front end needs to work with doesn't align with the GraphQL model ... then you've got complex solutions abutting each other and those spots is where the pain will be found.
6 years. For a lot of people, that was also the time ( 2013 - 2019 ) they moved from SPA or Client Side Rendering and started looking or move back to HTML+ approach.
So may be for any company operating on Boring Technology principle, you should not choose any hyped tech until they have been battle tested by the market for 7 years.
So in general had the same feeling about graphQL because I still had to write layer of code that gets stuff from database, get it limited or paginated. Maybe once you have enough endpoints then you can change your front end against already existing GraphQL - but for me that was never the case as 90% of work is adding new tables, new endpoints so graphQL feels like additional work with additional footguns for almost no benefits.
The thing that I still _like_ about GraphQL is that it's a nice approach for expressing complex domain models over a protocol. If you are working with either REST or protos, you may still have to reason about graph-like structures but without the benefit of a graph-like schema.
It is often a poor choice for websites that should be cached, are publicly accessible and have simple and predictable data access patterns.
GraphQL has a fairly flat but very long learning curve which makes it a power tool that is great for those who have learned it in depth. Without that experience one is almost always better off using REST.
We've got little to gain, and the lack of flexibility from our POV is a relatively good thing. It gives us direct insight into desired use cases, and since the APIs are internal it lets us provide guidance on potentially better ways of implementing things as well as in some cases being able to provide direct optimizations for specific tasks.
I do really like the Api definition part of it though - but I found something like typespec now to be the perfect middle-ground, it allows out you describe your apis similar to a graphql Api without enforcing the graphql engine on you though.
Apollo federation and implementation of supergraph and nested graphs is pretty robust
We use both in production at reasonable scale and complexity 10+ roles few hundred object models, 100s of request/sec
We were basically just enhancing an external graph with some of our own stuff added on though so fairly straightforward.
Authorization: I do it in the database using roles, row level security and column level security. It works well and I defer everything to PostgreSQL's security controls, it feels like the right place to do it and I don't have to worry about it going out of fashion, PostgreSQL is here to stay. Anybody else who talks to the database directly is also subject to the same authorization rules, which is nice.
Introspection: this should really be disabled on production services. Only enable it for development.
N+1 problem: I don't really have a problem with N+1 because PostGraphile converts the request into an efficient query. In other cases this problem presents itself in REST too and the article proposes hoisting N+1 queries to the controller, but that's just really moving the problem around, and you can do this with GraphQL too.
The other problems, yeah sure they are present and a worry if you're running some highly visible/very loaded public API.
As someone who has used GeaphQL extensively, I really don’t understand most of the complaints, which seem like they’d be common to any complex API surface. Sure you can write a query that triggers a server bug, but that happens with REST too. Yes, your server needs to be hardened against these queries… so what?
And security is hard, granular security doubly so. If you need to do field level authorisation then the problem is that you need a policy engine, not a different query technology.
Then again having GraphQL as the definition for them is probably still not bad, I'll just have to write something that converts them to SQL::Abstract 2 trees once I get around to porting it to TS.
https://chillicream.com/docs/hotchocolate/v13/security
Honestly most of the "problems" the OP discusses has solutions in the documentation.
BINGO.
Someone (VP Eng who was a FE dev) 7 years ago decided shiny new thing was the best and we had to use it, it was owned by FE team, then no one wanted to own it, now BE team has to deal with it, and every dev goes out of their way to avoid it in arch design for new features.
Have seen this at two companies so far.
Basically, these are all solved problems.
My biggest complain is there seemed to be no way to just to query all fields. I know that is intentional and the point of GraphQL.. but they should support something for the server side to enable this. Maybe they have over the years, I don't know. But my experience was during the implementation phase the client kept adding new fields that I didn't know about and then I had to constantly ask them for the fields because I thought I was missing data. If I could have just queried ALL the fields, then saw what came in, and chop them down to what I need.. great.
The only way GraphQL seems to make any sense is if everything is basically completely defined and you are at a final project stage. After many many years of experience... this is rarely the case.
Cool piece of technology? Sure.. but hardly practical except in scenarios of extreme amounts of data and only when it makes sense for the client to return specific fields.
Although I think still for 95% of even those extreme cases, you just write a REST endpoint that returns the fields that make sense for the query....
I worked on a team where 3 of us were well into our second decade of software development, yet we still had a consultant come in to help us sanity check our graphql implementations and ongoing strategy.
We were mostly on the right track. The struggles we were having were just… Normal.
At that point I really lost steam. Prior to that I was motivated by the thought that something wasn’t clicking yet. Discovering that I understood graphql just fine but it was typically a bad developer experience with obtuse tooling and queries took the wind out of the sails.
The worst part was mutations.
Writing graphql handlers in Rust was also awful. The more you try to leverage the flexibility of graphql, the more Rust freaks out because you’re doing exactly what you shouldn’t in such a strict environment.
Yet… Doing this in a language with weaker typing and less strictness seems like a potential minefield of bugs.
I see the appeal of graphql and I’ve liked it in small one-off situations where it was useful but had limited scope. Otherwise I genuinely hope I don’t work with it again.
It indeed seems like GraphQL saved your client in this case.
GraphQL works pretty well when it's acting as a Backend-For-Frontend. The client can make a single call to get everything it needs at once, the BFF can then fan out to various microservices as needed. The frustrating part is when it's blindly used for something like a monolithic CRUD API with some light permissions. In that scenario you're taking all of the drawbacks, without reaping any of the benefits.
I'm really glad to be leaving this project behind as I move jobs, and I'm praying no one suggests using it in my new job. At least not until the industry more broadly understands it in more depth.
In this way services get freedom to define their stack while still neatly fitting into the suite of services, products get a tidy interface from which to query everything, and because the GraphQL consumer is more akin to a regular database consumer, the database muscle memory kicks back in.
I’ve also grown to prefer bespoke backends for each client over a super backend that tries to anticipate all of the needs of all the clients. It just opens too many holes and what it buys in versatility for the client author it also gives to the exploit author.
But at the same time it doesn’t have to be that bad. I don’t have this array of issues because I do: - query whitelisting for performance and security, - data loading to avoid n+1, authentication with whatever works(session cookies, tokens), - permission check based on the auth context in a resolver.
It works decently for us, allowing to stay away from getting into ESB. Yet have some shared domain, type safety, and easy integration of legacy and new systems/services.
I would say a bigger issue for us was to keep it all nicely organized / designed in terms of types and api contracts. But that’s manageable.
I have no beef against doing REST, jsonRPC etc. Actually I consistently steer people that way. But the documentation format we chose as an industry to build these things with, Swagger, is just disappointing. Some times I think the industry would be at a totally different point had we gone with more powerful standards like API blueprint (or maybe raml).
Case in point, I'm consulting an org with roughly 1k engineers right now on improving their API ecosystem and what we are seeing is that the more OpenAPI tooling they use, the worse the DX gets...
What are your recommendations? gRpc?
This right here is IMO the biggest advantage of a GraphQL system. What equivalent to GraphiQL is there for OpenAPI? With GraphQL my frontend devs can go shopping for data in one UI, with rich descriptions and strong typing and even try out the queries live.
However, lately I’ve been thinking that with the advent of React Server Components we will see a migration away from GraphQL-focused Frontend applications, and instead move toward other solutions. Such as ones based purely on exposing Postgres databases (e.g. supabase & postgREST) and type safe APIs (e.g. tRPC).
Authorization is performed by GRPC which returns only fields available to the user, rate limiting is performed on GRPC requests so at some point your data will start coming partially if you make too many queries or query that is too complex, N+1 problem is still there, but in a distributed system it's there even without GraphQL, the mitigation is caching in GraphQL server on GRPC request level.
In my experience GeaphQL really helped decouple frontend and backend and I'm pretty happy about it.
Besides GraphQL offers some standard way of making server side event streaming, it's not supported that well, but at least it's more comprehensive than bare web sockets.
I never got around to implement mutations though, individual change requests via REST are enough.
It's very true that a few years ago the gap was there, and people implementing GQL had to solve all these problems without access to these mature solutions.
But these days, they're mostly solved problems. Authorization by type that propagate through the graph is pretty simple. Rate limiting and complexity limits on queries is often built in the frameworks. Query parsing is solved with persisted query all the major frameworks have as first class citizen. Performance: lots of GQL caching solutions out there, and the "multi threaded by default" model used by most frameworks helps a lot.
Etc etc etc.
I jumped companies a lot, did both. For a while I too thought GQL was redundant, then went back to a more classic world after a successful GQL implementation. I had forgotten how many problems were solved, especially organizational and people communication issues, through GQL. And so we're moving to GQL again.
Someone in the comment mentioned GQL is a tech solution for an organizational problem. I'll say most good software tooling and frameworks ARE tech solutions for org problem, because technology is usually a solution to people problem. GQL is the peek of that, and it does it quite well. Don't use GQL to solve tech problems, there's better tools for those.
In other words, they cared more about ideal ways to access data than the product of that data.
This has so consistently been the case in my personal experiences that I avoid people in hiring interviews that start talking about or try to sell me on why I should switch my stack to GraphQL.
I've built platform which does just that: https://saasufy.com/
This approach has allowed me to create a library of highly generic components which can be composed into surprisingly complex front ends and where all data updates in real time (in a targeted, efficient and scalable way): https://github.com/Saasufy/saasufy-components?tab=readme-ov-...
You can build just about any front end you can imagine just by assembling these components. Almost no code needed. It takes me hours to build web applications which would take weeks or months to build. The real time update features come free. The dev experience is essentially bug free; any 'bugs' you may encounter have clear visual symptoms which can be 'debugged' by observing the HTML DOM.
I'm currently observing people getting hyped up over HTMX and it's kind of funny to realize that developers do actually like the declarative approach... And yet they're going in the wrong direction again. Just as they did with GraphQL.
It would have been impossible to do this with GraphQL because field-granularity/autonomy is essential. GraphQL queries combine different resources and fields together in complex ways and this makes it impossible to efficiently avoid real time update conflicts on the front end.
Perhaps now it might be;
Nice to haves: Enough experience with GraphQL to never suggest nor implement it.
These are the kind of experiences I think which truly matter.
Looking back, it is hard to believe I spent a decade working in shops with no published spec, no code-gen. So many ugly surprises discovered in production, all of which were avoidable. Never again.
And for each issue he mentions that rest doesn't have said issue is basically because rest doesn't have the feature at all.
You could use graphql the same way as rest (by exposing queries without allowing to specify fields) and you still have a better rest, since at least the response has a schema out of the box.
GraphQL has a lot of advantages, the most important of which is data masking. In a properly structured GraphQL app, only the function that requested a field (i.e. included it in a fragment) sees that field. This allows many developers to move independently, without communication or coordination.
It's unfortunate, but there are lots of rough edges with GraphQL and it is hard to incrementally adopt, and so if you don't have enough developers (to have communication issues) and a team devoted to adoption, it might not be worth it.
Anyway, Isograph is a framework that seeks to take away many of those rough edges, while baking in stuff like data masking, etc. Y'all should check it out!
This essentially turns GraphQL into a DSL for implementing REST endpoints, which is still a useful thing since it allows to write the REST endpoint with only knowledge of the GraphQL API rather than knowledge of the whole backend architecture, which is generally useful if the backend and frontend are written by different people or teams.
That's the way Facebook uses it on their main website: they pass the query id in the "doc_id" form field, the arguement in the "variables" field, the server gets the actual GraphQL query based on the doc_id from some sort of internal dictionary and AFAIK no GraphQL query string ever goes on the wire in production.
That's already how it works, it is not an open ended SQL query. The GraphQL schema is the whitelist.
Something just never felt right about the client building the queries for me, I guess.
In my experience the use case is not limited to a single full-stack developer - this approach has turned our entire polyglot team into full-stack devs — no one is afraid of the data layer now. Super refreshing.
For public facing endpoints, "exposing an OpenAPI 3.0+ compliant JSON REST API" would be the first thing I'd reach for.
The problems with GraphQL where so obivious in the first 5 minutes that I looked into that. Looks smart and flexible - but isn't
I remember working on a Rest API and thinking about (things like) GraphQL... the typical "would it make life easier" and all that.
In the end, as the years have passed, I just stick to the minimum. If there is a tried-and-tested library or something.. I use it. When there is something new, I question it.
Never tried GraphQL. It looks like it can improve my data on one end, but cumbersome when it is hard to reason the data, or get generally complicated, which this article demonstrates.
But when you see those job ads with all these "great new technologies" it's hard not to fall into the trap.
Not sure if this will be good. This reads like they will add Vertical Tabs as a Sidebar, like all the other vertical tab-addons doing it now. But since the Quantum-update there is demand for a second sidebar dedicated for tabs, because as it's now, sidebar is really annoying to use.
But maybe it will at least add some improvements which the other addons can built upon.
> More streamlined menus that reduce visual clutter and prioritize top user actions so you can get to the important things quicker.
Oh gosh, no! Why make them even worse than they are already now?
Stick to tried and true - monolith, asp.net, angular or blazor front-end, relational database.
> asp.net, angular or blazor front-end
Since when are these not technologies pushed by big tech companies?
A company I worked with got badly burned by AngularJS.
As for how to tackle things today, I alternate between REST+OpenAPI and RPC models for communication. I am certainly biased as I have my own typed open source RPC library (Conduit) which is layered on top of arbitrary message passing transports such as WebSockets, but as of yet it's only suited for Typescript-based clients -- REST+OpenAPI is a better fit for an API with many consumers.
But for dynamic client/server and client/client single-implementor communication needs, I go with RPC.
- GraphQL performance issues
- GraphQL makes tasks more complex
- GraphQL schemas confuse junior devs, higher entry level
- REST cache easier
- REST if you understand what you are doing ... can do the same
- REST is better for error handling and tooling
Now in 2024, I clearly see LLMs gives much better autocomplete for REST or SDK code generated with protobuf ecosystem
There is not enough code repos based on GraphQL for LLMs to reason.
If you have like minded people who loves GraphQL, nothing can stop them but at higher and broader level it's a huge risk for dev velocity (like anything else if you dont know what you are doing)
The entire point is composition + client customization of the response body, but in practice the request schema is never modified. And even if it were, you could just version a REST endpoint.
GraphQL is never done "correctly," adds a layer of obscurity to monitoring and error tracing, and I've yet to see a case where it's actually the right tool for the job.
Maybe if you need to aggregate calls to multiple services into one, but if you're trying to avoid latency by reducing number of calls, you still eat that latency while the GraphQL server makes those calls for you.
GraphQL sucks.
However, you can go very far with a simpler version of it, just by allowing clients to specify what fields/relations they want and in what context.
I'm using a library that I've created for myself for years without any of the problems mentioned in the article.
The client side queries are generated by the server at compile time. Each request for an object requires a "segment" to be specified. On the server, each segment has the necessary filters/limits automatically applied to prevent data leaks.
The lie is that it makes things better. 100% of the time that I add an unnecessary thing to a project in the name of "it will make things better", it never did.
It's the old new-shiny, so here we are lathering on our dislike, which I feel is totally normal and expected. There's a new new-shiny around somewhere and a few years from now, I'll see articles like this one about it.
Having not yet read the comments, I plan to enjoy them thoroughly, I love a good hate-post about software I myself don't like. :)
I have been, like you, baffled by places that insist on using GraphQL for a biz software website that they barely support on mobile, or an app-only product with limited to 0 web support. But for situations where you want both a robust mobile app (sometimes multiple), and a robust web app (sometimes multiple), I don't know if there's a better solution that keeps the frontends and one true backend decoupled as well?
Curious what you say or what I may have missed for this scenario.
- https://github.com/rsinger86/drf-flex-fields
- https://django.wtf/blog/graphql-like-features-in-django-rest...
I am also over GraphQL but I really like the api explorers, code completion, type safety, etc.
So that: 1. we enjoy the current tech stack from REST/rpc 2. we gain the ability of quick composition from resolver / dataloader 3. with iteration, we can replace/ refactor endpoint without breaking anything.
for fastapi user, the lib 'pydantic-resolve' can help.
Seems really great to not have each service adhere to another service’s potentially unstable api contract.
Especially if that service is owned by another team.
> Data fetching and the N+1 problem
> [...] if a field resolver hits an external data source such as a DB or HTTP API, and
> it is nested in a list containing N items, it will do those calls N times
Why is this considered a problem regardless of the situation at hand?F.e. using REST, if the nested list contains on average N-2 items that can be efficiently cached, wouldn't that be better than having the cache constantly invalidated on the aggregation of the parent and its items?
Yes there is, simply throw JSON such as `[[[[[[...` at the server. You probably don't have to get into parsing though, nearly all C# (and Node, and Ruby, some Rust even) assumes that memory is, in-fact, infinitely large and just throw network input into a precisely-sized buffer. Just upload a few GB of nonsense to an API endpoint, bonus points if you omit Content-Length.
Another library to check out: Drizzle-ORM
Skill issue.
REST serverless business logic on top and graphql is great
For graphql like needs we use a standard set of props (select, filter, sortBy, limit, offset).
They map to a single sql statement and executed efficiently by PG.
select can be nested properties if we need deeper nesting. Server emits typescript types package for frontend.
It's worked really well so far at our scale. Eng team is fairly productive.
I'm less happy about the time I wasted on bash, DOOM WADs, and Desktop Linux. Yes these are are all a while ago. I didn't invest my time wisely when I was young.
I'm also glad I didn't invest in GraphQL, Kubernetes, Angular, Solaris, and perl.
One example that stood out to me though:
> GraphQL discourages breaking changes and provides no tools to deal with them.
GraphQL the standard doesn't provide tools no, but I've been very successful using Apollo Studio to solve this as (in my experience) the workflow maps to how you generally develop applications:
1) agree on some schema 2) make a (Apollo studio) "branch" with the schema change 3) mobile & backend devs (typically) code gen the objects they need for the schema 4) backend dev deploys a preview branch of their implementation, mobile dev points the client to it 5) test it, merge it, publish the schema on the main "branch" - deal with the breaking changes if Apollo warns you of them.
So maybe you can accomplish this with other tools, and maybe it's not fair to compare "the standard" to a particular tool, but I usually say I stick to gql (where appropriate) because there is a tool for it that works so well for the problem(s) I'm typically solving.
Would it be fair to say that GraphQL is extremely useful for internal-only clients? For example an inhouse data store, query service, test status monitor, etc?
So many issues disappear when you aren't concerned about bad-actors and you have _some_ control over both client and server.
We don't have mobile app and don't even plan, but if we do, we always can implement some rest api.
Then I'm designing my table structure, optionally designing my views when I don't want a 1:1 reflection of how data is stored, setting up row-level security at the data layer, and making user-defined functions for the one-offs that deviate from plain CRUD.
But solutions like Spring DGS for Java, Graphene for Python, and Apollo for Node? No thanks. Never again. Way too much trouble and pain. I'd rather make lambdas that talk to the data store directly and output HTML than make and maintain a GraphQL schema manually again.
I really wish Postgraphile and Hasura were more popular, so folks could have skipped the low level plumbing and optimization fences like dataloaders that make GraphQL such a chore otherwise.
It really is elegant when you're not stuck in a swamp.
At Firebase we chose GraphQL as the basis for our new Data Connect PostgreSQL product (https://firebase.google.com/products-data-connect) despite acknowledging pretty much all of the issues outlined in this article as correct.
GraphQL is an excellent IDL (interface definition language). It's compact, it's flexible (through directives), and there's literally no better way I'm aware of to rapidly construct complex nested relational queries. But the promise of "clients can fetch whatever they want" adds an enormous burden to backend developers because you have to secure yourself against queries of arbitrary shape and complexity.
In reality you don't want clients to "fetch whatever they want" because clients can't be trusted. The path we took is that developers can predefine the queries that are needed for their client, and then only those queries can be executed from an untrusted client.
This approach provides a surprising number of benefits - you get the flexibility of nested queries and field selection, the end-to-end strongly typed structure of a schema-driven API, and the ability to reason about security like it's a custom backend.
Made the dev experience a nightmare because I could never be sure whether it was my code or the client cache or the (actively in beta) server that was causing me to get back wrong data.
Has anyone experienced something similar with typedb? It feels a little too good to be true. Make it seems like graphql and nosql trauma.
https://blog.tailcall.run/writing-a-graphql-backend-by-hand-...
GraphQL makes sense only at a certain scale when you have multiple APIs built by multiple teams that need to be exposed as a single endpoint. GraphQL as an API or APIs is the perfect use case and the one that it was designed for.
In every other case, REST is harder to do wrong and easier to do right. The tooling for it is widely supported and well known. Every aspect of building an easy to use, easy to own, easy to scale API is -- well -- just easier with REST.
I have seen more than a few small, 4-8 person engineering teams reach for GraphQL and then wonder why every cycle starts feeling slow. In a 4-8 person team, the devs are usually working full stack and if not, then they are working closely enough that having a highly flexible API layer servicing the front-end doesn't make any sense since it's either the same devs or they're one Slack ping away from getting the right shape from the backend in one simple `GET`.
Like the author, I've found that the story with OpenAPI nowadays is really good. I have a short writeup here with .NET that shows hot re-generation of the API into a TypeScript definition that can be used with React, Vue, or your framework of choice: https://chrlschn.dev/blog/2023/10/end-to-end-type-safety-wit...
This workflow is super productive and perfect for small teams working full stack; far easier than getting GQL right.
GraphQL makes it easier for front-end developers to do the wrong thing, which is why they love it.
it's similar to OpenAPI, but its simpler, and cleaner. In fact, you can generate webrpc schema's to OpenAPI and then generate OpenAPI clients.
Not just for the content but I love it when someone changes their mind (very very hard thing to do) and details out what led them to change.
Frontend - React
Backend - GraphQL
What's extra creepy is the private equity firms squeezing this space dry
Why? Isn't YAML human readable?
GraphQL can be very powerful, but also very dangerous.
For example the whole n+1 data fetching topic: Yes this issue exists and like the author is mentioning: GraphQL has a well understood and common solution for this problem. On the other hand: In REST world you can have exactly the same issue, but either create a custom solution for this issue or push the issue to the client side, where this issue needs to be solved, too. I could not exactly follow the explanation of the special case of the n+1 problem in context of authorization. "This is actually trickier [...], because authorisation code is not alway run in a GraphQL context." OK authorisation code is not run in a GraphQL context, but why is this then a GraphQL issue?
On the rate limiting issue I also do not see why these issue would by design just exist in GraphQL world: Yes you can have recursive data structures and write queries which can take a lot a processing time to resolve. But again: Why would the same recursive data structure not be possible to be modeled for a rest api and why would the same data not be queriable from a rest api? My guess again is: You _can_ model the same data structure and also _can_ query the same amount of data. And also again: In case of the rest api the complexity of querying this data is just pushed to the client side and that is why it seems easier to rate limit individual rest endpoints. But actually I see no reason why a GraphQL API could not rate limit e.g. on data loader or any expensive operation level, too.
Generally regarding malicious (e.g. malformed) queries: I think this is definitely a valid issue, which should be addressed in GraphQL APIs. I think there should be a common way for application developers to sign their queries somehow and query engines should only attempt to process signed queries. This way only trusted developers are able to write arbitrary queries.
On the "Authorisation" section I can not quite follow the reasoning. Aside from implementation-specific extra-calls to a presumed authorisation framework: Why would domain requirements be different when they are implemented either as REST API or as a GraphQL API?
"Compare this to the REST world where [...] you would authorise every endpoint". OK if you can implement an authorization requirement simply on a REST endpoint, why could you not implement this simply on a Query-Type field in an equivalent GraphQL API? If this is about authorization on nested data structures: Can you not have equally nested data structures on REST endpoints, too? I think in these cases authorization on REST endpoints wouldn't be sufficient, too. Assuming you would avoid nested data structures: Would this not simply push the n+1 problem from the server side to the client side?
My general feeling about the REST vs GraphQL debate is both worlds can in principal cause pretty much the same issues. Even for client side caching I see no real design issue which prevents GraphQL queries and responses to be cached, but probably more like a middleware issue. On the other hand I still see a lot of benefit in using GraphQL: Especially the query flexibility for developers, the solved the n+1 query problem (which may still exists on the rest client side or is solved by a custom solution) and the error handling. I still hate to define exact use of HTTP status codes and custom error payload structure in every context where REST is a new or badly defined concept again and again.
I'm looking at Shopify API at the moment, and there's a notification that they're deprecating their REST API and moving it all to GraphQL. I hope it's a joke. But I suspect not. Enshittification intensifies
In other news, bitcoin will never replace cash, most companies don't need k8s and AGI isn't 2 years away.
I think it's worth mentioning that there is a GraphQL project which has worked to address every single point elucidated here. Of course whether the framework's conventions satisfy your tastes and sensibilities is an entirely different story. The project had at least an answer if not the answer to every gripe listed in this article by the end of 2022.
Despite having basically solved all these issues two years ago, there's been a consistent stream of articles, videos, and podcasts like this one claiming that GraphQL is too complex and the GraphQL ecosystem doesn't provide any tooling or support for managing these complexities.
The great irony is that the framework in question is now pivoting away from GraphQL. Seemingly no one wanted to use a GraphQL framework. But why? Whenever someone learned about the framework, they would say to themselves:
"Well I don't want to use a GraphQL framework since GraphQL has all these problems like auth and caching and rate limiting and performance. It's just too complex so I'll avoid any frameworks that use GraphQL." They would never seem to realize that the very framework they were refusing to use was literally the answer to the justification provided for why they didn't want to use the framework. Eventually, after having this exact conversation a few hundred times with a few hundred devs, I decided that there's only two ways I could explain what was happening, either:
(1) Every time while trying to explain this, all of these developers were experiencing some kind of out of body dissociation not dissimilar to a stroke or brain aneurysm which made it impossible for any semantic meaning to reach them.
(2) These developers just didn't like GraphQL and didn't want to use GraphQL. They were making up a bunch of reasons to not use GraphQL but they had no interest in a framework which solved these issues cause the issues weren't the problem, GraphQL itself was the problem.
Since I've already spent hundreds or even thousands of hours trying to explain to the open source web dev community how RedwoodJS solves these issues, you'll have to forgive my laziness today and make due with this ChatGPT rebuttal instead of another one of mine.
Understanding RedwoodJS in the Context of GraphQL Criticisms: A Detailed Response
The blog post raises several valid concerns about GraphQL's use in production environments, particularly around security, performance, and complexity. Here's how RedwoodJS addresses these issues, considering the specifics mentioned in the blog post.
1. Attack Surface - The blog highlights that exposing a query language like GraphQL increases the attack surface of an application, necessitating robust security measures. RedwoodJS leverages GraphQL Armor, which provides built-in protections against common GraphQL vulnerabilities. These include rate limiting, depth limiting, and cost analysis to prevent overly complex queries. Additionally, RedwoodJS uses Envelop plugins for enhanced security measures, such as blocking field suggestions and masking errors, which are essential to prevent information leakage and mitigate potential attacks.
2. Authorization - GraphQL's flexibility can lead to challenges in properly authorizing access to different fields based on context. RedwoodJS integrates authorization directly into the schema using directives like `requireAuth` and `skipAuth`. This ensures that each query and mutation can enforce authentication and role-based access control efficiently. By embedding these security checks into the GraphQL schema, RedwoodJS helps developers avoid the pitfalls of broken access control.
3. Rate Limiting - The blog points out that GraphQL queries can vary significantly in complexity, making it challenging to implement effective rate limiting. RedwoodJS includes mechanisms to estimate and limit query complexity. It supports configurations to set maximum query depth and complexity, thus preventing queries that could otherwise lead to server overload. These settings help ensure that the GraphQL server can handle incoming requests without being overwhelmed, addressing the concern of unpredictable query costs.
4. Query Parsing - GraphQL's requirement to parse queries before execution can lead to vulnerabilities if not handled correctly. RedwoodJS mitigates this by implementing limits on query size and the number of operations, ensuring that the parsing process does not lead to excessive memory consumption or server crashes. This approach helps safeguard against denial-of-service attacks that exploit query parsing vulnerabilities.
5. Performance - The blog mentions issues like the N+1 problem and challenges with HTTP caching in GraphQL. RedwoodJS uses the Dataloader pattern to batch and cache database requests, effectively mitigating the N+1 problem. By ensuring efficient data fetching mechanisms, RedwoodJS maintains performance even with complex queries. Additionally, while HTTP caching is not inherently compatible with GraphQL, RedwoodJS’s structure and client-server interactions are designed to minimize redundant data fetching.
6. Complexity - GraphQL's flexibility can lead to increased complexity in codebases, making it harder to maintain and debug. RedwoodJS aims to simplify the development process by integrating various tools and abstractions that reduce boilerplate code. The framework's philosophy of convention over configuration helps maintain a clean and manageable codebase. Moreover, the built-in support for Cells (components that handle data fetching, rendering, and updating) abstracts much of the complexity away from developers, allowing them to focus on business logic rather than the intricacies of GraphQL.
7. Conclusion - RedwoodJS addresses many of the concerns raised in the blog post through its robust tooling and thoughtful abstractions. By integrating security measures, optimizing for performance, and simplifying the developer experience, RedwoodJS presents a compelling solution for building modern web applications with GraphQL. While the criticisms of GraphQL are valid, RedwoodJS's approach demonstrates that with the right framework, many of these issues can be effectively mitigated, making GraphQL a viable choice for many projects.
> if you expose a fully self documenting query API to all clients, you better be damn sure that every field is authorised against the current user appropriately to the context in which that field is being fetched.
The same is true for a "regular http API" [now abbreviated as "rest API"] (in case it's not clear: imagine how the http API would look like and think about if it would be somehow magically secure by default. Spoiler: it won't). Security by obscurity was never a great thing. It can help, but it's never sufficient on its own.
> Compare this to the REST world where generally speaking you would authorise every endpoint, a far smaller task.
Just think about how many endpoints you would actually need to cover all combinations that the graphql API offers.
> Rate limiting: With GraphQL we cannot assume that all requests are equally hard on the server. (...)
It's true. However: a rest API only makes that easier because it's less flexible and you have to manually create one endpoint for each of the various graphql API combinations. So instead, a classical graphql mitigation that the author does not mention is to simply require the client (or some clients) to only use fixed (predefined) queries. That is then the same as what a rest API does, problem solved. So graphql is not worse of here, but it can be much better.
> Query parsing
This point is actually fair, except for the claim that there is no equivalent in REST. That is simply not true, as there have been many cases where rest frameworks worked with json and could be ddos'd by using large json numbers.
> Performance: When it comes to performance in GraphQL people often talk about it’s incompatibility with HTTP caching. For me personally, this has not been an issue.
Funny, because that is indeed one factual disadvantage of graphql.
> Data fetching and the N+1 problem: TLDR: if a field resolver hits an external data source such as a DB or HTTP API, and it is nested in a list containing N items, it will do those calls N times.
No, this is wrong. But first: this problem exists in a rest API as well, but worse: instead of N+1 queries to the DB, it will N+1 http requests AND N+1 db requests. But with graphql and some libraries (or some handwritten code) it is comparibly easy to write resolvers that are smart and "gather" all requests on the same query level and then execute them in one go. This is harder to do in a http api because you definitely have to do this by hand (as the auther describes).
> GraphQL discourages breaking changes and provides no tools to deal with them.
What? Comon. In a rest API there is no means to do anything against breaking changes by default. Graphql at least comes with builtin deprecation (but not versioning though).
> Reliance on HTTP response codes turns up everywhere in tooling, so dealing with the fact that 200 can mean everything from everything is Ok through to everything is down can be quite annoying.
True, but that can be adjusted. I did that and made it so that if all queries failed due to a user-error, the request will return 400, otherwise 204 if they partly failed.
> Fetching all your data in one query in the HTTP 2+ age is often not beneficial to response time, in fact it will worsen it if your server is not parallelised
Okay, I'm starting to be snarky now: maybe choose a better language or server then, don't blame it on graphql.
And actually, many real problems of graphql don't even get mentioned. For example: graphql has no builtin map type, which is very annoying. Or, it has union types in the response, but you can't use the same types as inputs due to a lack of tagging-concept.
And so on. My conclusion: the auther is actually fairly inexperienced when it comes to graphql and they probably had a bad experience partly due to using ruby.
My own judgement: graphql is great except for very very specific cases, especially in projects that require huge amounts of servers and have a high level of stability in the API. I would always default to graphql unless the requirements speak against it.