I'm currently rewriting our company's customer-facing web app with this stack, as well as the GraphQL backend. I have to say its an amazing experience so far. IMO it greatly helps facilitate readable and maintainable code, on both ends of the stack. Having a strict, self-documenting contract between frontend and backend is the biggest selling point for me.
Coming from a heavy-client implementation using backbone models, or in another instance Ember Data with JSON-API, React+Apollo feels like a breath of fresh air, and simplifies my life to the extent that I wonder why I get paid so much to do what I do.
I would suggest looking at Github's API explorer (https://developer.github.com/v4/explorer/). You can assemble graphql queries in there, inspect the results, and copy/paste the exact query into an Apollo component to make your own app.
REST is still a bit simpler to implement on the server side but once you have the GraphQL API built it is infinitely more maintainable when you're working with multiple teams, and API consumers, each with their own needs and focus.
One weakness I've observed is that since each attribute of an object can make its own DB query, you can have a situation where a single GraphQL query can create dozens, or even hundreds, of individual DB queries, creating a performance issue with a naive implementation. This is mitigated with libraries that batch requests transparently, or with clever structuring of your schema that groups similar attributes into one query.
Also, tree-like data structures are not well supported (like a comment thread, where comments can have replies that recurse infinitely. GraphQL straight up doesn't do that)
However I think that the benefits outweigh the weaknesses considerably.
I don't want to take away from your main point, but I wish at least on HN we could use the term "REST" with its proper meaning, but maybe it's a lost cause.
REST has nothing to do with JSON-over-HTTP or pretty endpoint URLs. REST and HATEOAS is about minimal, stateless coupling of web clients and server apps. The idea is that you point your browser at an endpoint, and the user/browser drives forward every further interaction and state evolution by hyperlinks and other affordances presented in the return "representation" (eg. HTML). Specifically, the web client isn't supposed to do requests against out-of-band provided URLs for JSON or other payloads with hard-coded endpoints.
I know it might not be a realistic application model for what the Web has evolved into today, but misuse of the term REST has frustrated its inventor since at least 2008 [1].
I hear this a lot but I think the point is moot. It kind of reminds me of communists telling me that real communism has never been tried.
Okay okay I know, analogies are sinful. Sorry! My point is that this pure definition of REST doesn't conflict with what the parent comment outlined. I'd go even further and say that to implement in this purer, Content-Type agnostic, state-evolution by hyperlinks kind of way makes the implementation even harder (around as hard as it is to implement a GraphQL endpoint).
> I know it might not be a realistic application model for what the Web has evolved into today, but misuse of the term REST has frustrated [basically everyone]
Amen, sister.
JSON RPC over HTTP is... you know... the 99% use case. HATEOAS has its place, but IMO and IME that place isn't most of what industry is doing. For whatever reason, though, there is a buzz about actual-REST and HATEOAS that seems both ignorant of the original intent of REST/HATEOAS and of practical development considerations. REST-fullness has been an undefined architectural ideal for over a decade now and shows no signs of convergence of clarity...
I would really like a new buzzword for JSON RPC so that I know what the heck people are talking about when they talk about REST. I am tired of guessing, and tired of listening to consultants was poetic about HATEOAS who have clearly never read the white paper... :)
>One weakness I've observed is that since each attribute of an object can make its own DB query, you can have a situation where a single GraphQL query can create dozens, or even hundreds, of individual DB queries, creating a performance issue with a naive implementation. This is mitigated with libraries that batch requests transparently, or with clever structuring of your schema that groups similar attributes into one query.
From my experience writing a GraphQL server, I completely agree. This could really impact performance.
I looked at multiple solutions including join-monster[0] and prisma [1] and settled on Prisma which I run on docker between my graphql server and my AWS RDS database. It translates queries into SQL for you :)
I wish I could use those, but the backend I'm working with is DynamoDB, with a relatively normalized data model. As in, we're using a non-relational database in a relational way, which means we're doing joins in javascript. GraphQL for us is a way to at least keep from doing that and abstract our client/server interactions so we can eventually transition to a more sane SQL environment.
You can return an array of nodes, with each node having a depth property. Sometimes the client needs it flattened anyways, it is better for normalization.
I have 2 very naive questions about graphql. I am not trying to poke holes or antagonize, I just want to learn.
1. First one is, if graphql is a query language - why not use an existing query language that many people are familiar with already?
Eg instead of doing "query{ users(limit: 10) }" why not just send in the request's body "SELECT * FROM users LIMIT 10;" Or an ORM like query and have the server execute that (after of course parsing/sanitizing/normalizing it first)?
Why do we need one more ORM like language/abstraction on top of already our language abstractions? If my backend is in Postgres and ElasticSearch, can't I just have the client shoot sql and ES json queries instead? Why the new language?
2. How does it secure against the client trying to make super heavy queries? I always find it funny (and it is a good lesson) when an intern writes an ElasticSearch query that tries to return a 300gb JSON object back to the browser and crashes everything in the process (not production of course). I 've read that GraphQL has built-in checks for timeouts and query complexity, but if I am able to write optimized and highly performant SQL is there any guarantee that the GraphQL layer will output the same results?
To answer your first question, GraphQL allows the client to safely declare exactly what data it needs based on the GraphQL schema.
We don't want clients to be able to arbitrarily execute server side SQL queries, that's dangerous and unsafe. Which is why REST is even used as an abstraction over the backend in the first place.
The problem with REST is that it's not very adaptable. Any time a client needs a slightly different payload, either an entirely new endpoint has to be created on the server side or you have to version the API to handle evolving use cases.
I understand and agree with you with the issues about REST. But I still don't understand why GraphQL is not unsafe. For example, assuming that we have an endpoint that returns a list of users but skips banned users. Would I have to do something like query{user(banned:0)} ? That would be pretty unsafe as well. So how is graphql different than executing limited queries on the server?
The equivalent REST implementation would be `/users?banned=0`, which would be equally unsafe if you didn't want clients to view banned users.
In both instances, you would want to implement client authorization, so only clients with special permissions would be able to execute that query, or would simply get a different super/subset of users.
I think you're conflating the purposes of GraphQL with that of SQL. GraphQL is not meant to be a general-purpose, all-powerful, turing-complete query language like SQL, it is simply a way for clients to specify the exact data structure they require, based on a server-defined schema.
Authorization issues like you highlighted would have similar solutions under both implementations, GraphQL does not claim to do that out of the box.
SQL could work here but I think GraphQL is better for client apps anyway because it's based on very easily serialized graph structures instead of a string of english words that must be parsed. The client and the server can both work with it much more easily.
I think if you wanted to, you could map SQL onto GraphQL. Someone might have already done that. The opposite is also very likely already done (GraphQL mapped to SQL).
Unfairly, in my opinion. SQL92 is not Turing complete, yes. SQL92 is pretty old, and implementations have definitely added features since then. The more-upvoted response immediately above the one provided in that StackOverflow question provides examples of Turing completeness in SQL.
GraphQL queries are strongly typed. You can only add fields for what your schema defines. And no, in that case you would simply have a users query and in the GraphQL resolver on the server side for that query you would just filter out banned users. If there's anything you don't want exposed to the client, you simply leave it out of your graphql schema.
Got it. So the above example that I gave with SQL is pretty much the same thing as GraphQL. Assuming you would have a SQL resolver that just like the GraphQL resolver filters out things that should never hit the DB. So GraphQL is a middleware for safely querying the database openly by untrusted 3rd parties. Thanks that explains a lot, it's a pretty interesting idea actually
The piece that got me hooked on GraphQL (prior to implementing servers with it, and now I really like GraphQL—but it’s not a silver bullet in any way) was a podcast where one of the creators of GraphQL at FB made it clear that, from the server side, every field in GraphQL is a function, not a field on a record.
It means that, although the FE is specifying the shape of the data it wants, the BE can determine whether to return the data or not. Also, errors are additive and may be returned in parallel to the requested data fields.
It doesn't secure the client inherently against super-heavy queries. GraphQL is a spec, that's an implementation concern.
One cool thing though is that if you define an schema that includes a field that is expensive to resolve, the server will only attempt to resolve it if asked to by the client (defined by the query it sends), whereas a REST implementation would still need to resolve the field, even if the client in question isn't making use of it.
GraphQL is awesome. As a consumer of a graphql api, it's a no-brainer, it's so much easier/faster/flexible than REST (obv. a well designed rest server/client can be fast and flexible and get caching right, but that is not typically the case (whereas caching and what not is typically built in to the graphql clients)).
I think the server-side of graphql is holding it back right now. It's harder to get started building a graphql server than a rest server. Another issue I see is lots of graphql servers are just proxying an existing REST API which is unfortunate since you're just adding latency and complexity (for a rather significant front-end development boost).
My experience trying to build out the server side backend in GraphQL for various features was miserable:
* you are adding an additional abstraction layer on top of your data store (in our case, SQL), with significant cost in complexity and speed. This was for queries that served many thousands (or more) of requests a day, btw, so maybe it would be fine for apps w/ just tens or hundreds of users.
* trying to get any complex queries performant was _very_ difficult. We knew how to avoid n+1's w/ SQL and our ORM, but doing so with GraphQL loaders was very difficult and required some very intense promise-based code that was incredibly hard to debug or understand.
* the abstractions felt all wrong - it often felt like our the GraphQL loading layer was completely disjoint from the existing domain model. Building out the GraphQL layer often came at the expense of the domain layer -- maybe this was due to our own inexperience in mixing the two ? Anyway, it felt similar to how impossible it felt to build a decent domain model back in the days of Javabean / EJB frameworks.
That was my experience around a year or year and a half ago, anyways. I definitely see the value in the declarative nature of GraphQL for client usage, but the cost on the server side felt _very high_ to make it a reality.
It’s miserable when your starting point is the default js implementation for the executor, but if you write your own executor you have a lot of freedom to do cool stuff.
I didn't have that experience. Plus you can always have both REST and GraphQL. Your GraphQL server can simply call your REST endpoints. If you already have a REST API built it's not that much work to set up a GraphQL gateway.
Not true per se, the name GraphQL is you are conceptually thinking about your data in terms of a graph. The underlying data store could be parsing a CSV file to resolve your data & graphQL still adds immense value.
Having used graphql in production environments for about 6 months now, I would say that it is amazing and should be a first option for new APIs. The big selling points for me are:
1) self-documenting and explorable API (via GraphiQL interface) mean I don't have to ask backend if this or that value has been put in there.
2) expensive-to-fetch attributes returned in a query are only fetched/calculated if asked for. This means we don't need to split off logically similar queries into two REST endpoints to optimize the site
3) strict type and value checking mean I will always get back what I expect to get back. Attributes can be deprecated and throw a warning if I ask for them. If I ask for an attribute that is outside the spec it will give me an error. This is great for code maintainability.
4) frontend and backend can communicate more effectively with a standard contract. Building a react frontend with Apollo made me constantly question why I was getting paid so much to do what I did, it was that easy.
5) Implementation specific, but Apollo is really amazing and handles caching, dependent queries (I can ask a set of queries to refetch if I perform an update), and is dead simple to create a frontend around.
I haven't played with graphql much yet but one thing that really struck me most was that it's possible for a single graphql query to make multiple queries to database. I read that dataloader helps in this case, is this similar to what you mentioned in #5?
#5 was just about client caching. Yes it is true a naive graphql implementation can make multiple db queries which can create a performance concern, but there are ways to mitigate it with various tools, or with clever structuring of the schema.
Its an artifact of being able to define an individual, independent resolver for each attribute. The advantage of this though is that it makes it easy to stitch together multiple data sources into one schema, so GraphQL shines especially as a wrapper, or as a query layer on top of a non-relational data store, like Mongo or Dynamo
Apollo client is great in many ways. Where I'm having trouble is as my React app grows I need to update the local cache when I add or delete items. You have to tell Apollo what to do and it becomes more complex as the app grows. Almost like you're mirroring your db on the client.
You could just always tell it to re-fetch, effectively disabling caching.
> Almost like you're mirroring your db on the client.
Well that was the goal of mini-mongo in Meteor, before they ramped down development to work on Apollo. I think mirroring the db on the client is sort of the goal.
I've really come to enjoy using it, especially with Apollo and Absinthe. I now make client-side changes all the time without having to touch the server at all.
It did take me some time to wrap my head around the details, but the ability to project many of the requested fields more-or-less directly into the SQL SELECT, and being able to batch things together to turn 1+N queries into 1 has made writing resolvers a total breeze. Apollo and Absinthe are some of the most polished pieces of OSS I've ever worked with – well worth the extra boilerplate for non-trivial projects.
Not saying anything about GraphQL in particular as I have almost no experience with it in practice, but there are cases where REST is simply unbeatable, especially if you're very comfortable with it: Simple APIs, encapsulating simple data models. Often you can get away with using the simplest and most common (client- and server-side) frameworks available to implement those, and that's all that's needed.
No. A GraphQL server is agnostic to how your data is fetched. You could pull the data from a REST endpoint, or put the SQL query to the db right there in your resolver on the GraphQL server itself.
REST and GraphQL have slightly different use cases. REST is simpler, both in concept and implementation than GraphQL. GraphQL offers more flexibility in exchange for that added complexity. GraphQL puts more of the burden on the front-end/client-side code to define the data they want, which is a good fit for services that have many different consumer platforms that require different data (web, mobile, etc), or when the scope of the data that should be returned is very context-specific (e.g. I only need preview-size images for this view, but I need a large images and more metadata for another, and some other requirements for a third).
It's great for consumers of APIs, especially focused on web frontends and mobile apps. That's about it.
Anyone offering APIs can help their users but like all things it takes effort to do and usually isn't worth it unless it's a very big service with lots of users.
If you control both the client and the server then there's little, if any, benefit over REST.
My 2c: I really like it to fetch data on the client side, works well with component based frameworks. But data mutations are a mess and lots of stuff you have to figure out yourself on the server side.
Author here :) I used Apollo for the first time in this application, it was great throughout the implementation, but this was truly the worst part to implement. It came with multiple flaws in my eyes which are perhaps due to Apollo being in early stages or me just being inexperienced with it.
- updateQuery was a deprecated property when I implemented it and one should use update instead. But it wasn't and maybe still isn't possible to use update for the fetchMore scenario.
- I couldn't find any real consensus on keeping immutable data structures in Apollo. Since I favor to work with it, I kept it this way. However, in the case of pagination and deep nested data structures, you would have to fallback to a library to deal with it. Apollo adds their own immutable helper to the whole tech stack which means to adapt yet another API... I wouldn't want to do it and I think it's a bad decision to introduce yet another immutable helper. That's why I picked the object spread operator.
- I would hope that updateQuery goes away and one would be able to use writeFragement in the update property to update the paginated data. If it's possible, I would love to speak more about this topic with people more knowledgable than me in Apollo :)
Having just written something similar to that, I agree, the pattern for server-side pagination is pretty clunky. However that is a client-specific implementation detail - GraphQL is an implementation-agnostic spec.
Honestly, `doFetchMore()` represents all of the worst aspects of "modern" javascript -- incredibly terse syntax that does little to help the developer understand what is actually happening.
I personally dislike some things about Apollo & prefer Redux+graphQL+RxJS combo, and can't wait to see what happens w/ React Suspense
The problem with Apollo that I cannot get over is it couples data fetching to components. The whole reason Redux is popular is it decouples UI actions from data fetching. It decouples asynchronous logic from state mutations. Apollo intentionally couples that logic.
With redux, I can dispatch an action anywhere in my app to trigger a data fetch. That data fetch itself could be a graphQL query.
With apollo, I have to mount a component to trigger a data fetch. The workaround to pre-fetch data is to render a "dummy" component, causing it to pre-populate the cache.
Another issue, Apollo batching seems to be "all or nothing". Any components mounting in the same event tick get batched into 1 query. This is a problem when one query is in the critical UI path & is fast, but Apollo has batched it together with a slow query that is not critical. Think about mounting an <Article> with some <Comments> below it, but you don't want to wait on rendering <Article> just because <Comments> is slow. Now you have your fast UI components blocked from rendering because you're waiting on this batched request.
With Redux I would get around the batching problem with redux-observable middleware. I could write an epic that looks for actions, buffers these, issues multiple graphQL queries (while giving me control over how to batch them, if at all), and deal with responses in real-time as they arrive.
With Apollo v2, they totally ditched Redux. There is no escape hatches in Apollo to deal with the problems that Redux solves. That being said, Apollo cuts out tons of boilerplate & its a great library, so give it a try & decide yourself. Personally I think co-locating data requirements with components is a wonderful concept but the current implementation of graphQL clients is quite stovepipe.
Also the landscape is about to shift again due to React suspense. You would throw a promise in your render() method, and React will block rendering of the component tree within a <Placeholder />, conceptually similar to error boundaries. There is rumor of a Redux integration as well.
If you're not yet onboard the React hype train I'd recommend the author's Road To React course (https://www.robinwieruch.de/the-road-to-learn-react/), it's a good introduction to React and modern JS and a leaping off point for more complex stuff.
I worked through this in ~8 hours the weekend before a job interview and had no trouble building and updating a simple React single page app as part of the onsite.
Coming from a heavy-client implementation using backbone models, or in another instance Ember Data with JSON-API, React+Apollo feels like a breath of fresh air, and simplifies my life to the extent that I wonder why I get paid so much to do what I do.
I would suggest looking at Github's API explorer (https://developer.github.com/v4/explorer/). You can assemble graphql queries in there, inspect the results, and copy/paste the exact query into an Apollo component to make your own app.
REST is still a bit simpler to implement on the server side but once you have the GraphQL API built it is infinitely more maintainable when you're working with multiple teams, and API consumers, each with their own needs and focus.
One weakness I've observed is that since each attribute of an object can make its own DB query, you can have a situation where a single GraphQL query can create dozens, or even hundreds, of individual DB queries, creating a performance issue with a naive implementation. This is mitigated with libraries that batch requests transparently, or with clever structuring of your schema that groups similar attributes into one query.
Also, tree-like data structures are not well supported (like a comment thread, where comments can have replies that recurse infinitely. GraphQL straight up doesn't do that)
However I think that the benefits outweigh the weaknesses considerably.