[HN Gopher] Solving the double (quintuple) declaration Problem i...
___________________________________________________________________
Solving the double (quintuple) declaration Problem in GraphQL
Applications
Author : jensneuse
Score : 60 points
Date : 2021-08-14 13:32 UTC (9 hours ago)
(HTM) web link (wundergraph.com)
(TXT) w3m dump (wundergraph.com)
| krthr wrote:
| And that's why I love so much NestJS and its code first approach.
|
| https://docs.nestjs.com/graphql/quick-start#code-first
| pharindoko wrote:
| Same opinion. Graphql With nest.js (code first) and prisma is
| nice!
| newhouseb wrote:
| While I'm very excited about the emergence of type checking in
| "modern" web development the triad of GraphQL / Typescript Types
| / JSON-Schema is a real cluster and I hope we engineer ourselves
| beyond a solution that requires endlessly translating between
| them in the next few years.
|
| After having iterated through a bunch of permutations of Rust
| frameworks, Typescript frameworks and ORMs here is a stack that
| I'm particularly excited about (that mirrors this article a fair
| bit):
|
| - Design a Postgres database in whatever means you prefer (I
| prefer SQL, since it's easy to design constraints in using the
| full power of the DB engine).
|
| - Use Postgraphile to auto-derive a CRUD GraphQL Schema for your
| database (this is similar to Hasura, but encourages using
| Postgres row-level security if you need fine grained access
| control).
|
| - (Also) Use pgTyped for extensions to Postgraphile's schema when
| you want to implement mutations outside of DB functions.
|
| - Use Apollo GraphQL Codegen in your front-end to auto-generate
| types for your GraphQL Queries.
|
| The net effect of this is you get full type safety and your DB
| Schema is your singular source of truth (Note that I'm not auto-
| generating forms from JSON-Schema like this post, but this isn't
| necessary for our particular use case).
| breytex wrote:
| I like to start with code. If you use typeorm and type-graphql
| you can write a Type once and generate the database as well as
| the resolvers out of it. You can then even import the same type
| in frontend (as part of a mono repo). This stack does not need
| any generation routine for types. You just have to generate a
| migration if the postgres target schema changes, but that's
| normal and I don't see a way around this for the future.
| newhouseb wrote:
| SQL is code too :), but I follow your point.
|
| Typeorm looks nice, but I need polymorphism for some of our
| "business" logic and have pretty specific requirements about
| how I want correctness enforced that typeorm doesn't appear
| to support (but raw SQL does).
| pkilgore wrote:
| Typeorm does not scale to large datasets. Real nice for
| MVPs. Just a warning. Hydration times explode for lists and
| joins that postgres barely registers on the query side, and
| hydration is blocking.
| AmericanChopper wrote:
| The way I see it, we got here by abandoning OOP for web
| development. There's a lot of reasons that MEAN, NoSQL, no-
| schema... is so appealing, because OOP is so tedious. But we've
| just been slowly recreating OOP in a piecemeal fashion, with
| ORMs, TypeScript, JSON Schema, GraphQL... because the new
| approach didn't actually provide an alternative solution to the
| problems that OOP attempts to address, it was just based on the
| premise that you could ignore them.
|
| I don't think we're that far away from coming full circle back
| to OOP, even if the "framework" ends up having a different
| name.
| sbr464 wrote:
| side note, the *.wundergraph.dev ssl certificate seems to be
| expired. I was testing out your service.
| kogir wrote:
| It's been interesting to see the ecosystem around GraphQL
| experience the same problems and finally reach parity with oData.
| say_it_as_it_is wrote:
| GraphQL simplified the frontend by making the backend 100 times
| more complicated. So many fewer requests, though! Problem solved.
| Big names made and books sold.
| ldiracdelta wrote:
| GraphQL is SOAP 2.0
| occz wrote:
| > GraphQL simplified the frontend by making the backend 100
| times more complicated. So many fewer requests, though! Problem
| solved. Big names made and books sold.
|
| As opposed to making the backend simple, by making the frontend
| complicated?
| salmonellaeater wrote:
| > Most Web Applications are just forms that talk to a database.
|
| And this is why the author is frustrated - GraphQL is designed
| for larger, more complicated systems. It's overkill for a basic
| web app like this. In fact, coupling the different declarations
| together like this defeats the purpose of GraphQL.
|
| One of GraphQL's purposes is to let the different layers and
| parts of a system evolve independently. For instance, the
| database schema can be changed without changing the GraphQL
| schema. A resolver can be changed to pull data from a new service
| instead of the old monolith. It also affords more custom
| implementations, where the db schema is not exposed directly to
| the client or the data is stored across multiple technologies.
|
| There's a larger point here, which is that it's okay to have
| multiple definitions for things that seem similar. It's okay to
| have one version of a User for the front-end, a different one in
| the GraphQL layer, and several different ones with varying
| details in different backend services. The key is that these
| Users are different things - for each context, there is a useful
| definition of User that is different from other contexts [1].
| Some systems need security details, some need contact info, some
| only care about the user id, etc.
|
| It's really important in large engineering orgs to allow these
| different definitions to vary independently so that teams can
| make incremental changes without updating every single other
| system. Hence tools like GraphQL.
|
| [1] https://martinfowler.com/bliki/BoundedContext.html
| SkyPuncher wrote:
| I've dealt with the same issues in nearly every non-GraphQL
| project I've worked on. If you use types, you need to define
| those somewhere that each system can understand.
|
| In most REST/JSON/whatever, I've worked with this ends up as
| serializers and deserializers on both ends of the
| client/server. At it's most basic, you're doing no more work in
| GraphQL than a "legacy" client-server.
|
| -----
|
| I've dabbled with GraphQL in personal projects and I think it
| far exceeds anything I've worked with in a REST setup. The
| major issue that I see with GraphQL is it lets you do stupid
| things incredibly easily. However, this is also it's benefit -
| a client can grab only and exactly what it needs.
|
| The major problems I see with GraphQL is developers using
| fundamentally poor relational data models.
| hn_throwaway_99 wrote:
| Hallelujah. I see all these posts about auto generating GraphQL
| from your DB schema and I always think "But you're literally
| getting rid one one of the _best_ parts of GraphQL by tightly
| coupling your DB to your API ". I also feel that this approach
| is putting people in for a world of hurt: it's easy to get up
| and running quickly, but it will kill productivity later on
| when it makes refactoring much more difficult.
|
| FWIW I define my API using the GraphQL schema language and then
| generate everything after that (Typescript definitions for
| server and client) using graphql-codegen and it works great.
| BulgarianIdiot wrote:
| I find these DB-to-REST, DB-to-GraphQL and so on variety
| frameworks to be terrible.
|
| That said I've come to accept some people really just need to
| expose their database out there. And so having these tools
| help them.
|
| It would be nice if those tools did provide for a smooth path
| from 1:1 mapping to full customization. Unfortunately, most
| don't.
| jensneuse wrote:
| I completely agree with you. I've also wrote this post
| explaining why tightly coupling the DB to the frontend is
| such a big problem:
| https://wundergraph.com/blog/tight_coupling_as_a_service
|
| That said, I've added support for direct DB access to
| WunderGraph based on demand from the community. Although it
| has drawbacks, users want to expose their DB as an API. I
| don't want to ignore them.
|
| That said, I also want to address your second point.
| WunderGraph offers two abstraction points. For one, you can
| always swap out a backend and join multiple APIs together.
| This means, you could replace an API generated from a DB at
| any time, even partially, without breaking the client
| contract. Then, there's also a second abstraction point
| which has to do with the fact that WunderGraph persists
| GraphQL Operations by default. It allows you to swap out
| the entire schema if the Operations stay the same. Both
| options might not always work but offer good migration
| paths from a DB generated API away. If you have the
| resources, it always makes sense to "design" your API.
| Generating an API is not wrong in many ways. However, for
| prototyping and simple projects, it might be the right tool
| to get the job done in a cost sensitive way.
|
| To sum it up, generated APIs are not ideal but it really
| depends on the case. In the end, the customer decides.
| maxcan wrote:
| I agreed with this until I found Hasura.
|
| For me its just the right level of abstraction and saves me
| a boat load of time writing CRUD queries. Now, 90% of my
| CRUD ops are autogenerated, with great, granular
| authorization, and for more complex logic I can easily
| "join" a lambda function.
| andrewingram wrote:
| Yeah. The unacknowledged assumption being made is that
| n-definition (much less of a mournful) is inherently a problem.
| The reality is that each layer of the stack, the definition
| means a different thing even if the fields are the same _right
| now_. I'm an advocate of utilities to flatten the layers as an
| effort-saving measure, but as something to opt into, rather
| than out of. The layers must always be conceptually there.
|
| The reason I don't like things like Hasura et al is because
| they flatten by default (your GraphQL server essentially
| becomes just an ORM for your database), and you're made to do
| extra work if you don't want that.
| bitL wrote:
| GraphQL is extremely badly designed - try to do nested
| pagination, that's basically unsolvable in a single server
| roundtrip. The hack of keeping an n-dimensional array of
| pointers to cover each level of pagination falls apart with any
| concurrent changes. And nested pagination pops up often when
| doing advanced UI on mobile on web.
| danjac wrote:
| Efficiency often takes a back seat to Conway's Law.
| SkyPuncher wrote:
| REST has the same issue.
| infogulch wrote:
| The "nested pagination with a single server roundtrip"
| requirement seems a little odd, I'm not sure why that's
| necessary or desired. I can imagine an example, a view with a
| list of users, then expanding one user inline to view a list
| of their comments. But I don't see why I'd even want this
| whole nested view to be returned in one round trip, it seems
| quite wasteful to return the first page of comments for every
| user before it's expanded. Do you have a better example?
| andrewingram wrote:
| The need for hitting nested "paged" data structures on
| first render isn't _that_ contrived, but I've never found
| myself needing to juggle the cursors, since you're usually
| only hitting the first page for the nested ones. Loading
| subsequent pages would be a standalone query, rather than
| running the root query again with different cursors.
|
| (the example I came up with for this article a few years
| has a fairly realistic example of nested pages:
| https://andrewingram.net/posts/optimising-your-graphql-
| reque...)
| adsharma wrote:
| What does an ideal solution look like in your view? Do
| snapshot queries help, so you're protected from concurrent
| changes?
|
| With fquery (https://adsharma.github.io/fquery/), the problem
| boils down to constructing a new query with additional where
| clauses.
|
| Because of the property that the shape of the query and the
| shape of the output are identical, nested queries fit
| naturally into the system, as opposed to GraphQL <-> SQL
| where one is nested, but the other is flat, requiring a
| mapping.
| adsharma wrote:
| Similar benefits without codegen (based on decorator magic) for a
| python based stack:
|
| https://github.com/adsharma/fquery * Use
| dataclasses for both database schema and the user facing
| operations * use decorators to generate django models
| * use decorators to generate graphql operations (uses strawberry-
| graphql) * Supports a method chaining query language, that
| can map to SQL
|
| No code generation. Declarative mapping between database schema
| and web API schema possible, but not implemented.
| csnweb wrote:
| You can get a lot of types on the server without the need to
| write any types manually or having to generate them by using
| https://github.com/sikanhe/gqtx
| djstein wrote:
| Current stack for creating easy to use GraphQL APIs + Frontend
| Integration:
|
| - Django 3 backed with a Postgres database (standard stuff) [0]
|
| - Add Graphene to enable Relay [1]
|
| - Setup a fresh TypeScript based NextJS project [2]
|
| - Add graphql-code-generator with a few plugins, mainly the
| graphql-request one [3]
|
| - THE MAGIC: Generate your sdk by pointing graphql-codegen at
| your Django GraphQL schema!
|
| - Add react-query, use the newly generated graphql-request client
| [4]
|
| From here you've enabled the best of frontend technologies
| (NextJS, react-query) with a fully typed SDK generated by your
| familiar Python/Django bindings.
|
| You can then move forward to add GraphQL based WebSockets via
| django-channels [5] + django-channels-graphql-ws [6] that update
| your existing react-query caches. Combine this with background
| Celery workers that make push to django-channels via Redis and
| you've got a a UI that auto updates based on background tasks as
| well.
|
| [0] https://www.djangoproject.com
|
| [1] https://docs.graphene-python.org/projects/django/en/latest/
|
| [2] http://nextjs.org/
|
| [3] https://www.graphql-code-generator.com
|
| [4] https://react-query.tanstack.com
|
| [5] https://github.com/django/channels
|
| [6] https://github.com/datadvance/DjangoChannelsGraphqlWs
|
| [Edit] Line formatting
| yeneek wrote:
| I already solved this problem for me. I use graphql code
| generator (https://www.graphql-code-generator.com/) to generate
| graphql resolvers on BE and angular services on FE. I use MongoDB
| and database models are written in typescript. I want to manage
| db models separately from the API. With this approach, I'm type-
| safe from end to end.
|
| Edit: I forgot mobile. Apollo Android client generates schemas
| from schema and queries. I've never written iOS app, so I can't
| recommend anything yet.
| latchkey wrote:
| Same. I paired that with https://typegraphql.com/ (which builds
| the graphql schema dynamically from the code using
| buildSchemaSync) and MikroORM (which you can overlap your
| entities with to remove duplication there) and it was a totally
| generated solution with full type safety.
|
| A bit crazy complex to setup so many moving pieces, but once it
| is, it works great.
| recursivedoubts wrote:
| Money quote:
|
| _> "Most Web Applications are just forms that talk to a
| database."_
|
| Yeah... so why are we doing all this crazy stuff again?
|
| Maybe we could just use hypermedia for this relatively simple
| problem? Sure, there are usability issues at times, but there are
| solutions to that [1][2][3].
|
| I read stuff like this I go zoolander[4].
|
| ---
|
| [1] - https://unpoly.com/
|
| [2] - https://hotwired.dev/
|
| [3] - https://htmx.org (my solution)
|
| [4] - https://www.youtube.com/watch?v=D4f2j-Xmgc8
| villasv wrote:
| I don't care about hypermedia in particular, but it is
| intriguing how one can say that "apps are just forms" and
| justify all that tooling. Might as well just go with CouchDB.
| jensneuse wrote:
| Maybe you're right, I don't know. What do you think why are
| technologies like React, GraphQL and NextJS growing their
| communities while hypermedia seems to be a niche thing? These
| frameworks are bloated and add a lot of potentially unnecessary
| JavaScript to we apps. At the same time developers seem to like
| the developer experience and trade this with a better
| technology like e.g. hypermedia APIs. Is it the usability? Lack
| of tools? Your tool seems to be thought out well. What's
| preventing it from becoming mainstream?
| recursivedoubts wrote:
| it's a complex story, but here are some reasons:
|
| - HTML by itself had usability issues that were addressable
| only with javascript
|
| - REST/HATEOAS got lost in the morass of JSON APIs, which
| isn't a natural hypermedia, and none of the thought leaders
| came out strongly for hypermedia or even really pointed out
| what was going wrong
|
| - Developers tend to prefer RPC over Hypermedia because it is
| closer to their natural way of thinking
|
| - FAANG-chasing
|
| - Developers generally don't like saying "this is too
| complicated" because it sounds close to "I am not smart
| enough."
|
| Hypermedia is too good an idea to die completely, and I think
| we'll see a swing back. But in the meantime, a huge amount of
| time and energy has been and is going to continue to be
| wasted on rube goldberg contraptions to make "forms talk to
| databases".
| dionian wrote:
| Complication leads to higher cost of ownership, including
| training the junior devs who inevitably wind up maintaining
| the thing
| halostatue wrote:
| I've implemented hypermedia APIs before. It was a lot of
| work for exactly zero benefit because none of the client
| developers wanted what they were given in those APIs, and
| we were forced to develop _additional_ APIs that gave the
| client developers something they felt comfortable working
| with.
| recursivedoubts wrote:
| Were the hypermedia APIs returning HTML or some other
| real hypermedia? I assume not.
|
| Were they consumed by a hypermedia client? I assume not,
| since there is nothing to complain about except
| compliance with the hypermedia specification in question.
|
| Hypermedia APIs never made sense once we flipped to JSON
| apis, it was all cargo cult. I've written some essays on
| this:
|
| https://intercoolerjs.org/2016/01/18/rescuing-rest.html
|
| https://intercoolerjs.org/2016/05/08/hatoeas-is-for-
| humans.h...
| halostatue wrote:
| Your premise about "HTML or some other real hypermedia"
| is bunkum. There is nothing about REST or HATEOAS that
| depends on HTML at all. The whole _point_ of REST was to
| build on the semantics of HTTP without regard for the
| transport protocol or encoding format (and HTML, XML, and
| JSON are all encoding formats).
|
| What makes a RESTful API a HATEOAS API is that it
| provides you actionable hypermedia definitions. There is
| fundamentally no difference between:
| <link rel="self"
| href="https://my.server.example/api/customer/id/1">
|
| and { "link": { "self":
| "https://my.server.example/api/customer/id/1" } }
|
| When you include child responses, you include reachable
| endpoints for them, too. {
| "link": { /* top-level item refs */ }, "posts":
| { "link": { /* post collection level refs */
| }, "items": [ { "link": { /*
| post level refs */ }, /* the rest of the object */ },
| /* more objects */ ] } }
|
| And yes, in fact, that is _exactly_ what the API that I
| developed did--so every application client that worked
| against the API was required to work based on that (and
| it made making Postman collections really interesting,
| because you could not just "guess" at the URL required,
| you had to look it up through the chain). It worked.
|
| While it felt nicer from a "purity" standpoint, it made
| the server slower, less agile to client requirements. We
| eventually abandoned it as a mistake in the next
| generation platform that we built.
|
| There are a lot of good things about REST, and I like the
| idea of having discoverable APIs as expected in HATEOAS.
| But the number of people who _get_ it on the client
| development side is vanishingly small, and there's always
| unfortunate drift where you get people templating API
| URLs even though they are given the exact API URL
| required to perform an operation.
|
| These days, I'd much rather write up a good GraphQL API
| with Absinthe and just get the job done rather.
___________________________________________________________________
(page generated 2021-08-14 23:01 UTC)