Migrating Netflix to GraphQL
netflixtechblog.com
netflixtechblog.com
I would suggest avoiding it until you're certain you need it, and consider how annoying it might be for the user. (Don't make me learn a language, then syntax check it, just to ask your API to give me some basic info)
For public APIs, you have to choose further in advance whether or not to use it. Never been in that situation myself, but I'd be on the fence because I agree it's annoying for the user.
Frontend dev team didn't want to own this much logic and started asking for nested objects in the API, usually with only a few fields. Like they needed the poster's handle and avatar for each post, and they'd only request more detail when visiting the profile. So we started cramming it all in there. A bit wasteful and tedious, not the end of the world, but GraphQL would have been a much more elegant solution.
Also, frontend and backend teams were on opposite sides of the world. If backend team added a new endpoint but missed something the frontend needed, it'd be a full day before they heard back. So it was best to err on the side of sending too much info back then remove parts of it later.
Graphql is popular in part due to communication dysfunction.
I think I've encountered the same (or a similar) issue when the front-end needs some data that conditionally relates several objects in your data model. None of your openAPI endpoints cleanly map to the "bag of data" you need, but you can have the server bolt on additional data from other models in response to certain types of requests, and the front-end can construct its components based on that. However, this gets ugly.
The alternatives, as I see it, are to have the frontend make multiple requests, sometimes in sequence, and then compose the data it needs from the separate responses, or to altogether abandon REST for a JSON RPC (something like tRPC might really shine here).
Or to use GraphQL, which simplifies things greatly as the frontend can now request the data it needs in the structure it needs it, and Apollo server or whatever you have on the backend can service those requests and plan the optimal path to fetch and transform the data.
Caveat, I've never actually implemented this pattern on a backend, it's just something I've considered after running into the above issues, so I admit I could just be misunderstanding how GraphQL can be useful in this problem space.
But I'm curious if there are other patterns for this problem that, as I believe you were suggesting, could eliminate the problem without adding GraphQL into the mix
Also, to be frank, frontend team was paid a lot less per head than backend team. Their expertise was just in building the UI according to what the backend team and designers gave them, and they were very good at that. Stubbing wasn't an efficient use of their time.
But also in my experience it’s better to go all-in on either REST or GQL; supporting both is a time suck.
I think it’s a stupid thing to deploy
I have been using json-api but unfortunately the only good server-side impl I found was graphiti.dev (ruby).
Are there other recommendations?
GraphQL is very useful, it removes whole classes of bugs. You can even use a Result/Either type where if you don't handle both the success and error cases, the GraphQL request won't even compile, so you can get type safety at both the client and the server while also not having to use purely TypeScript like with tRPC, allowing multiple clients like mobile and web. Pothos does this very well as an error extension [1], where, given the schema:
type Error {
message: String!
}
type Query {
hello(name: String!): QueryHelloResult
}
union QueryHelloResult = Error | QueryHelloSuccess
type QueryHelloSuccess {
data: String!
}
you can then query it with query {
hello(name: "World") {
__typename
... on Error {
message
}
... on QueryHelloSuccess {
data
}
}
}
If you forget `... on Error` the query will simply fail.I should also add that most people use GraphQL incorrectly, to get the benefits of not over/underfetching, the Relay approach using fragments is the only viable way currently. Otherwise it's not too much better than REST. You define the exact data required for a specific React component in GraphQL fragments, then the Relay compiler will stitch those fragments together into one and only one request. This is a great (if long) overview on the problems Relay solves and why everyone else uses GraphQL incorrectly (then complain that GraphQL sucks) [2]. Hint, Apollo is not the way.
[0] https://www.youtube.com/watch?v=yab6i9lrEv0
Not sure how much on the "better" scale it counts but the server introspection with tools like GraphiQL is one of the highlights of working with GraphQL for me I don't get with other systems.
FYI, GraphiQL is deprecated, GraphQL Playground is a good alternative.
You have this backwards.
https://github.com/graphql/graphql-playground/issues/1366#is...
I agree that most people use GraphQL wrong because its impossible to use 'right'.
Graphql 'solves' the trivial while hinting towards a future of self-describing apis and anything in between. Users will hopefully find out that GraphQL never solved any actual problems. Actual problems(valdation, (actual)error-handing, cache-invalidation being the big one in the end like usual) are kept for the user as an exercise, like always. Their promises are always not explicit but implied so the tech-enthusiasts can pick it up.
I remember an article here that was talking about how GraphQL would enable apis to discover eachother on the whole internet and learn to speak to eachother without human intervention.
The main selling point in the beginning was so you don't have to query fields you don't actually need to keep data transfer to a minimum. A completely trivial feature for a json api.
Even a JSON api with relations is just as possible and just as easy with a JSON api. Unlike GraphQL you don't have to pretend syntax was the issue.
As I mentioned elsewhere, Relay solves all of these problems automatically while people try to hack around their homegrown solution with React Query and the like. JSON Schema is tacked on as an afterthought to REST, not built in, so it's necessarily more hacky than REST.
Graphql SHOULD have been Relay.
Graphql without Relay is almost just a syntax.
Quite a hot take but I can't disagree. I was one of the people using GraphQL incorrectly for the longest time. I always knew something was off, that we might as well have just been using rest, but could never really articulate it.
Alan's article is a great read, thanks for sharing.
Sadly, Relay usage is 1/100 the size of Apollo, even though it is literally the way GraphQL was meant to be used, so I try to talk about it wherever I can. It really is such a simple concept (list out all of the data that you need for a given part of the app, then let the compiler manage these dependencies) that is only now starting to be used in other areas, such as the GraphQL Codegen tool: https://the-guild.dev/blog/unleash-the-power-of-fragments-wi.... It's still more incomplete and not as powerful but hopefully we can start integrating fragment stitching into the mainline GraphQL entities so that everyone benefits from this approach.
It really reminds me of Vim, it started off being one of the best ways to write, and now 20 years later we have other editors reinventing the wheel to get closer to the Vim philosophy.
It's not so much downsides of Apollo, more so that it could be done better. Apollo does have things like the n+1 problem which are not present in Relay, so that could be considered a headache as it's annoying to solve if not using Relay.
Relay is for React only however since Facebook made all of these technologies. I recommend using React and NextJS if you can, I used to use Vue but found that the ecosystem for non-React frameworks was still worse than React, so I switched.
Good work and great write up.
Strongly Schema'd interfaces makes client development go faster and smoother in general.
What trade-offs exist for strongly typed schemas? It reminds me of indexing database inserts, longer to write and faster to subsequently query. I would argue strong schemas require more design and architectural discipline during constructions and are thus easier to consume down the line.
To quote Bay Area rapper G-eazy "everything costs something bro"
When people speak of tooling they are almost certainly talking about Apollo. I think you could probably find a similar analog in the SOAP XML days. There were lots of vendors, lots of tooling, and mostly the same challenges and outcomes. It could be as nice as you wanted if you spent the time and effort getting there.
To make this more concrete, GraphQL is "strongly typed" in the sense that you get a run-time error when you use the types incorrectly. That's fine for development. Not so much for production. In order to get end-to-end typing you need to integrate with TypeScript or whatever language you're using. That has its own set of challenges. Your build process needs to know which GraphQL endpoint to use (not so easy when doing development on the API or need to use staging environments, etc.) and then generate typings based on that.
Errors are another GraphQL travesty. Do you use out-of-band HTTP errors? In-band error codes? Both? (ugh, but probably once more than 1 developer touches the code). Do those errors kill your entire query? What or who determines that?
I could also mention caching. But that's enough headaches for today. You need tooling just to get up to bog standard HTTP and browser caching gives you for free.
Hasura https://hasura.io/
Examples of "batteries included" GraphQL. Combine with a type generator for the language of your choice (eg. https://the-guild.dev/graphql/codegen), and you've got your client interface to avoid any type errors. Each comes with a GraphiQL UI for access out of the box as well. It's more turnkey than any REST solutions I've seen, that's for sure.
For different environments, it's quite possible (even normal?) to generate your types and GraphQL schema ahead of time, before CI/CD build of the UI to catch errors early at compile time.
Now add in SvelteKit with KitQL (https://www.kitql.dev/), and you've got a very simple, flexible, and efficient end-to-end solution.
As for errors, because each resolver can generate errors and not all errors are considered fatal, the errors must necessarily be embedded with the response and treated by the network client as they see fit. It's definitely different from REST, but not inferior. It's a different set of tradeoffs. When using a schema generator like Postgraphile or Hasura, it's unlikely you'd get errors in practice. The endpoints are basically guaranteed to resolve, and the generated SQL is nigh certain to execute correctly. If you get one error, chances are the whole endpoint is down due to a database connectivity problem. (That has been my experience at any rate.)
Don't take my word for it. Bring up a Postgres database, put some data in, and point one of these tools at it. I think you'll be pleasantly surprised. You may still prefer REST (or gRPC), but I think we can both agree that "batteries included" is definitely an option with GraphQL.
If unions are used for errors as well, you essentially have the Result/Either type inside GraphQL at compile time. This is much stronger than anything REST provides.
...
Organizationally speaking, I've worked in a company that an interesting approach to federated teams: All the backend applications would have an GraphQL endpoint that would connect to a middleware where all front-end would consume the data.
This allowed not only to avoid that over fetching, but different applications with different data needs could cross fetch the data via GraphQL and resolve them and display it in their own front-end.
Took a lot of work to reach a level of maturity but was the best place that I've worked in terms of federated services/backends serving multiple front-ends.
The article also desperately needs more context. I don’t doubt a lot of cool engineering went into making this work, but for an outsider reading the article (and without them stating any goals apart from velocity) it’s unclear whether the whole thing was just an expensive resume driven development or an actual necessity that justified the invested engineering time
To me, I think backend teams largely want to be left alone, while frontend teams want to make Their Thing today. So the backend teams say "sure we'll make an API" and then the frontend teams in theory never schedule PM-driven meetings with them, but can still experiment with new interaction patterns cheaply.
How this all works out in practice, I'm unsure, but highly suspicious. My personal opinion is that frontend and backend developers working on the same codebase, going to the same meetings, reviewing each other's code is probably faster and more efficient in the long run. But at some point you need 100 teams, and the "just have one team" method can't scale, so at that point, you have to accept that you can't be perfect and come up with something. GraphQL is that something.
(I would have just used SQL. Many good libraries around for parsing them into query plans that are easy to execute.)
Another example that comes to mind, one I actually bared to watch some of, is Stephen Hawes (née Stephen the Robot). Great content, obviously a smart guy doing some great stuff. But every video starts over-excited 'hello my goblins and ghouls' (what?), we get crash zooms in on his face and mouth, violent music and head jerks to mark scene transitions, etc.
Of course by all means have some personality, it's your video, etc., I just don't believe this is their (offline) personality?
Matthias Wandel, Clough42, Marius Hornberger, and Phil's Lab I think each clearly have a certain personality that comes through, but it's 'professional', it's truly them, I assume, I think if I knew or met or worked with them in real life that they'd be exactly as I 'know' them to be. They seem genuine, and not insane.
- Many teams with many micorservices with many underlying stores
- A fetch-based architecture
Each team can be responsible for implementing the resolver for their service along with caching, scaling, etc. At Netflix's scale you don't want to have every single service have to scale to the level of the BFF/Orchestration layer.
https://www.apollographql.com/docs/apollo-server/data/resolv...If you have push-based architecture, it makes more sense for underlying microservices to publish to a broker like Kafka and then materialize a view to DynamoDB, Mongo, Redis, etc.
And if you don't have many teams with many microservices, then just do the REST, Rails, MVC thing and save yourself the headache.
Edit: to elaborate; “a lot of freedom” means an entire schema instead of pre-negotiated contracts, potentially reducing back and forth between frontend and backend.
This isn’t something I’d rush into though. There is real overhead in managing graphql and in my opinion is best suited for extremely large API spaces that most companies simply don’t have to deal with. Microsoft and their graphql implementation is one where I see it as a better option than hundreds if not thousands of different services with different endpoints.
1) An endpoint returns a superset of the data any client may need, which means more data is loaded than any given client needs (which means wasted bandwidth/compute)
2) An endpoint returns a subset of the data all clients need, which means any given client has to tack on additional queries to get everything it needs (which means more roundtrips)
(There's the third option of tacking on "includes" or "excludes" parameters to endpoints, but at that point you're evolving an ad hoc, informally-specified, bug-ridden implementation of half of GraphQL, to massacre Greenspun's tenth rule).
How important solving this dilemma is to your application may be a function of your team or system topology, and the type safety that GraphQL happens to come with is a nice bonus, but this dilemma is the crux of things.
An endpoint that fetches exactly the data it needs for an entire application, never over or underfetching.
Independence between the frontend and the backend, as the backend can implement resolvers for a certain piece of data and the frontend can then query anything that contains that data without waiting for the backend to implement a specific endpoint with that data.
Seriously, GraphQL (when used correctly with Relay, rather than Apollo) is among the best developer experiences I've ever had.
Making it to the front page again with a different query string.
It tends to support the latest accepted RFCs of the GraphQL spec, works with subscriptions, can do a form of federation via Schema Stitching[1] and is a blast to work with.
The company behind it (The Guild) also has a service called Hive[2] that can provide analytics, schema checks, manage gateways etc.
For complex cases, they also have GraphQL Mesh[3] as an alternative to Apollo Gateway
[0]: https://the-guild.dev/graphql/yoga-server/docs
[1]: https://the-guild.dev/graphql/stitching/docs
I'd also like to give a nod to Wundergraph and their Apollo Alternative.
We were given early access to Cosmo[0] and so far are very happy with it.
They have their own GoLang based router that is independent of Apollo, works with v1 and v2 of federation, and works with subscriptions.
Comes with Studio, a CLI tool, and a OTEL Collector. So far it's been great for us at our org.
IMHO the strict contract that grpc makes sense if the tooling and language support can catch up.
Until recently, an internal API framework, Falcor,
powered our mobile apps. They are now backed by
Federated GraphQL, a distributed approach to APIs
where domain teams can independently manage and
own specific sections of the API.
I was an early-adopter of Falcor back when Netflix first started promoting it, and have also spent some time migrating those Falcor calls to GraphQL.Hasura https://hasura.io/
Stop writing GraphQL resolvers by hand, especially if you're working off of a relational database model.