Hacker News new | ask | show | jobs
by xyzzy_plugh 2459 days ago
> The following query produces the data that we want... we just need to figure out how to write a batch loader that generates the same result.

This is where the abstraction breaks down. Should you care what the query ultimately looks like? For anything beyond a toy, you sure should. But how far should you go to ensure the abstraction produces the implementation you want? Why not just skip to the implementation you want?

Sooner or later, you need to break the abstraction. I'd rather just write the SQL for all my endpoints.

I'm not bullish on GraphQL for the same reason I'm not bullish on ORMs. For some things, an abstraction suffices, but it's far from a universal solution. If I have to break out of the GraphQL abstraction, why bother in the first place? Is there value in a mixed GQL/REST API?

> GraphQL can be as efficient as REST, but requires approaching optimizations from a different angle. Instead of upfront optimizations, we lazy-load data only when required, loading it in batches to avoid excess trips to the database.

Admittedly, the author does a good job of preserving the abstraction, but I'm hesitant to expect most devs to dive this deep, and thus hesitant to expect this level of quality/performance from a GraphQL backend.

6 comments

>For some things, an abstraction suffices, but it's far from a universal solution

Fair, but the ORM covers 80% of what you need. Mapping to objects, autoloading associations. These are simple selects and joins and make up a large portion of most information based systems.

When something needs performance and the ORM isn't optimizing appropriately, sure, drop to raw SQL and optimize away. Even then, what did you gain? Shaving 100 seconds off a 200 second query, sure that's worth it. But if you optimize and manage to shave off a mere five seconds, was it worth it? Maybe. Maybe not.

For most systems where you're displaying information, you just need good enough, an ORM that feels productive and doesn't get in the way (e.g. ActiveRecord).

I agree with everything except the five seconds note. Web pages loading in fives seconds is an awful experience.

Optimizing expensive queries not only improves the user experience (be it web or api or whatever) but also can open up entire usecases that otherwise wouldn’t be practical. Five second queries are a primate candidate for DOS attacks, whether intentionally malicious or by users spamming refresh, retry, etc.

I think he meant shaving only 5 seconds off of the 200 second query.
Maybe, but shaving almost 5 seconds off a 5 second query can be quite realistic for some pathological ORM queries. I use ORMs for the simple cases, but it's definitely worth cutting them out in the complex ones.
I've seen many mobile developers be frustrated at constantly needing to ask the backend developers to make new rest endpoints for them because the existing ones were not exposing the data in the way they needed it exposed. And similarly I've seen backend devs be frustrated at continually having to make new endpoints for those nagging mobile devs. If you have teams working independently of one another, where the backend team is providing a service to the frontend team(s), it makes sense to use a tool like GraphQL to able to go "here's access to anything you need - go ahead and use it however you need" instead of needing to keep coming back and building new endpoints again and again.

If you're the one both creating and consuming the endpoints, then sure, it doesn't matter where you write your query, and building abstractions to move the query all the way out to the client is a waste of time.

> here's access to anything you need - go ahead and use it however you need

But that's almost never what you want: as the backend developer, it's one's job to care about security and the performance of queries. As far as I know there's no straightforward way of automagically translating every possible permutation of SQL query primitives into GraphQL primitives in a way that is intuitive to a frontend developer, reasonably performant and secure, so at some point the backend dev has to step in and do the work of connecting the GQL layer to the SQL layer.

I'd say that that you can generalize fulfilling the queries and making them secure. It's not easy to generalize making them performant. But you can be more reactive on performance, when a query becomes a problem you break out of the general system and write something optimized.
It seems that the problem that GraphQL solves is Conway’s Law. For an org that can keep the backend and frontend together, it’s not much of an improvement.
I would argue that I've never seen even small teams keep the backend and frontend together if they are doing anything important. GraphQL is, if nothing else, a nice layer over your data that allows the backend developers to see what the front end developer are doing better than REST. That introspection is really helpful.
There’s a different way of managing graphs queries. When you remember that you’re effectively running a query per layer of the graphql tree (much like an ORM does) then you can optimise all the stuff that sits in a single layer by creating a view to aggregate as required.

You get to keep the data aggregation complexity in the data layer and then have a loader that sits above it which is effectively just choosing which subsection of the tree to load.

I’d recommend looking at Hasura which does a great job of this.

The problem with "just SQL" is that you arguably want to compose queries.

A foundational observation that GraphQL makes is that every query is different depending on the client's needs. You can do "SELECT * FROM users JOIN companies ON companies.id = users.employer_id" and call it a day, but now you're already probably overfetching, and the next evolutions of your codebase ends up accruing extra logic to add or remove features: Some queries want to fetch just a subset of fields from each of those relations. Some queries don't want to fetch any companies. Some want to paginate, filter, order in different ways.

You can do all that in REST, of course. You can use an SQL builder to build the final SQL query programmatically. You can add WHERE clauses and FULL OUTER JOIN clauses to do your N+1 queries on foreign associations, with some kind of query parameter to define what associations the client wants. Field selection is also doable with query parameter and so on. You can also centralize a lot of the boilerplate to do this (e.g. FULL OUTER JOIN clauses on associations, if you do them, need to be untangled into object graphs after you get the flat results back).

And so on.

What you end up with is pretty much GraphQL. You need code — identical for every relation, modulo client-specifiable constraints and parameters — for filtering, pagination, selection, joining, etc., all of those things. You need some notation for letting the client specify what they want to fetch, both fields and associations. Then, even when you have ended up with something approximates GraphQL, you have to write a custom client for it that only works for this exact API. And then you have to write documentation. And tests, tests which don't just exercise your "business logic", but also exercises all the REST-based query API stuff.

The benefits to GraphQL are that it's standard, so you can piggyback off existing libraries both on the client side (things like Apollo) and on the backend, libraries that are already tested and documented. GraphQL itself defines a schema, which serves as documentation and a way to machine-generate clients. As a case in point, try out Prima's GraphQL playground app [1]. It's basically a web-based IDE for GraphQL. You can point the app at any GraphQL API, inspect the schema, write queries (you can autocompletion of all fields, queries, mutations, etc.) and see live results. It's a really productive debugging tool.

That alone is worth a lot, and that's why I'm bullish on GraphQL.

[1] https://github.com/prisma-labs/graphql-playground

Or you can use OData, which has the same benefits as graphql.
Maybe in principle, but I don't see OData having anywhere near the traction that GraphQL does.
I think GraphQL will certainly find it's place after the initial hype. There are some good use cases. But in my opinion it should be only used on a need basis.
I agree but can't ignore this idiom

> If you start without an ORM you end up writing a custom ORM eventually and often that custom ORM is inferior to the third party one you're considering

Funny enough this engineering problem becomes an HR one:

If your recruiting and training processes can consistently hire devs actually fluent in SQL and web application architecture you can completely avoid the ORM and just write good modular and testable code that avoids the chaos of poor SQL performance etc.

If you're like most organizations and have trash recruiting and training processes for developers, you don't have the luxury of trusting your teams not to fall over with technical debt, and may prefer to use an ORM for immediate short term complexity and also long term technical debt management (since the ORM effectively becomes a framework with consistent patterns that can scale across teams).

I think this is a really great point, as often the HN answer is "hire better developers". The majority of companies outside the HN bubble are full of very average developers. ORMs work well in that sort of environment as it allows them to be productive, and any performance issues can be tackled by the less-average developers on the team.

A while back, I worked with a client who's stack was predominately Java but due to company policies paid 25-50% under the going rate for decent developers. This caused two problems: not being able to hire enough developers, and some of the hires not being great.

We solved that problem with a technical one: the stack moved to Clojure powering backend services with Node.js on the front end. The coolness and newness of those technologies at the time meant they could make hires. Ironically, now the demand for those technologies means they command high salaries and they're back at square one.

Bit of a long story, but my point is that sometime you need to solve people problems with technology and technology with people.

> The majority of companies outside the HN bubble are full of very average developers.

I think you underestimate the HN bubble : I'm an average dev (I don't even have a Github account !) yet I'm in the HN bubble.

Remove ORMs and write raw queries for everything? Or write our own orm/query builder?

I didn't like orms much at first but with some practice you can shape your queries. Much neater and cleaner if that matters in your case.

> This is where the abstraction breaks down.

No. In REST, the client is responsible for resolving IDs into entities.

In GraphQL, that responsibility largely moves into the backend.

This is not "breaking the abstraction", this is exactly what GraphQL is good for.

It breaks down, when you write an API to solve a task, rather than when you write an API to expose resources and then let the consumer of the API define the task.
Well, but that's not breaking down GraphQL as an abstraction, isn't it?