GraphJin – GraphQL to SQL Compiler
graphjin.com
graphjin.com
> It's always the same thing, figure out what the UI needs then build an endpoint for it. Most API code involves struggling with an ORM to query a database and mangle the data into a shape that the UI expects to see.
Most business API's have business specific per user read/write access roles.
I am starting to think that a better solution to GraphQL and other DSL's is to:
- A. Make the iteration speed of adding a new plain HTTP endpoint very fast.
- B. Use a typed language, and some kind of macro to extract the types into an Open API spec.
This way everything is just a regular function in your general language:
- http_handler(request) -> response
- user_has_access(user, resource) -> bool)
etc.
Regular functions have no external dependencies, are easy to understand, edit and stand the test of time better than DSL's.
If GQL does what you need out of the box, it seems a win. But I would assume some endpoints need to fall back on the above approach anyway giving you a mixture of GQL-generated and hand-written handlers.
It's pretty close to what's being described here, and removes the boilerplate in API orchestration.
- runtime assertions [0] - to map unknown values at i/o boundary into statically typed code (rpc input parameters, sql results etc)
- template based sql combinators to sanitize sql/generate sql [1]
- jsonrpc over websockets - for bidirectional comms between f/e and b/e
It works very well for us for hundreds of rpcs - it's easy to manage things like permissioning/narrowing visibility and other aspects you need in business/enterprise-like setting ie. performance monitoring is very easy/straight forward.
Here's an example of a relatively complex query that you'd need when building something like a blog. GraphJin will compile this nested query into a single efficient SQL statement.
https://gist.github.com/dosco/f604c47c1d643fb62072f62c4f6f70...
I don't know if this was because of PostgREST or because all the logic was encoded in grotesque looking SQL that nobody understood (and changing some things requires dropping and readding things).
It is an extremely cool project, don't get me wrong, but it's one of those things that's "I know this already and I'll use it for a weekend/poc project to not deal with writing a backend" and not for something important. And again, I don't know if this was just poor usage or something that manifests itself in every project that relies on it. Also the DSL is very much it's own thing, so migrating away from it is painful, especially if you inherited the project and don't want to read the document. But again, sample size is 1, so it's anecdotal.
1- Don't expose your real database but expose a database consisting of view on your real database so you can refactor.
2- Wrap your bussines logic in stored proc to keep your sql readable
3- Use jwt for authentication
That said, I would not recommend it for a large scale application. I consider it, maybe wrongly, as the MSAccess of the backend.
Also, don't overlook the whiz-bang of the GraphQL introspection tooling -- it's super handy for just kicking the tires on something in ways that "dump the SQL schema to the browser" likely wouldn't do
The related pg_graphql posted a while back (https://news.ycombinator.com/item?id=29430720) actually mentions GraphJin positively, and talks about a bunch of competing implementations, although it's not one-to-one with GraphJin because it seems to support mysql whereas pg_graphql is of course a PG extension
Manipulating rows in SQL is easy but doing nested joins and trying to represent one-to-many relationships correctly in the response is non trivial. I would say you have to be good at SQL to leverage the DB to make structured data, returning tables and rows is entry level.
In GraphQL on the other hand, it’s seamless / entry-level to query for nested data and represent it really semantically, which is very appealing when doing presentational work (read: UIs, I suppose).
Therefore, the purpose of constructs like this is to allow those working on the presentational layer to be able to construct queries for semantic, structured data themselves with no requirement for SQL / backend expertise. This is a pretty meaningful improvement to unblocking development for both sides of the stack, in my opinion, and is why I apply Hasura everywhere I can.
We use a WebSocket connection to keep all queries fast. Auth is solved with row level security.
It's even in the name: The backend layer is just a "thin" layer over the database. Most of the business logic is then implemented in a "rich" client/frontend.
Unfortunately, there are two closed out issues, seemingly with no resolution, that seem to indicate cursor-based pagination doesn't work (with Relay) and perhaps correctly (at all).
Apparently, there's strong demand in the market for quicker ways to create GraphQL APIs (see Graphile and now Graphjin), so I'm excited to see more innovation in this space!
Hasura solution would be as a separate service and separate authentication most likely
One of the strengths of GraphQL is you can transform data from multiple sources into a common model the UI can use. If your whole backend is a single SQL database schema GraphQL doesn't seem to add a whole lot of value there. I'd rather have a code generator that builds a simple Go microservice and use protobuf JS in my front end.
You might, but there's something to be said for consistency within an org. GraphQL is powerful and there's many a use case for querying data via a simple GraphQL API in the frontend. You could have just stated you have a bias against use of GraphQL without the need for multiple data sources, and left out the rest of the FUD.
Projects like Postgraphile, Hasura, AppSync, and Supabase's pg-graphql aren't popular because people are lazy or are out there in droves implementing APIs laden with antipatterns. They're popular because they're useful and powerful. And for many companies that funnel all of their data into single stores like Postgres or other relational databases, these kinds of tools are invaluable.
Here's an example https://pkg.go.dev/github.com/dosco/graphjin/core#example-pa...
And here's an example of a Redis custom resolver to join with data from Redis. https://pkg.go.dev/github.com/dosco/graphjin/core#Resolver
But really, with proper authentication in place and preferably built-in to a GraphQL-enabled datastore, a GraphQL server can more secure than your average self-implemented HTTP API.
The latter can easily leak through e.g. haphazard app-level joins. Meanwhile, the GraphQL server can secure things at object level, much like if people actually integrated their frontend authentication all the way to their (No)SQL server.
It does spare you from having to write SQL, but instead you have to write the GQL documents. It also spares me from having to come up with pagination SQL as PostGraphile handles that in pagination cases, along with text searches.
PostGraphile also lets you selectively expose which tables / fields should be available to a GQL client, but if PostGraphile itself can be attacked, then yes, an FE client could get to the underlying DB.
You're pretty much having the middle tier act as the business logic layer as the FE client(s) doesn't have to worry about calling a sequence of operations to do a task, and instead just have to call a single operation on the middle tier instead.
We are soon going to be evaluating a similar idea at work, so I'm just curious how it works. If it's lengthy you don't have to go into too much detail if it's a bother to explain!
It's a really nice pattern particularly for multi-client applications consuming the same resources. Instead of trying to make a single REST api work for desktop, mobile, and web, you can have a BFF for each which all share the same GQL definitions and data access patterns, but can have platform specific routing/handling/auth logic.
Yes to your question overall.
As another reply states, it's exactly a backend for a frontend.
With frameworks they're in code, with graphql adapters, they're declaratively managed (idk if this project does it, only talking conceptually, but Hasura does it). This is kinda better when you think about it because it's easier to simulate/validated the ACL. And on top of that, it's possible to build no-code interfaces on top of it to manage those rules by anyone in an organisation. Add hooks and you can add complex business along side these access rules, without encumbering the business logic with ACLs.
I realise this is a bit idealistic, but I don't think it's an unachievable goal with the current tech we have out there.
With all that said... Even though I sound like a proponent of this now, I'd still be a bit nervous and on the fence about having this in production.
Also in production the query is compiled into a db prepared statement the first time and then on queries after validation go directly to the prepared statement. This is no different than a hand written HTTP endpoint + ORM.
Addition if using the standalone service is not for you then you can use GraphJin as sort of an ORM within your own http handlers.
The question around why use it is complex. Some reasons even nested queries and mutations are compiled to a single SQL query. Knowledge of indexes is used to write a fast query. Support for things like joining again Postgres Array columns, JSON columns, fast cursor pagination, Graph queries using recursive CTEs, full-text search and a lot more out of the box and will work on Postgres, Mysql, Cockroach, Yugabyte and I'm working on more.
Thats "straight to a db" by my definition. But I agree that saved/allowlisted queries are a key problem that needs to be solved in any prod environment, regardless of tooling you are using.
I'm not saying there isn't a use case for it, but I think there is a better solution 100% of the time. I am clearly not the target audience for this though, so my opinion is kind of irrelevant.
In my world graphql is a tool for aggregating microservices in to a supergraph, when you are at a point that a monolithic DB (or a couple of them) with data from lots of different domains no longer exists. In that sort of environment, which is an extremely common use case for gql, doing this to expose your domain data in a graph breaks microservice isolation patterns and directly ties your domain contracts to internal datamodels; both of which are almost always bad long-term.
If the problem you are trying to solve is "spend less time building http endpoints for CRUD apps", then something like graphjin is a win, but I'd argue it not a pattern you'll want to use forever. If you are using graphql to aggregate cross domain services in a large engineering organization, this is a bad idea.
Thin Backend takes a bit more of a higher level approach to database operations than services like GraphJin, but solves fundamentally the same problem. Doing things in a more structured way also allows us to do things like optimistic updates by default that require manual work with GraphQL tools.
To see some code examples, here's a small example project done with thin-backend: https://github.com/digitallyinduced/thin-backend-todo-app It's running on Vercel here: https://thin-backend-todo-app.vercel.app/
If you're interested in how building a small demo app with Thin looks like, check this video https://youtu.be/-jj19fpkd2c
Kind of lame to pitch this in the comments on GraphJin. Make your own post and try to get on the front page on your own merits without bagging on this.
Thin was on the Frontpage of HN around a month ago: https://news.ycombinator.com/item?id=31164799
Many posts will have alternatives posted in the comments. Often the creator is the best to provide links and describe the differences.
My alternative is a generalized DSL to code framework powered by CUE. You can write directly in the output and later regen because it uses diff3. Generate all the things in any languages!
Here's a quick example of using it as a library in your own code.
https://gist.github.com/dosco/2422006fe322bd81c00988570f50a5...
In what way?
If it's RBAC, why am I not just giving creating per-user postgres roles inheriting from appropriate roles for the relevant features?
> In a traditional app auth is on the app level and defined in code, not a mix of code, SQL, JWT claims and Hasura/Supabase configs.
Doing authz via DB-side permissions attached to per user DB accounts is an equally, and more well-established, traditional app approach.
If you use tools like Sqitch, Metagration or any other suitable migration management system then functions in your database stop being scary.
Sqitch: https://sqitch.org/ Metagration: https://github.com/michelp/metagration
You can try a demo here: https://datasette-graphql-demo.datasette.io/graphql?query=%0...
Despite years of asking people to explain its use, any example cases given can always be resolved with regular old http/REST style apis.
You still have to write sql queries to back your graphql calls anyway, so why are we spending extra obfuscation and effort instead of just getting the work done.
Facebook has so corrupted the engineering environment over the years that if a company recruiter contacts me and I find they're using any FB tech other than React, it's an instant no. I've walked over that luau bbq pit enough. React only gets the pass because it's not specifically bad tech, and it's too ubiquitous.