Hacker News new | ask | show | jobs
by Azeralthefallen 2930 days ago
I recently moved one of our API's from Loopback to GraphQL using Postgraphile. It has been a nightmare.

- Access restrictions, and security is very painful. Trying to enforce RBAC through postgresql policies over hundreds of tables with slightly different policies is a nightmare. Hiding all this on the DB's end and burying this stuff in migrations sucks for anyone trying to develop on it.

- I still have not found a good solution to querying/filtering over things like nested JSONB objects without writing my own resolver functions, which then complicates numerous things.

- Versioning is a mess, especially when you don't have an easy way to force clients to upgrade (i.e. mobile apps), along with other parties using your graphql api directly.

- However the worst thing so far to me has been i have watched as UI developers go from writing simple rest queries, and doing a bit of work on the front-end to writing extremely inefficient and convoluted queries. Even worse is when a third party is doing it and i have no say over what they do with it.

7 comments

Oh god, this sounds like a nightmare. I posted this in another story on GraphQL, but it is now clear to me that GraphQL is being used in ways that make it a disaster, and part of the problem is that there are tools (sounds like Postgraphile) that encourage this misuse.

My thoughts on the issue:

- Perhaps GraphQL's biggest mistake is using "QL" in the name. GraphQL is NOT AND SHOULD NOT be a replacement for SQL, and that's where I think folks get confused. Whenever I see complaints about "I can't do joins, and GraphQL is missing generic filter statements" I'm sure people are totally missing the point of GraphQL.

- Your last bullet point (about UI developers going from writing simple rest queries to convolute front end queries) made me extremely sad, as when done right this is where GraphQL truly excels. Where I've used GraphQL to great success is to define my types to very closely match what the UI developers need. In fact, I have the UI devs themselves write the type and query definitions, code reviewed by backend engineers. This makes it trivial for UI devs to code against, for example, a mocked up version of the APIs while backend devs build out the resolvers.

The issue is that GraphQL is GREAT as a layer BETWEEN your lower level data sources and backend APIs and your front end, and the "translation layer" is done in the resolvers. This is especially true for native clients when you may have multiple versions of clients in the wild.

Amen. GraphQL is neither a query language, nor particularly graph-oriented, IMO. It's a framework for building APIs. It's comparable to a really full featured hypermedia framework. I'm a huge fan, but the marketing is definitely confusing.
Co-author of the tutorial and founder of prisma here.

Adamkl is right, you are missing a layer in your application. postgraphile, prisma and hasura turn your database into a graphql api, but you don't want to expose that directly. Graphql Bindings allow you to construct a Graphql Api by mapping an existing Api and implementing extra logic.

This allows you to keep permission and business logic in your application layer where it belongs.

My Co-founder Johannes is giving a talk on this technique at GraphQL Europe on Friday. The talks will be available online shortly after, so I would recommend keeping an eye out for that if you are not going.

So basically you are running two graphql services, and using the private one as an ORM?

Doesn't that drastically increase complexity in the fact that you now need to maintain multiple services? Why not just skip postgraphile and just use an ORM in your business layer? As opposed to being required to maintain multiple services.

It also feels very odd that since you can't even expose your private one to other internal services because all your authorization and access restrictions are now sitting in your business layer, which to me seems like a huge waste.

Correct, you are running at least two separate GraphQL APIs.

If you look at the architecture of modern tech companies they are already moving in this direction. Facebook has TAO for data access, and twitter has Strato: https://about.sourcegraph.com/graphql/graphql-at-twitter/ The purpose of these data services is to enable feature developers to focus on their feature instead of worrying about infrastructure, caching and scalability. Prisma is an open source version of that same pattern.

To the question of whether you should expose your database API to other internal services or not. This very much depends on the kind of architecture you are choosing. If you are building microservices you typically want to ensure that only the microservice owning the data can access and update it.

Facebook has a quite different approach where TAO enforces access control policies, and all services can query the same data. We'll explore how Prisma can support this pattern in the future.

To further this point a bit, because of GraphQLs introspection capability, it becomes easy to combine and proxy existing GraphQL services without redeclaring the schema every time. If you take a look at what Prisma is doing, it’s creating a GraphQL schema based of an underlying data source which you can then generate bindings off of to include in other applications (think auto-generated client bindings via SOAP WSDL files.

At my company, we are taking a different approach by defining GraphQL APIs that are essentially exposing pure data (similar to Prisma) and then we are merging them and proxying them through multiple layers that augment and alter the schema and data as it passes through.

We can do this because we are leveraging The ability to inspect and alter the abstract syntax trees that power GraphQL under the hood.

This sounds like a super interesting approach Adamkl! Do you have something written up about this somewhere?
I've had a few discussions about doing a formal write-up of what we are doing, but for now I can only provide a brief overview:

Our data APIs map from existing SOAP/XML services to small GraphQL sub-sections of our greater API.

Then, we merge those small APIs together with Apollo's graphql-tools, similar to what Airbnb is doing[1].

Now we are starting work on our proxy/processing layers using this[2] work-in-progress.

[1]https://medium.com/airbnb-engineering/reconciling-graphql-an...

[2]https://github.com/adamkl/graphql-proxy

This just sounds like you are missing required layers of your application.

I haven't used Postgraphile, but it sounds like you are just exposing your database directly out through a GraphQL wrapper. You'd be experiencing the same pain if you tried this approach with any API technology (gRPC, REST, SOAP, etc).

You should still have code for authorization and business logic, and your API should probably be exposing a focused subset of your data model unless your clients really need to be able to traverse the entire database.

Except utilizing loopback i simply define my models, what fields are exposed by models, and their relationships. I can easily implement RBAC restrictions via a simple access hook or ACL's.

To me having to maintain two separate graphql services for a single API seems extremely convoluted. I can't expose the private api to any other internal services, because all the authorization and restrictions are done on the application layer.

To me isn't that just using a GraphQL server as an ORM? To me that seems like an extremely roundabout way to do something like that.

Well, Postgraphile is offering you a shortcut that comes with limitations.

A more complete approach would be to structure your application in a more typical fashion, and put a GraphQL layer on top. Have a data access layer that maps from our back end sources (databases/REST/RPC services) to GraphQL types. Then have a logic/authorization layer, with GraphQL on top. Using Postgrapile seems to take those application layers away for the sake of convenience.

> because all the authorization and restrictions are done on the application layer.

Which application tier? It sounds like you're using GraphQL in-memory to query the database in the same process that's creating the GraphQL query. You should be treating GraphQL as a database, and it should not exist on the same machine that's using it. This way your GraphQL app has centralized permissions & auth, and all your other apps would then be beholden to whatever you implemented.

> just using a GraphQL server as an ORM

If you're using it the way above, then yes. But that's the wrong way to use it.

> - However the worst thing so far to me has been i have watched as UI developers go from writing simple rest queries, and doing a bit of work on the front-end to writing extremely inefficient and convoluted queries. Even worse is when a third party is doing it and i have no say over what they do with it.

To your last point, there are a few ways you can provide feedback on or limit the cost of your queries. I'd recommend checking out Github's article on resource limitation [0] and the section on query cost analysis in an article by the Apollo team [1].

[0] https://developer.github.com/v4/guides/resource-limitations/ [1] https://dev-blog.apollodata.com/securing-your-graphql-api-fr...

Have you looked at Hasura's postgres offering at all?

I haven't used either yet, but I spoke to one of the founders a while back and auth difficulties was one of the things we talked about. I believe they're open sourcing their data layer soon.

is it a legacy db? can you give more details as to in what domain you have hundreds of tables? with postgraphile, are you exposing your tables directly or did you create a special "api" schema with only views and functions that is exposed as an api?

as for your migrations problems, a combination of sqitch and apgdiff provides a way to work with sql similar to other languages (migration files are created automatically and managed by sqitch)

I am sorry but you are using Graphql the wrong way, it should be stressed that GraphQL never and never could replace your SQL(it is a wrapper around your SQL backend)