40 comments

[ 5.5 ms ] story [ 109 ms ] thread
So instead of having a schema a schema is created from the React usage? I am not entirely sure what this does.
If I understand correctly, it's more like the query (not the schema) is generated from the expected result.

So your application does not need to "talk in GraphQL".

The main advantage is that the query is resolved at runtime. It only queries for exactly what your component needs. No over-querying. No sharing queries across components, which is a huge mistake people make.
> query is resolved at runtime

Does it mean that we get errors at runtime and not build time, too?

Have you found a way to get build errors with GraphQL queries? I only ever get them during runtime when the request is made, I assume because that's the point at which the back-end and front-end disagree on what you're requesting.
If you generate types from your GraphQL queries, they are validated against the schema at that time (so you'd get schema errors then, of course there can still be network errors, server errors or user/semantic errors at runtime)
Using Typescript,GraphQL-codegen, and Hasura, I can build correct-by-construction React apps. GraphQL-codegen & Hasura introspect the DB to type the query result, and typescript confirms my JS is not accessing any of those fields illegally.

It’s pretty killer to open GraphiQL, write a new query, drop it into a graphql file in my repo, rerun the codegen, and be able to have type safe operations on the queried data.

Also makes me completely unsure why anyone would want what the OP is but that’s ok

This solution seems to work like this:

  - Generate type safe client based on your schema.
  - Access GraphQL properties as if they were just JS object properties.
So in this solution the codegen step comes before writing a query.

You get the same advantages pretty much, and you have less likelihood of querying for unused data.

This part is confusing to me.

I prefer this will validate our component for correct schema usage at build time.

It fully typed, so you get errors at build time.
I think the part that's throwing people off (including me) is if I'm looking at that main example, how do I get type completion on this line:

    Hello {query.me.name}!
query dot me dot name should all have type completion. And in most TS "frameworks" you have some way to type hint this all out. us TS folk are not seeing the type hints in the example. Maybe it works in VSCode like that, but totally confused how it does.

Believe me, I'm super interested in this project and it would save a ton of time if it's doing what some of us are hoping :)

It does autocomplete with types on that line, and yes, it’s really that cool.

Disclosure: I funded the development of version 2, and have written a large app using it. I am very happy with it, and believe it’s the best data fetching solution around.

The client is generated from the schema, so “query.me.name” is fully typed.
I'd say you're broadly right, the key idea is that your client code has a facade over the fact that you're using GraphQL, so it looks cleaner -- it's "just JavaScript". Whether this is an advantage or not is largely down to preference.

The "Why" section in the docs is a pretty good summary:

https://gqless.com/intro#why

"I wanted a GraphQL client that lets you forget you're using GraphQL, whilst tackling some issues shared across all existing GraphQL clients"

The fact that it's resolved at runtime isn't necessarily an advantage, it locks you out of some performance and security patterns. I believe GQLess is investigating support for static extraction of queries at build time.

It's an interesting API, I'm not entirely sold on the idea of disguising the fact that you're using GraphQL though.

I’m very interested in this! Thanks for posting!
This looks like it has a shot at being a better developer experience than Relay while still encouraging the use of nested queries (unlike clients like Apollo and urql). Nice.
How do Apollo and urql discourage using nested queries?
You can’t pass along the result (not exactly but that’s how it looks) of a query to a component and query more things on it.

An example might be something like, you have a page showing a user, and you have a ProfilePicture component with certain data requirements (e.g. get picture at 100X100). It would be nice if you could pass down the user and have the ProfilePicture component query the data it needs, with this being merged into the parent request.

In Relay this kind of thing can be done with useFragment.

In this solution it seems you could just pass the user.

Apollo/urql don’t support this pattern, everything must be in one top level query, or multiple queries (bad perf).

I really like these event-loop/proxy-object tricks.

It always feels like magic.

How do you deal with error handling? I don't see anything in the docs about error handling.

How do you deal with union types? The docs seem completely scant on that.

Otherwise very interesting approach.

Handling loading states also seems to be a little bit on the anemic side, but I didn't investigate too deeply.
Yes, it doesn't really distinguish between loading vs null values in any of the examples.

My guess is its going to be something like `undefined` means loading and `null` means null.

There’s a different hook you can use that returns an error state.
The primary example code relies on Suspense, meaning that loading behavior will be handled by a parent/ancestor <Suspense /> component and not in the component itself. I think they default to showing this pattern since it is the “future” of React. There is an alternate version of the API where you can get a loading boolean for imperative control.
The useQuery hook’s opts have an onError property that accepts a callback.

I also don’t see anything about union types. That definitely seems like the hardest thing to reverse construct based on usage, since the underlying proxy wouldn’t know what you’re doing with __typename.

the greatest value on it for me is the scalability of relay, without the boilerplate.

Colocating data requirements next to where it is using it without writing it two times is awesome!

This is great except for one thing. I like types generates during static time, not runtime. Anyway we could verify types at that time?
It generates types via CLI statically up front, all queries are then fully typed, only the final request is generated at runtime which means you never fetch anything except what you need.
(comment deleted)
One of our requirements is to disable schema downloading in production in apollo server. (We can have it enabled in dev/local). Can this work if that is disabled? (Yes we know this is silly but our auditor caught it)
From my understanding this should work as the schema is introspected ahead of time. This gives all the type information needed for the `useQuery` hook to work properly. Just the queries themselves are generated at runtime; there shouldn't be any need to introspect the schema then.

Where this might have an issue is with persisted queries. But then you'd need some sort of client/server build tooling to keep them in sync.

Wow, that's cool!
Cool for prototyping, but likely difficult to maintain over a longer period of time.

Just like with an ORM on top of a DB, if one needs to to performance optimization or support parallel app versions and ensure backwards-compatibility of queries, the actual queries are useful if not essential to know and manage.

There’s no reason all of this isn’t supported as well if not better in GQless than any other client.

You can hoist the query logic if you need to ensure it stays static for some reason.

I’ve built a very large app with this and am perfectly happy with the model, it has many upsides vs having to manually link strings into views, like never overfetching and having fully typed queries without any plugins.

It reminds me a little bit of QBE (Query by example), but instead of saying what data you want, you say what schema you want.
This is amazing concept. It literally transforms remote api into local resource with the aid of graphql.
So it's a generation of graphql on-fly, right?

I conclude that it will be incompatible with hasura Allow-list (or lead to explosion of Allow-list for all possible combinations which is impossible in practice)

So it could look nice but makes unusable in case you rely on Allow-list for security.

It could be good for prototyping.