GitHub client built with React, Apollo, GraphQL
github.com
github.com
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].
Edit: as discussed many times here eg. [2]
[1]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...
[2]: https://news.ycombinator.com/item?id=3635085 - Nobody understands REST or HTTP
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... :)
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).
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 :)
[0]https://github.com/stems/join-monster [1]https://github.com/graphcool/prisma
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?
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.
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.
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).
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.
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).
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.
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.
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.
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
> 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.
* 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.
I’ve built a graphql interface here https://subzero.cloud on top of the powerful rest interface of https://postgrest.com which in turn sits on top of postgresql.
The end result is you define your tables/views/functions in sql and on the other end you get rest+graphql api to your data
You can simply create one graphQL field corresponding to each REST end point you have now, and have it do an SQL query in the resolver.
> you are adding an additional abstraction layer on top of your data store
yes that is one of the main benefits (it allows you to coalesce data from multiple data stores).
- 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 :)
https://facebook.github.io/relay/graphql/connections.htm
It's not really a GraphQL specific method though. I've seen the same sort of pagination used on REST APIs.
I think the complexity of it on the client could easily be abstracted away as part of apollo or as a separate package.
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.