All for a little bit of a velocity gain from declarative syntax and horizontal/vertical filtering.
This code is probably very useful to the people who made it and those in the GraphQL+Swift ecosystem, but we were already here (and just starting to get drastically better) with OpenAPI -- it's not like all the effort is only going in one direction but GraphQL really feels like a less-than-optimal branch.
The final piece of the puzzle for me will be when someone starts to replace the dynamic properties of the more advanced REST-ful techniques (API discovery, link following, etc) in GraphQL. Then the circle will be complete -- we'll have rebuilt XML (in the "linked data" respect anyway, not so much the accidental complexity side) and it's related standards twice in under ~20 years.
I think of it as a domain querying language.
SQL is meant to allow you to write queries to your data model which are:
- arbitrary, and
- efficient
I don’t think of GraphQL that way. I think of it as the place where you encode your set of valid domain actions (i.e. not arbitrary). And I don’t think the consumers of the GraphQL API should think about efficiency. They should just specify what data they need and then the backend is responsible for figuring out how to query the data model efficiently.
In other words, I don’t really see any overlap between GraphQL and SQL in terms of the role they play in a stack.
One helpful thing this distinction allows, is type inference. You can trivially write a type generator that gives you the type signature of a GraphQL query in any language. This is precisely because of its limitations. That allows you to automate the validation of your frontend and backend speaking the same language.
You can’t easily infer the return types of arbitrary SQL queries. To me, that highlights the different purposes of the languages.
That is the point of the SQL language. It's declarative. You define what you want your data to look like and the query planner handles the actual fetching of the information in the most efficient way it can. Obviously it's not perfect and you still have to have someone who knows what they're doing to define schemas that make sense and indexes where appropriate, but that is a separate job from defining what data is needed.
> I don’t think of GraphQL that way. I think of it as the place where you encode your set of valid domain actions (i.e. not arbitrary). And I don’t think the consumers of the GraphQL API should think about efficiency. They should just specify what data they need and then the backend is responsible for figuring out how to query the data model efficiently.
This is how SQL works, so there is some overlap there. Optimizing SQL queries is might be a performance-seeking operation, but SQL is declarative, and it is left largely to the query optimizer to make your queries run fast. You can help the query optimizer make the query run fast, but that's all you can do -- and I can guarantee you that doing query optimization has not gone away due to GraphQL, you've just pushed the problem somewhere else, or you're forgetting the bits of your API that you've modeled awkwardly in order to avoid performance degradation/difficult-to-write resolvers.
But I think we're a bit off-track here -- GraphQL and REST is at a different level of abstraction than SQL. My point is that we've taken a step back from what we had already with REST rather than that people should be using SQL on the front-end. I think GraphQL is doomed to attempt to reach expressive parity with SQL but that's another conversation all-together.
> One helpful thing this distinction allows, is type inference. You can trivially write a type generator that gives you the type signature of a GraphQL query in any language. This is precisely because of its limitations. That allows you to automate the validation of your frontend and backend speaking the same language. > > You can’t easily infer the return types of arbitrary SQL queries. To me, that highlights the different purposes of the languages.
Most sufficiently ORMs can also give you this, and in other languages there are libraries that will compile-time-check the arbitrary SQL queries you write and won't compile if they're invalid. What you need to have that kind of thing work is sufficient type-checking power (Typescript offers this) and sufficiently rich metadata (there are some examples in the haskell[0] and rust[1] worlds). It wasn't necessary to throw away REST to get these kinds of benefits. I've been quite happy with TypeORM for example, and it would form a good base for this kind of effort -- I don't know a library that's already doing it, but this actually isn't as hard as you think, especially for the simple case.
I'd argue that there is no difference (without too much evidence, to be fair, as I am not an expert in inner working of GraphQL) in the difficulty or parsing and validating a GraphQL query for the simple case (i.e. the actual subset of SQL that GraphQL represents) than actual SQL.
[0]: https://hackage.haskell.org/package/postgresql-typed-0.6.1.2...
> Tools like GraphiQL make exploring APIs a pleasure.
This is an innovation of the developers of GraphiQL, not of GraphQL itself. It's possible to build IDE support similar ot that of GraphiQL, with the rich metadata and schema data that is available by design in OpenAPI + JSONSchema + JSON-LD + HATEOAS land. The problem is that no one did/was excited enough to, the slog from Swagger2 to OpenAPI3 might have sapped the enthusiasm of the community just enough (or the emergence of GraphQL), but it's not that the tools isn't possible with other approaches.
> There is also the nice addition of strict typing and there are libraries that automatically generate TypeScript types for you from the schema and/or your operations.
This was already present with OpenAPI and related tools, so I personally don't put this as something that GraphQL brought about.
> Tools like GraphiQL make exploring APIs a pleasure.
No argument there. Is there something similar for traditional REST? For some reason, I thought the point of HATEOS was to make that kind of exploration possible.
I haven't done any GraphQL stuff myself, but the "I can get whatever data I need" aspect feels like a huge potential headache for backend devs. Won't be a problem at prototype scale, but once you have a significant client count, how do you deal with unpredictable data access patterns that can't be optimized ahead of time?
Is there any equivalent for the latter with swagger? Yes, you can do everything in there with custom redux middlewares or sagas/observables, but it’s more glue code you have to write and maintain.
Is this something you couldn't achieve with the idiomatic rules on HTTP request types, and serialization of HTTP requests parameters? It really depends on where you're trying to do the caching and what kind of caching you're trying to do.
A lot of work has gone into HTTP headers, method semantics, and other details in order to facilitate caching. The "backend for frontend" pattern has been very popular as of late in order to try and shift the plumbing load, exposing endpoints that are catered to certain clients to boost efficiency -- would that be good enough for a corollary in OpenAPI land?
Without it, you end up having to create tailored endpoints for each use-case vs. for example a single “user” endpoint/vertex that supports the complete buffet of fields for all use-cases in one convenient place.
[0]: https://postgrest.org/en/v7.0.0/api.html#vertical-filtering-...
So usually you have backoffice devs scrambling to retrieve data in weird ways from existing APIs. And things like "does this inventory item come from a provider with an active contract, and who else is on that contract" become ... weird and unwieldy. This is where GraphQL with it's ad-hoc queries definitely helps. And since the volume of queries in back office is low, you don't worry too much about the performance of some of the queries.
I wouldn't use it for anything else though.
Anecdata:
Just last month it took me about half an hour to implement an "omni search" functionality in GraphQL that takes string as input, coalesces results from three different backend APIs and return back a nice view to fuzzy-ish search for three different entities in our backend. Frontend couldn't be happier.
This is less ideal than GraphQL in this case, since GraphQL has done this hard work and standardized it/created the conforming reference clients/servers already.
Isn't this exactly what GraphQL does? I may be missing something
You can create your own global json schema and build a ratchet version of GraphQL using proven orms in about 20 minutes using an unpersisted “filtering” field.
If you want automatic access to your data, bite the bullet and do something cool like implement Apache Dremio.
I was told this was the new hotness, and I can’t be lied to. Plus, many job descriptions were asking for Graphql about 2 years ago, so what gives? Is life not fair? I was told it was fair this time around.
I've been using it for more than a year with TypeScript types generation and couldn't be happier. All of my interactions with the server are properly typed and haven't had a single bug related to server/client missmatches.
I find it amusing that some of the comments are basically "Why use graphql when I can combine these 5 other technologies to do the same thing?"
It's the fanboys, and more the people selling tooling. There's a guy who (was?) over at graphqleditor.com that was posting a whole bunch of articles, at least one of one said it was a mistake not to convert all your shit to graphql immediately.
I mean, kind of?
My problem with GraphQL isn't necessarily GraphQL itself, but with the sprawl of things creeping further and further into client-side code.
As companies are finding GraphQL to "unlock greater developer productivity", it's becoming more and more expected to have this in our toolkit as "front-end people".
There's been a few blog posts recently that I relate to. I feel like I'm at a point in my career where I could "knuckle-down and get good" but I don't actually want to get good at more and more endless technologies. I'd rather be able to define my niche specialization and thrive within that space.
[1]: https://bradfrost.com/blog/post/front-of-the-front-end-and-b...
[2]: https://www.trysmudford.com/blog/i-think-im-a-design-enginee...
[3]: https://notes.baldurbjarnason.com/2021/02/20/the-layers-of.h...
Maybe you are referring to generating the model from the queries. The problem we had with that approach was that it's hard to reuse the generated structures in your application model. You can read more about the idea here:
https://github.com/maticzav/swift-graphql#what-are-the-pitfa...
that module: https://github.com/captain-refactor/graphql-compose-typescri...
- Relay-swift: A port of Relay to Swift - https://relay-tools.github.io/Relay.swift/docs/
- Graphaello: Inspired by Relay, but deviates a bit more from the patterns than Relay-swift - https://github.com/nerdsupremacist/Graphaello
The main difference between these clients and SwiftGraphQL is that SwiftGraphQL tries to abstract away GraphQL in favour of Swift language feature.
`relay-swift`, for example, relies on query strings which doesn't bring type-safety to your code;
`graphaello` is indeed very similar and I've tried using it before creating SwiftGraphQL. One of the goals of SwiftGraphQL was to let you easily separate the model from your queries. That's why we let you do complex logic in the selection itself. Otherwise, you'd have to first create a utility struct and then translate it into a model-type. I think that's the main difference between graphaello and SwiftGraphQL
Thanks again for sharing the links!
Haven't checked out SwiftGraphQL much, but Relay.swift is definitely the best swift based GQL I have used to date.
Learning new things and doing things better than they were done a year before is an essential part of being a professional software engineer, it's what makes this field hard and that's why we're paid so much. It's what makes us able to tackle so much added complexity of the world, new use cases, new platforms and paradigm shifts over the years. To me, it's also what makes it so challenging and rewarding.
The problem usually isn’t the library itself and that’s true of GraohQL. It’s the cargo culting of GraphQL & other new tech that might be unnecessary or overengineering for your specific use case.
By moving all that complexity to the backend. It's not luddism to call this out.
> Learning new things and doing things better than they were done a year before is an essential part of being a professional software engineer
Yeah, no. It's not "better than they were done a year ago". It's just dumping all the complexity in somebody else's lap, reimplements a bunch of things from scratch, poorly, and calls it progress.
I was at Facebook when GraphQL was invented, maintaining a backend storage service where a core assumption was that storage should be reorganized based on access patterns and that predicates should be pushed down to storage where they can be executed more efficiently.
GraphQL was hard to push predicates down, because you don't know which of the edges were written in PHP.
My response was fquery[1], which is like what's being discussed here but with python as the source language instead of swift and amenable to preserving the largest possible query structure for backend optimizers, including SQL optimizers.
It has some early demos converting a GraphQL/fquery into SQL where possible. It should be possible to add enough metadata to fquery to identify if an edge is non-trivial (calls into another microservice) or trivial (can be optimized to a storage backend or SQL).
From what I understand, this project tries to solve one clear pain point that isn't solved elsewhere: Swift developers can use GraphQL in Swift without writing GraphQL directly. Being able to create idiomatic operations through Swift is nifty to see.
Somewhat interestingly, it seems like to solve other problems, @maticzav is independently following similar paths that the original developers of GraphQL in Objective-C followed. Some of the highlights:
- Using code generation to make access patterns more type safe.
- Creating a centralized set of Type Models, that map 1:1 with the schema the app accesses.
- Mostly separating the GraphQL operations (in this case, as written in Swift) from the models a component interacts with: the components just see that they have a Schema-based Type Model.
I'd personally think about using the query-creation API to produce query (and fragment, once those are supported) specific models for consumption. The problems of both over and under fetching may expose themselves when more than a couple interactions are operating with different views over the same types. I've talked about why Facebook no longer recommends Type Models before: https://www.youtube.com/watch?v=Vo8nqjiKI3A
It's really encouraging to see the recent explosion of ideas for "how to do GraphQL": I'm not sure what sort of synthesis these myriad projects will create for the broader community.
GraphQL is a genuinely objectively useful technology in places that have a frontend-backend split (including non-web frontends). It doesn’t require building specific APIs for specific pages for speed concerns. It allows backends to add fields non-destructively (RESTful JSON does work at the cost of a bigger network payload).
GraphQL provides the right tools for the right problem, and has created an ecosystem around it. (How should I reliably compute a SQL query cost? How should I control data access policy in SQL? How should I return the SQL result in a easily-parsable format?)