Introduction
MeshQL is a small TypeScript library for when you want GraphQL-style “give me these fields” queries, but you’d rather keep REST and write normal SQL.
Clients send a query (what fields they want, including nested stuff like user.tokens.accessToken). You get a JoinPlan with exactly those fields and joins. Write one query, return flat rows, MeshQL shapes the JSON. No resolver per field, no dataloader dance, no codegen eating your types.
Install
Section titled “Install”JSR (TypeScript source)
Section titled “JSR (TypeScript source)”Published on JSR under the @meshql scope.
# Nodenpx jsr add @meshql/core @meshql/http @meshql/client
# Bunbunx jsr add @meshql/core @meshql/http @meshql/client
# Denodeno add jsr:@meshql/core jsr:@meshql/http jsr:@meshql/client| Package | JSR | Purpose |
|---|---|---|
@meshql/core |
jsr.io/@meshql/core | Parser, planner, shaper, createMesh() |
@meshql/postgres |
jsr.io/@meshql/postgres | Postgres buildSelectSql ($1, $2, … placeholders) |
@meshql/sqlite |
jsr.io/@meshql/sqlite | SQLite buildSelectSql for Node 22.5+ node:sqlite / Bun / D1 |
@meshql/http |
jsr.io/@meshql/http | Express, Fastify, Hono adapters |
@meshql/client |
jsr.io/@meshql/client | Typed client SDK |
@meshql/upload |
jsr.io/@meshql/upload | File uploads (optional) |
@meshql/integrity |
jsr.io/@meshql/integrity | Request signing and integrity tokens |
@meshql/access |
jsr.io/@meshql/access | Entity, row, and field access control |
@meshql/persisted-queries |
jsr.io/@meshql/persisted-queries | Persisted query IDs, X-Mesh-Query-Id transport (v0.8.0) |
@meshql/access-cache |
jsr.io/@meshql/access-cache | Cache permission results per user (v0.8.0) |
@meshql/prisma |
jsr.io/@meshql/prisma | Prisma catch-all resolver (nested select) |
@meshql/drizzle |
jsr.io/@meshql/drizzle | Drizzle relational query resolver |
@meshql/kysely |
jsr.io/@meshql/kysely | Kysely + buildSelectSql flat-row resolver |
Core stack (most apps — pick a DB adapter):
# SQLite (zero setup, built into Node 22.5+)npx jsr add @meshql/core @meshql/sqlite @meshql/http @meshql/client
# Postgresnpx jsr add @meshql/core @meshql/postgres @meshql/http @meshql/client
# Prisma (catch-all ORM resolver)npx jsr add @meshql/core @meshql/prisma @meshql/http @meshql/clientWith security (signing + access):
npx jsr add @meshql/core @meshql/http @meshql/integrity @meshql/accessnpm (compiled ESM)
Section titled “npm (compiled ESM)”Until the @meshql npm org is available, packages publish as unscoped meshql-* with compiled dist/. Requires "type": "module" (or .mjs).
Core stack:
npm install meshql-core meshql-http meshql-clientFull stack (uploads + security):
npm install meshql-core meshql-http meshql-client meshql-upload meshql-integrity meshql-access| Package | npm | Purpose |
|---|---|---|
meshql-core |
npmjs.com/package/meshql-core | Parser, planner, shaper, createMesh() |
meshql-postgres |
npmjs.com/package/meshql-postgres | Postgres buildSelectSql |
meshql-sqlite |
npmjs.com/package/meshql-sqlite | SQLite buildSelectSql for node:sqlite / Bun / D1 |
meshql-http |
npmjs.com/package/meshql-http | Express, Fastify, Hono adapters |
meshql-client |
npmjs.com/package/meshql-client | Typed client SDK |
meshql-upload |
npmjs.com/package/meshql-upload | File uploads (optional) |
meshql-integrity |
npmjs.com/package/meshql-integrity | Request signing and integrity tokens |
meshql-access |
npmjs.com/package/meshql-access | Entity, row, and field access control |
meshql-persisted-queries |
npmjs.com/package/meshql-persisted-queries | Persisted query IDs, X-Mesh-Query-Id (v0.8.0) |
meshql-access-cache |
npmjs.com/package/meshql-access-cache | Cache permission results per user (v0.8.0) |
meshql-prisma |
npmjs.com/package/meshql-prisma | Prisma catch-all resolver |
meshql-drizzle |
npmjs.com/package/meshql-drizzle | Drizzle relational query resolver |
meshql-kysely |
npmjs.com/package/meshql-kysely | Kysely + SQL builder resolver |
Imports use the npm package names:
import { createMesh } from "meshql-core";import { meshExpressRouter } from "meshql-http/express";import { createClient } from "meshql-client";import { integrityPlugin } from "meshql-integrity";import { accessPlugin } from "meshql-access";Or install from a GitHub Release tarball (per-package tags like npm/core/v*):
npm install https://github.com/meshql/meshql/releases/download/npm/core/v0.1.4/meshql-core-0.1.4.tgzSee CONTRIBUTING.md for the release workflow (Changesets → per-package tags).
SQLite is first-class.
@meshql/sqliteruns on Node 22.5+’s built-innode:sqlite— zero native deps, zero Docker. Try the express-sqlite example. Postgres works via@meshql/postgresand the express-postgres example.
Quick start in 5 steps
Section titled “Quick start in 5 steps”Full walkthrough with Express, Hono, and Bun: docs/run-example.md
1. Init a project
Section titled “1. Init a project”mkdir my-meshql-app && cd my-meshql-appnpm init -y && npm pkg set type=modulenpm i -D typescript tsx @types/nodenpx jsr add @meshql/core @meshql/httpnpm i expressmkdir src2. Create src/index.ts
Section titled “2. Create src/index.ts”import { createMesh, type MeshSchema } from "@meshql/core";import { meshExpressRouter } from "@meshql/http/express";import express from "express";
const schema: MeshSchema = { entities: { user: { fields: ["id", "name"], table: "users" }, token: { fields: ["accessToken"], table: "tokens", columns: { accessToken: "access_token" }, }, }, joins: { "user.tokens": { entity: "token", on: "tokens.user_id = users.id", type: "many", }, },};
const mesh = createMesh(schema);mesh.resolve("user", async () => [ { user_id: 1, user_name: "Ada Lovelace", tokens_accessToken: "tok_ada" },]);
const app = express();app.use(express.json());app.use(meshExpressRouter(mesh, "/mesh"));app.listen(3001, () => console.log("http://localhost:3001/mesh"));3. Start the server
Section titled “3. Start the server”npx tsx src/index.ts4. Test with curl
Section titled “4. Test with curl”Q=$(echo -n '{"user":{"$select":{"id":true,"name":true,"tokens":{"$select":{"accessToken":true}}}}}' | base64 | tr -d '\n')
curl -s "http://localhost:3001/mesh/user/1" \ -H "X-Mesh-Query: $Q" \ -H "X-Mesh-Format: json"5. Or use the client SDK
Section titled “5. Or use the client SDK”npx jsr add @meshql/clientimport { createClient } from "@meshql/client";
const client = createClient({ url: "http://localhost:3001/mesh" });const user = await client.query( { user: { $select: { id: true, name: true, tokens: { $select: { accessToken: true } }, }, }, }, { entityId: "1" },);console.log(user);Collection queries and catch-all resolvers
Section titled “Collection queries and catch-all resolvers”Collection reads — use the same canonical query object as the wire payload:
const users = await client.query( { user: { $select: { id: true, name: true }, $page: { first: 10 }, $orderBy: [{ field: "name", direction: "asc" }], $where: { field: "role", op: "eq", value: "admin" }, }, },);console.log(users.items, users.pageInfo);Catch-all resolver — one handler for every entity (the pattern ORM adapters use):
mesh.resolve("*", async (plan) => { const { sql, params } = buildSelectSql(plan, schema); return db.query(sql, params);});A specific mesh.resolve("user", fn) always wins over the "*" fallback.
ORM adapters (v0.6.0+) and schema inference (v0.7.0)
Section titled “ORM adapters (v0.6.0+) and schema inference (v0.7.0)”Use your existing ORM client — MeshQL does not create database connections. See docs/database-connections.md.
Prisma (infer schema from schema.prisma):
import { PrismaClient } from "@prisma/client";import { createMesh } from "@meshql/core";import { schemaFromPrisma, withPrisma } from "@meshql/prisma";
const prisma = new PrismaClient();const schema = await schemaFromPrisma("./prisma/schema.prisma");const mesh = createMesh(schema);withPrisma(mesh, prisma, { schema });Drizzle — schemaFromDrizzle(tables) + withDrizzle(mesh, db, { schema }).
Kysely — withKysely(mesh, db, { schema, dialect: "postgres" }) runs buildSelectSql via executeQuery.
Full guide: docs/orm-adapters.md. Runnable demo: express-prisma.
Try the showcase
Section titled “Try the showcase”Interactive full-stack blog (React + @meshql/client) on SQLite — no Docker:
git clone https://github.com/meshql/meshql.gitcd meshqlpnpm install && pnpm buildpnpm --filter showcase startOpen http://localhost:3010/ — the browser app uses @meshql/client against /mesh/* for login, reads, writes, and uploads. Check DevTools → Network to see signed MeshQL requests.
Optional CLI tour: pnpm --filter showcase demo
See examples/showcase/README.md. Examples: express-sqlite, express-postgres, express-prisma.
Server (with SQL)
Section titled “Server (with SQL)”Pick the adapter that matches your database. Both expose the same API.
SQLite (Node 22.5+ built-in, no Docker, no native deps):
import { DatabaseSync } from "node:sqlite";import { createMesh } from "@meshql/core";import { buildSelectSql } from "@meshql/sqlite";import { meshExpressRouter } from "@meshql/http/express";import express from "express";
const db = new DatabaseSync(":memory:");const mesh = createMesh(schema);
mesh.resolve("user", async (plan) => { const { sql, params } = buildSelectSql(plan, schema); return db.prepare(sql).all(...params);});
express() .use(express.json()) .use(meshExpressRouter(mesh, "/mesh")) .listen(3001);Postgres (via pg):
import { createMesh } from "@meshql/core";import { buildSelectSql } from "@meshql/postgres";import { meshExpressRouter } from "@meshql/http/express";import express from "express";import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });const mesh = createMesh(schema);
mesh.resolve("user", async (plan) => { const { sql, params } = buildSelectSql(plan, schema); return (await pool.query(sql, params)).rows;});
express() .use(express.json()) .use(meshExpressRouter(mesh, "/mesh")) .listen(3001);Packages
Section titled “Packages”| Package | npm | Purpose |
|---|---|---|
@meshql/core |
meshql-core |
Parser, join planner, response shaper, createMesh() |
@meshql/postgres |
meshql-postgres |
buildSelectSql for Postgres |
@meshql/sqlite |
meshql-sqlite |
buildSelectSql for node:sqlite / Bun / D1 |
@meshql/http |
meshql-http |
Header transport + Express, Fastify, Hono adapters |
@meshql/client |
meshql-client |
Typed client, sets query headers for you |
@meshql/upload |
meshql-upload |
File uploads (optional) |
@meshql/integrity |
meshql-integrity |
Signing token lifecycle and request integrity |
@meshql/access |
meshql-access |
Entity, row, and dynamic field access |
@meshql/prisma |
meshql-prisma |
Prisma catch-all resolver |
@meshql/drizzle |
meshql-drizzle |
Drizzle relational query resolver |
@meshql/kysely |
meshql-kysely |
Kysely + SQL builder resolver |
HTTP adapter docs (routes, headers, curl): docs/http-adapters.md
ORM adapters: docs/orm-adapters.md · DB connections: docs/database-connections.md
Protocol specs (for language ports): specs/ · docs.meshql.dev/specs
Client SDK (browser, auth, uploads): docs/client.md
Security
Section titled “Security”Built-in limits (depth, complexity, rate) live in @meshql/core/builtins. Custom plugins use MeshPlugin from @meshql/core and mesh.use(). For signed requests and access control, add the dedicated packages:
# npmnpm install meshql-integrity meshql-access
# JSRnpx jsr add @meshql/integrity @meshql/accessRunnable demo: samples/npm-access in the meshql_stack repo.
JSR:
integrityandaccessrequire one-time package setup on jsr.io before first publish. See CONTRIBUTING.md.
Hacking on it
Section titled “Hacking on it”Node 22+, pnpm 11. Monorepo uses Turborepo.
packages/core engine (DB-agnostic)packages/postgres buildSelectSql for Postgrespackages/sqlite buildSelectSql for node:sqlite / Bun / D1packages/http adapterspackages/client SDKpackages/upload uploadspackages/integrity signing tokenspackages/access access controlpackages/prisma Prisma adapterpackages/drizzle Drizzle adapterpackages/kysely Kysely adapterexamples/ runnable demos (express-sqlite, express-postgres, express-prisma)Testing
Section titled “Testing”260+ unit tests across the monorepo (pnpm test). The engine is the focus:
| Package | Tests | Coverage highlights |
|---|---|---|
@meshql/core |
158 | JSON/QL parser, join planner, response shaper, spec conformance fixtures |
@meshql/postgres |
19 | buildSelectSql, nested joins, cursor keyset |
@meshql/sqlite |
31 | Same SQL builder contract as Postgres |
| Other packages | 52+ | HTTP transport, ORM adapters, integrity, access, persisted-queries, … |
Core tests exercise the full parse → plan → shape pipeline, including golden
queries from specs/fixtures/ so alternative implementations
can match the published protocol.
pnpm test # all package unit tests (CI)pnpm test:integration # Postgres integration (requires Docker)pnpm --filter @meshql/core test # engine onlyPRs welcome. See CONTRIBUTING.md.
Evaluating MeshQL? See the FAQ for GraphQL comparison, security, ORMs, and migration questions.
License
Section titled “License”MIT. Security issues: SECURITY.md.