5 ms·
Show HN: OmniQL – One Query Language for PostgreSQL, MySQL, MongoDB, and Redis
Hey HN, I built OmniQL — an open-source Go library that compiles
a universal query syntax into native database commands.
You write:
:GET User WHERE id = 42
It becomes:
PostgreSQL → SELECT * FROM "users" WHERE "id" = 42;
MongoDB → db.users.find({ id: 42 });
Redis → HGETALL users:42
It's a compiler, not a wrapper. Your query becomes an AST, then
translates to native commands. No runtime overhead — it generates
the same query you'd write by hand.
Works for DDL too:
:CREATE TABLE User WITH id:AUTO, name:STRING:NOTNULL
Becomes native CREATE TABLE for Postgres/MySQL or
db.createCollection for MongoDB.
Why I built this: I run a multi-database platform and got tired of
maintaining four different query syntaxes. OmniQL started as an
internal tool and we're open-sourcing it.
GitHub: https://github.com/omniql-engine/omniql https://github.com/omniql-engine/omniql
Docs: https://docs.omniql.com https://docs.omniql.com
Happy to answer questions about the compiler architecture, AST
design, or how we handle the semantic gaps between SQL and NoSQL.
- aerzen 8mo agoThe `:` prefix is an interesting choice. I don't fully understand when it is needed. It seems to always be used at the beginning of the query. But why is it not used before WHERE?
- canu21 8mo agoThe : prefix marks OmniQL commands, the action you want to perform (:GET, :CREATE, :DELETE, etc.). It's how the parser distinguishes OmniQL syntax from native SQL or other query languages. WHERE, AND, OR, LIMIT etc. are clauses. They modify the command but aren't commands themselves. So the rule is simple: commands get :, clauses don't. Think of it like a terminal. You prefix the command (git push) but not the flags (--force). Same idea here.
- aerzen 8mo agoWhy does JOIN get the prefix then? Is it a command? I would assume that it is a GET clause.
- canu21 8mo agoIn OmniQL, JOIN isn't a clause inside another query. It's a standalone operation. You write :INNER JOIN Order User ON user_id = id, not :GET ... JOIN .... Since it starts the query and tells the engine what to do, it gets the : prefix. The rule is: anything that begins a query gets :, anything that modifies it (WHERE, ON, LIMIT) doesn't. This is actually a deliberate design choice. In SQL, JOIN lives inside a SELECT so you end up with one big statement doing multiple things. In OmniQL each : command does one thing. Flat and predictable rather than nesting clauses inside clauses. This also lets the engine translate consistently whether it outputs SQL JOINs for Postgres and MySQL or a $lookup aggregation pipeline for MongoDB. Same syntax, engine handles the rest.