What does HackerNews think of sqlx?

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, SQLite, and MSSQL.

Language: Rust

#2 in MySQL
#2 in PostgreSQL
#2 in PostgreSQL
#8 in Rust
#3 in SQL
SQLx [1] is commonly recommended. It has the nice feature that your queries can be verified at compile time.

[1]: https://github.com/launchbadge/sqlx

I felt the same way until I found sqlx (https://github.com/launchbadge/sqlx). The package lets you write sql in your app with compile-time checks. Was so much easier to get up and running than learning a new ORM.
If writing SQL directly, what process do you use to update your queries during schema changes? Do you rely on a test suite to catch errors then update queries by hand? Are you using compile-time checks through libraries like sqlx [1]?

[1]: https://github.com/launchbadge/sqlx

Part of the solution could be using tooling which can compile time check SQL is valid like https://github.com/launchbadge/sqlx
On a similar note, there's also sqlx for rust [1] - and I find it extra impressive that they manage to support not only postgres, but sqlite and and mysql too.

[1] https://github.com/launchbadge/sqlx

I find that the best of both worlds is to use something like sqlx for rust [1], which does compile time checking (using postgres explain analyze!) of the queries and macros for static typing, but still let you write the query raw (and can be turned off completely by removing the trailing macro `!` as an escape hatch)

We use it extensively for Windmill (e.g: [2]) and couldn't be happier.

[1]: https://github.com/launchbadge/sqlx [2]: https://github.com/windmill-labs/windmill/blob/d60a770eb710e...

ORM's come and go just like web frameworks, so I really don't care for them. I use sqlx [1] when I can.

1: https://github.com/launchbadge/sqlx

> I wonder why this wasn't built on top of, or an enhancement to, the existing (excellent) multicorn[1] project

Have to agree with you there, multicorn is extremely cool. I'm a big sqlalchemy fan so their default SQLA wrapper was a killer feature to give up (although maybe we could do something similar with launchbadge/sqlx[1]). We investigated using multicorn early this year and had a few hiccups. Activity on the original repo[2] quieted way down ~3 years ago. For example, pg14 support hasn't landed and the newest supported python version is EOL in 2022. There is new fork[3] with pg14 support (15 in the pipe) that might pick up in adoption but thats still TBD.

Supabase aims to support new major Postgres versions within 2-3 months so we have to be very careful taking on dependencies that might slow that process.

> I'd love to understand more about the technical rationale that drove this.

Architecturally, multicorn has postgres communicate with a separate python process on the host that does all the hard work. That's convenient, but it can bloat over time and/or be memory hungry for larger result sets. The rust implementation runs in-process and is generally a lot lighter.

Currently I'd say supabase/wrappers is a safer/easier version of the C API vs a direct analog to multicorn. Over time I think we'll see that comparison become more appropriate. There's a lot of excitement around the concept internally and we've already been floating some ideas wrt `auto-mapping` tables for common SQL dialects, a generic JSON HTTP API wrapper, etc. Stay tuned!

[1]https://github.com/launchbadge/sqlx [2]https://github.com/Segfault-Inc/Multicorn [3]https://github.com/pgsql-io/multicorn2

Rust is getting there, with sqlx[0] you can write plain sql and it gets validated at compile time.

[0]https://github.com/launchbadge/sqlx

It also could be that the learning curve is steep to get some things done and this was a side project that I wanted to get done. That could have contributed to my bad experience, also I'm not developing in php professionally anymore and sometimes when I want to get shit done, but this time it didn't worked for me.

I would not go with nodejs/deno because it doesn't feel a resilient foundation, let's say you put your php server out there, it wouldn't crash easily or if it "crash" would not kill the entire server, so there are benefits with php in that regard, it feels more easy to fire and forget.

Go should have my go to, but because never used didn't wanted to go that route, then Rust that is more painful than php in regard of the frameworks, there are not so mature ecosystem in the web category, but I can build a resilient system with rust, even I could write plain old sql that get's validated at compile time[1], that sounds amazing (well there are few drawbacks, but I like to be able to freely refactor the app in the future and the tooling tells me where I broke it, and with all other alternatives, except Go, there is no such library).

So this is more personal preference and php didn't meet my expectations, that doesn't mean that php is bad, but for me and my use case is, we cannot blame to php yet, but I think the core developers must push even harder the type system, offer better 1st party tooling and more guaranties in regard of the integrity of your program, this is one take of rust that if it compiles it works so when you do code review you only need to review the logic :)

[1] https://github.com/launchbadge/sqlx

In Rust: https://github.com/launchbadge/sqlx In Haskell: https://hackage.haskell.org/package/esqueleto

Either it analyzes the given SQL to determine the in/out types of each SQL query, or it calls the database describe feature at compile-time.

> Since Java 8 there is static and default methods in interfaces.

That's not relevant to the part of my post that you quoted...

I was describing a hypothetical situation where you already have two interfaces, but would like to have functionality that only makes sense for an object that implements BOTH of those interfaces. The only way to do that in Java is to write a THIRD interface that combines the two and then go through and change your implementations to implement that new interface instead of the two separate ones.

However, in the bullet point above, I mentioned static methods on traits in Rust, which is different than what static methods on interface in Java are. In Java, a static method on an interface is just a function on the interface, itself. In Rust a trait can declare that an implementing type must have a static method matching the signature. This is because Rust traits are type classes, while Java interfaces are just object interfaces and cannot constrain the implementing type, itself.

> Curious, what rust sql query builder are you refer to?

I have been mostly using mysql_async (https://docs.rs/mysql_async/latest/mysql_async/), but recently started playing with sqlx (https://github.com/launchbadge/sqlx). I guess "query builder" isn't the right way to describe them, but I'm not sure what else to call them...

In reality, sqlx [1], probably the most popular SQL library for Rust, has a query! format string that ensures that all parameters are properly escaped. As far as I can tell, you can't use the new format string support to create SQL queries with that macro yet, so there is no security problem. When that's fixed and query! is updated for the new format string support, I'm certain that they will escape their parameters, so there will be no security problem then either.

Because all format strings are in macro context, where the macro has full control over what to do with all substituted parameters, Rust already has sanitized string interpolation. In terms of that JEP, the macro invocation is the policy object.

[1]: https://github.com/launchbadge/sqlx

We use sqx: https://github.com/launchbadge/sqlx. The devs create and commit the up/down migration files and the cicd runner runs the migrations against the db.
sqlx is able to do this, but using compile time macros. For a typescript project you'd possibly need a running process reading your source files for queries and probing the database for what will come back.

https://github.com/launchbadge/sqlx/#compile-time-verificati...

If we consider GraphQL to be a domain querying language, what have we gained over REST? You are free to model endpoints in REST according to your domain (and deal with complexity behind the facade), and I'd argue that REST can offer an even more ergonomic DSL interface if you just write whatever you want (s-expressions, let's say) and pass them to some POST endpoint that reads your DSL and parses it. If the idea of writing the DSL and POSTing it, parsing it, and doing the very specific logic you want sounds wrong to you, it seems like GraphQL should also similarly wrong. If it sounds good to you, then is GraphQL far enough?

> 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...

[1]: https://github.com/launchbadge/sqlx

hmm, no. While compile-time type check[0] for SQL queries changes your whole experience of how you do SQL, at the end of the day creating an webapp in rust is not as easy or mature as any of the other languages more suited for the task. If you want to learn, then its fine. But if you are newish to rust and want to do your next CRUD app at work in rust, I don't think its going to a good experience.

[0]: https://github.com/launchbadge/sqlx

Also look at https://github.com/launchbadge/sqlx. I love compile-time checked queries (it's optional).

It's simple to use too, async and includes a pool.

There are some similar projects, like sqlx [1] for Rust. My problem with these is that they don't help to solve the actually hard problems.

While nice to have, preventing bugs with static SQL is usually easy to do by writing a few tests. Most of the SQL related bugs I have encountered were due to queries with dynamic/conditional joins, filters and sorting - and almost every project using a database needs those.

Approaches like this don't help there. That requires heavy-weight solutions that are more cumbersome to use and need a strong type system, like diesel [2] (Rust), Slick [3] (Scala) and some similar Haskell projects.

[1] https://github.com/launchbadge/sqlx

[2] https://github.com/diesel-rs/diesel

[3] https://scala-slick.org/