7 ms·
Show HN: PgTyped – Typesafe SQL in TypeScript and Postgres
- shrumm 6y agoLooks a little like the Typescript equivalent of Xo (https://github.com/xo/xo https://github.com/xo/xo) for Go. Especially with Go, getting help with some initial scaffolding can be a huge timesaver. I'm assuming it's a similar gain for Typescript.
- tadasv 6y agoA better equivalent in Go is https://github.com/kyleconroy/sqlc https://github.com/kyleconroy/sqlc
- shrumm 6y agothanks! I'll try this - one of my pet peeves with Xo is handling nullable types and more advanced types like JSONB required editing the generated code significantly to make it work. Hopefully sqlc solves that.
- zelly 6y agoIs there anything like this for Rust or C++? I like the idea of code generation instead of doing the work at runtime (like in ORMs). This is like making your database schema the IDL spec.
- aganame 6y agohttps://github.com/launchbadge/sqlx https://github.com/launchbadge/sqlx perhaps Also hugsql for clojure and pugsql for python.
- alde 6y agoSqlx looks nice! I had some ideas about porting pgtyped to rust and utilizing macros to do query type inference at build time, but was worried that such db-connected macros will slow down the build process. Nice to see that it worked out for sqlx.
- K0nserv 6y agoThere's Diesel[0] for Rust which is a full ORM. It's by Siân Griffin[1] who, as I understand it, is also behind a lot of how rail's ActiveRecord works. 0: https://diesel.rs/ https://diesel.rs/ 1: https://twitter.com/sgrif https://twitter.com/sgrif
- status_quo69 6y agoJust to clarify a bit for other readers since I've worked with diesel for while, diesel isn't a "full" orm, as there are no real helpers provided to you outside of "we can map the result of a db query into a struct(s) that you specify" and some really nice guarantees for compile time queries. Other than that, your struct is a pretty dumb mapped representation and it's on the implementers of the application code to provide sugar for better access patterns. For people coming from something like active record, this is (in my opinion) closer to Arel than ActiveRecord, or closer to sqlalchemy core than sqlalchemy orm. As an example, you won't necessarily be able to do `MyStruct.join(OtherStruct)` and have it magically figure out how to query the database and map the results out of the box.
- status_quo69 6y agoClarification: compile time query building, not querying. Due to inlining from the compiler, you can almost entirely construct the query at compile time and shave it down to a few string concatenations.
- emanuelez 6y agoYou might also want to check Kanel out! https://github.com/kristiandupont/kanel https://github.com/kristiandupont/kanel
- conroy 6y agoAs a maintainer of a similar project[0], it's great to see another entry in this space. sqlc currently has great support for Go and experimental support for Kotlin. I'm planning on adding TypeScript support in the future, so it's great to see that others in the TypeScript community find this workflow useful. [0] https://github.com/kyleconroy/sqlc https://github.com/kyleconroy/sqlc
- eyelidlessness 6y agoI came here to mention a similar approach, which last time I looked was a very compelling experiment[1], but its original author has actually built out a real library, Zapatos[2] which looks very very good. [1]: https://github.com/jawj/mostly-ormless https://github.com/jawj/mostly-ormless [2]: https://jawj.github.io/zapatos/ https://jawj.github.io/zapatos/
- alde 6y agoLooks like Zapatos still requires the user to manually specify param/result types for custom SQL queries?
- gmac 6y agoZapatos author here. Yes, it does. But for most of what you’d use an ORM for, you probably won’t need custom queries.
- eyelidlessness 6y agoHey I’m not sure this is the best venue, but I’m trying to make the case for getting my org off of sequelize, and your library is right in line with my goals. The hardest sell is going to be publicly visible test coverage. Would you welcome a dedicated effort from an early adopter to introduce tests?
- gmac 6y agoYes, that would be very welcome. I suggest you keep me in the loop from the start to make sure we end up with something we’re both happy with.
- renke1 6y agoLooks pretty cool. What I really want though is a library that let's me write plain SQL queries which are then mapped into nested objects in a smart way without too much manual work (I know Postgres can do JSON stuff, but the queries look pretty complicated for what little they actually do). Say `SELECT * FROM user LEFT JOIN post ON user.id = post.id` would be mapped to `[{userId: 1, name: renke1, posts: [{postId: 2, title: "foo"]]`. You probably need some kind of meta data to figure out how tables and thus objects relate to each other though. Basically, I want to be able to leverage the full power of modern databases without being constrainted by typical ORM limitations. Also, I don't need features like lazy loading, sessions, caches and things like that. A great advantage is that you can (provided you have some test data) easily test your queries while you develop a new feature (think IntelliJ IDEA where you can simply execute an SQL query on the fly).
- bijection 6y agoIf you were willing to give up a bit of magic, you could probably build this as a thin layer over PgTyped. The API could be something like this: Query.sql SELECT * FROM user LEFT JOIN post ON user.id = post.id Application.ts const results = await Query() const nested = nest(results, { parentFields: ['userId', 'name'], childFields: ['postId', 'title'], childName: 'posts' ) If you wanted, you wouldn't really have to specify child fields, since they'd just whatever wasn't a parent field. It'd take a bit more work to get it to do multiple levels of nesting, but after a point it doesn't make sense to write queries that return so much duplicate data anyway.
- kevsim 6y ago> but after a point it doesn't make sense to write queries that return so much duplicate data anyway This is what I'm constantly wondering. At what point does it stop being good to return the user table results again and again and just switch to, for example, an IN query to get the posts?
- alde 6y agoThanks! A grouping feature will definitely be useful, I have been thinking about a good way to add it to pgtyped. Will grouping fields by tables they belong to good enough? Or is there some different grouping logic you have in mind?
- adriancooney 6y agoI really like the unique approach of the annotated SQL files and can definitely see some use cases where it would be good to declutter the SQL from the code. For me personally, I'd be hesitant to add another build tool to my already bloated toolchain. Could create a special Babel-style "import" type that automatically transforms your code (JIT)? It could remove some of the friction in adoption (for Babel users at least). Another one in a similar vein with strict typing and really nice SQL interpolation for Postgres: https://github.com/gajus/slonik https://github.com/gajus/slonik
- ggregoire 6y agoSame, I really like this approach. Some of the benefits are: - better separation of concerns - better integration with SQL tools (syntax highlighting, autocompletion, etc) - way easier to run/test/debug your queries into a database client - better languages analysis of your projects (e.g. % of SQL in your GitHub/GitLab repo) -- If anyone interested in applying this approach in your Python projects, I recommend this package: https://github.com/mcfunley/pugsql https://github.com/mcfunley/pugsql
- goofiw 6y agoI wonder if the JIT compiling will work asynchronously - from the readme it's getting the types from the live database schema.
- goofiw 6y agoIts actually doing the with what looks like a custom async messaging queu. Pretty cool.
- hn_throwaway_99 6y agoCan't say enough good things about slonik. Have been using it in production for a while now and I love it. For people like me who believe "abstracting away SQL" is a mistake, but still want protections against SQL injections and a nice API, slonik is a godsend.
- garrybelka 6y agoHow is it different from Slonik? https://github.com/gajus/slonik https://github.com/gajus/slonik
- JBReefer 6y agoSo basically Dapper TS? I’m very interested!
- ksashikumar 6y agoLooks cool! And the header image looks awesome! Did you use any tool to do it?
- alde 6y agoThanks! Not really, just basic vector shapes and an isometric projection grid to make sure perspective is right.
- the_duke 6y agoThere 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 https://github.com/launchbadge/sqlx [2] https://github.com/diesel-rs/diesel https://github.com/diesel-rs/diesel [3] https://scala-slick.org/ https://scala-slick.org/
- rubber_duck 6y ago> preventing bugs with static SQL is usually easy to do by writing a few tests I've heard the same argument about TypeScript vs JavaScript and it's something dynamic typing proponents often say but in practice I find immense value in having the types autocompleted and checked in the editor - and I've worked plenty on both sides, current project is substantial RoR codebase, I've worked with Python and node.js backends on mature codebases. Eventually all these languages have some sort of static type hinting efforts to improve tooling - typescript being most successful. The best thing I saw in this space was F# type providers which didn't require a pre-build step - the language had a mechanism for writing custom type providers that would look up the data source during compilation - unfortunately I didn't get to use it on any real world projects.
- Nelkins 6y agoF# also has support for analyzers that can achieve similar functionality in case you don't want to take a dependency on a type provider. https://github.com/Zaid-Ajaj/Npgsql.FSharp.Analyzer https://github.com/Zaid-Ajaj/Npgsql.FSharp.Analyzer https://github.com/aaronpowell/FSharp.CosmosDb#fsharpcosmosdbanalyzer- https://github.com/aaronpowell/FSharp.CosmosDb#fsharpcosmosd...
- alde 6y agoORMs like Diesel are definitely very useful. The problem I have with them is that their ORM abstraction often leaks. Fixing these abstraction leaks is a hard problem [1]. Ofc, there has been attempts to reconcile relational DBs with OOP like languages, but they are not very popular. [2] PgTyped and some similar libs try to solve a simpler problem (typing static queries) and can be used to build more complex solutions when needed. Writing query result/param type assertions by hand and using tests to guarantee type synchronization between DB and code wasn't maintainable on most projects I have seen. [1] https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch https://en.wikipedia.org/wiki/Object-relational_impedance_mi... [2] https://en.wikipedia.org/wiki/The_Third_Manifesto https://en.wikipedia.org/wiki/The_Third_Manifesto
- tonyhb 6y agoThis is similar to sqlc for Golang: https://github.com/kyleconroy/sqlc https://github.com/kyleconroy/sqlc If you're looking for the ability to generate type-safe SQL – given you write SQL correctly – this project is pretty good. Aalso a fan of SQLBoiler (https://github.com/volatiletech/sqlboiler https://github.com/volatiletech/sqlboiler) for Golang, for simple type safety: `models.Accounts(models.AccountWhere.ID.EQ(id)).One(ctx, db)`. Though SQLBoiler breaks with left joins, as it auto-generates your structs and maps results 1-1 with table definitions. In this case you have to custom type something, either using sqlc or squirrel.
- Vinnl 6y agoLots of comments here about similar projects in a different language, but the fact that this targets TypeScript is explicitly what makes it interesting to me. Using regular Javascript database libraries, even ones that have type definitions, require a lot of double typing. I've been relatively satisfied with TypeORM, but one thing that's been a hurdle for me to some extent is its reliance on experimental decorators, and the resulting incompatibility with Babel - which in turn makes it harder to integrate with the wider ecosystem, e.g. Next.js. As far as I can see on first glance, there's nothing here yet that makes it incompatible with Babel, so my tip would be to make it an explicit goal to keep it that way :)
- smallnamespace 6y agoAs usual for Babel, there’s a plugin for that: https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata https://github.com/leonardfactory/babel-plugin-transform-typ...
- Vinnl 6y agoYeah, but then you still have the same problem of ecosystem divergence. (In addition to the fact that I don't like using unstandardised features in the first place...)
- mikewhy 6y agoOpening a can of worms for sure, but the reliance on Babel in the JS community is not a good thing to me. It's another reason why I prefer TypeScript, as in TSC, not whatever equivalent babel happens to support.
- SenpaiHurricane 6y agolol. This reminds me Hibernate xml mappings :D
- Allezxandre 6y agoMy favorite SQL library has been Go-Jet in Go: https://github.com/go-jet/jet https://github.com/go-jet/jet It has a different approach from PgTyped, which generates type-safe TypeScript code from SQL, whereas Go-Jet generates type-safe SQL from Go code I'd love to try something along the lines of PgTyped and see how the two solutions compare though
- hombre_fatal 6y agoAlmost every top-level comment is someone shilling another project, usually in Golang as if that's even related. Let's have some Show HN etiquette.
- xellisx 6y agoI sort of have something like this for PHP and MySQL. https://github.com/ellisgl/GeekLab-GLPDO2 https://github.com/ellisgl/GeekLab-GLPDO2
- garaboncias2 6y agoan other lib with light ORM: https://www.npmjs.com/package/pogi https://www.npmjs.com/package/pogi
- fmakunbound 6y agoIt’s cool, and I don’t fault the author for working on something that obviously gives him joy, but save yourself a bunch of trouble and avoid this kind of thing. The queries showcased are the least interesting of the set of queries you’ll ultimately end up with I’m a mature project.