Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whatif discussion: switching to GraphQL #77

Closed
bk3c opened this issue Oct 20, 2019 · 6 comments
Closed

Whatif discussion: switching to GraphQL #77

bk3c opened this issue Oct 20, 2019 · 6 comments

Comments

@bk3c
Copy link
Contributor

bk3c commented Oct 20, 2019

I'm conflicted about even bringing this up, but the more I think about it the more I feel like changing REST->GraphQL would actually have a pretty limited impact at this point, especially compared to ~6mo from now. I'd like to use this issue to document our thinking no matter which way we decide to go, since it'll surely be something that will be revisited later no matter which path we choose.

NB: Let's separate the timing of any potential change from whether or not it's a good idea. This is to discuss the latter, then we can get into timing if appropriate.

Reasons to Move --> GraphQL

  • Listings schema alone is now getting complicated enough to merit the problems GraphQL solves. Otherwise we're either going to have a bunch of data duplication on the upstream or a huge mess of endpoints. This will be a much more real issue when we have a partners portal with editing of listings.

  • Our state management needs might be simple enough that we could just solve them with the Apollo client cache, avoiding mobx (or redux). See here or to a lesser extent here for examples.

  • Support for GraphQL exists for other likely Bloom service / UI stacks. Apollo client works well with both React and modern Angular. Ruby/rails and python/django both have good native libraries, and there are even some projects for Salesforce, if we wanted to try to do that directly instead of using a proxy service with more robust translation logic.

  • My expectations for the importance of easy outside integrations have diminished relative to the importance of having the best tool for core team / trained partner velocity over the next 1-2 years.

Reasons to Stay with REST

  • REST APIs are still the lowest common denominator, and most accessible for new developers and potential ecosystem partners.

  • Tooling for REST is still more mature, and baked into a wider array of libraries.

  • Less immediate rampup / conversion time

  • Less dependency complexity

@jaredcwhite
Copy link
Collaborator

@bk3c Take everything I'm about to say with a grain of salt since I haven't used GraphQL much to date…

I feel like adding in a GraphQL endpoint down the road is certainly worth exploring, but since at the moment we're essentially calling the REST endpoint for all the data we need during the static site build process, I'm not too concerned about having a giant wad of JSON with a full dataset coming along at once. Once there's more data going back and forth dynamically over the wire during run time (if it even comes to that), that seems a logical moment to bring in GraphQL.

@software-project
Copy link
Contributor

@bk3c I don't have experience with GraphQL. It's an interesting concept but for now I'm with Jared.

@bk3c
Copy link
Contributor Author

bk3c commented Apr 22, 2020

@bencpeters
Copy link
Collaborator

I'll add my $0.02 on GraphQL here based mostly on playing with it on the platform that we've been building at Ovio.

If you are writing resolvers mostly by hand, GraphQL feels like a LOT of boilerplate for very little value. I recognize this is a bit of a strawman argument since hand rolling resolvers for a GraphQL backend is very likely not the path that we'd go down, but I think it's worth calling out.

More realistically, we'd probably use an automated mapper to produce a GraphQL API from schema introspection. This world is a bit appealing to me because it allows you to avoid most of the boilerplate of writing backend code; instead your schema "becomes" your API. I've used three of these:

  • Hasura Basically a API-in-a-box: runs as a stand-alone service that gives a degree of DB management and API customization along with automatic generation. This is a very efficient (time-wise) way to stand up a pretty full-featured API, and the performance seems to be excellent. It uses JWT token role-based authentication, and allows for both row and column-level permissions. This would make it easy to plug into a microservice architecture with an auth service if we wanted components with virtually no boilerplate. It even includes optional features to track migrations/schema changes. The team seems to be iterating quickly and to be quite talented, so I expect it to only improve going forward. I have two main complaints with this service from playing around with it. The first is the way that relationships are handled; they generally go through join tables which all need to support a fair amount of flexibility around sub queries/conditions; in real life this ends up making "graph traversal"-type queries a bit cumbersome:
article {
  id
  title
  articles_tags {
    tag {
      id
      tag_name
    }
  }
}

The suggested way to flatten these is by defining views in SQL, which works fine for read-only data, but mutations end up being more complicated. The second issue I have is that if you really need to customize the service and add some functionality totally outside the scope of what is exposed by introspection of your schema, you basically have to spin up another service that Hasura wraps with async calls and then exposes as GraphQL. Hasura calls this Remote Schemas, and has reasonable instructions for how to set them up, but then you're basically spinning up a separate microservice. You can certainly make the argument that if you need extra customization in this way, it probably belongs in a different microservice anyway, but it can be annoying if it's just a relatively small additional amount of functionality that is needed.

  • Postgraphile is superficially similar to Hasura in that it aims to generate a full API purely from introspection of a Postgres schema. It is a bit less opinionated in how it is deployed: it can be run as a stand-alone CLI, as a Node library, or even just to auto-generate a stand-alone GraphQL schema that is implemented separately. For me, I've found the "Node Library" to be a functionality sweet spot, because it gives you most of the benefits of API-in-a-box, but still allows for easy customization as needed using battle-tested libraries (e.g. Express middleware). Philosophically Postgraphile takes the opinion that the database is already very capable of defining almost all functionality, including authorization, so the backend should more or less step out of the way. It creates a context for a request (customizable using Node middleware) that can include async operations if needed for authorization, then provides that context to the database for resolution. You then use postgres Grants and Row Level Security Policies to enforce access. Out of the box, Postgraphile creates similar graph structures through join tables to Hasura, but allows flattening and customization through views and Postgres function definitions, which end up give you a fair amount of flexibility. It also has a robust plugin system (the whole core library is actually architected as a set of plugins, so it's easy to enable/disable bits of auto-generation functionality by modifying the list of core plugins that get loaded), which has led to some powerful community plugins, and also makes it fairly easy to add a wide variety of customization (query conditions/filtering, join resolutions, auto-generated schema customization, custom orders, etc.). I ended up choosing Postgraphile over Hasura for our platform at Ovio because of the additional customization and ease of integrating with other parts of the Node ecosystem.

Both Hasura and Postgraphile have excellent documentation.

  • Neo4J-GraphQL.js. I also played around with matching a Graph Database (Neo4J) to a Node-based automatic SQL generation stack. This was an interesting experiment, and did make some things very easy - in particular it was very easy to define a schema once in a common JS package, then have the database, API, and front end all have access to the same definitions. I found traversing graph nodes to be slightly easier with this solution than the SQL-based ones I tried out (perhaps not suprisingly). This is of limited benefit for query/read operations, but makes a bigger difference for mutations in my experience - it's much easier, on the surface at least, to create relationships and insert nested objects with a graph structure. Neo4J also can produce some very nice visualizations of your data right out of the box in the browser:

image

However, I found the SQL generation package to generally not be production ready. It takes an almost all-or-nothing approach to generating mutations (e.g. no mutations are auto-generated, or ALL available mutations are generated, with a weak ability to specify a blacklist). It also doesn't have very robust authorization support built-in, and I found it required doing a LOT of custom coding to do "basic" permissions controls that are built-in in other frameworks.

Neo4J also brings with it a whole new query language to learn and a LOT of data integrity and performance pitfalls for new developers for fairly dubious benefits in my opinion (to be fair, I come at most No SQL technologies with a "guilty til proven innocent" bias - I think they need to be very good at solving a specific problem that I'm having with SQL to prove their worth, since there's a LOT of value you get from SQL-based tools).

I haven't tried any of the GraphQL generator tools for Django or Rails, but they seem at least superficially similar to Hasura/Postgraphile in that they aim to generate a fully featured API from basic DB/model definitions. I'd imagine they're probably similar, functionality-wise.

Non-framework-specific thoughts on GraphQL

Personally, I think for internal facing APIs the benefits of GraphQL are somewhat overstated. It solves some problems, and the type safety in particular is nice, but it often has an "insecure by default" mentality and securing an API properly with e.g. persisted queries strikes me as almost as much work as just building a REST API to begin with. In cases where you need an eclectic selection of nested data too, it can actually be more work and larger payloads than a well-structured API endpoint that you designed for your use case and designed aggregations for (to be fair, you can define custom GraphQL routes for such aggregations too, it just doesn't end up being the "default" solution, and they're not auto-generated). Without persisted queries, conditions, filters, and relationships end up providing relatively easy attack vectors to generate very costly queries if you don't have timeouts/throttling and restrictions properly configured.

@jaredcwhite
Copy link
Collaborator

Wow @bencpeters, thanks for the extremely thoughtful and detailed writeup! I'll have to dig into those and do my homework now! I also appreciate your willingness to look at the cons as well as the pros for such an approach. 👍

@software-project
Copy link
Contributor

Really nice @bencpeters. Hasura looks really interesting. I would love to compare it with Redwood.js that @jaredcwhite suggested. Redwood look more like a complete solution but Hasura will be more mature.

@bk3c bk3c added this to the Farther Future milestone Jun 16, 2020
jaredcwhite pushed a commit that referenced this issue Jul 24, 2024
… for new listings (#77)

* feat: remove common digital application from partners new listing and add required text for URL

* style: add comment

* fix: set application method type as ExternalLink for custom URLs

* fix: comment code out instead of deleting it

* fix: lint

* feat: add check to disallow creating new listings with common digital application
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants