Skip to content

MeshQL

Shape your API, not your codebase.
TypeScriptRESTSQL-nativeJSRnpm

MeshQL adds GraphQL-style field selection to REST. Clients ask for exactly what they need. You write one SQL query, return flat rows, and MeshQL shapes nested JSON — no resolver per field.

The idea

GraphQL pain

40 resolvers, N+1 debugging, and codegen eating your types.

REST pain

Every client wants a different ?include= and you ship half the database.

MeshQL middle ground

Normal REST URLs. Query in headers. One resolver per entity. SQL you already write.

Get started in minutes

Published on JSR (@meshql/*) andnpm (meshql-*).

npm
npm install meshql-core meshql-http meshql-client
npm i express
JSR
npx jsr add @meshql/core @meshql/http @meshql/client
npm i express

No database required for the first run — follow the5-minute guide or try theinteractive showcase.

Read a user — GET + X-Mesh-Query
GET /mesh/user/1
X-Mesh-Query: <base64 of the JSON below>

{
  "user": {
    "id": true,
    "name": true,
    "tokens": { "accessToken": true }
  }
}
// Shaped response
{
  "user": {
    "id": 1,
    "name": "Ada Lovelace",
    "tokens": [{ "accessToken": "tok_ada" }]
  }
}

How it works

  1. 1

    Define a schema

    Entities, fields, joins — one resolver per root entity.

  2. 2

    Client sends a query

    JSON or QL in X-Mesh-Query. The SDK encodes it for you.

  3. 3

    MeshQL shapes the response

    JoinPlan tells you what to fetch. Return flat SQL rows; get nested JSON.

Map the API

Put that selection in X-Mesh-Query on the GET routes below (or in the body for POST /mesh).Reads and writes

Reads — where X-Mesh-Query goes

  • GET/mesh/{entity}List — put selection in X-Mesh-Query
  • GET/mesh/{entity}/{id}One row — same header
  • POST/meshComplex read — query in JSON body

Writes — REST mutations

  • PUT/mesh/user/1Update scalars (+ optional $select)
  • POST/mesh/user/1/tokensCreate many-child
  • DELETE/mesh/user/1/tokens/42Delete many-child
  • PUT/mesh/user/1/businessConnect one-relation
  • DELETE/mesh/user/1/businessDisconnect one-relation

Packages

Fifteen focused libraries — install only what you need. Same semver on JSR and npm.

Explore the docs

Client shapes the response

JSON or QL queries via headers. The SDK handles encoding — browsers and Node.

One resolver per entity

JoinPlan tells you exactly which fields and joins to fetch. No N+1 resolver maze.

SQL-native

Optional buildSelectSql() for Postgres and SQLite, or ORM adapters for Prisma, Drizzle, and Kysely (v0.6.0).

Your DB connection

MeshQL does not pool connections — pass your Prisma client, pg.Pool, or Drizzle db into the resolver. See the connections guide.

Framework adapters

Express, Fastify, and Hono — plus Deno and Bun guides with the same HTTP contract.

Security plugins

Integrity signing and access control ship as optional @meshql/integrity and @meshql/access plugins.

One /mesh tree

REST resources for reads and writes. Field selection on reads only — no GraphQL mutation DSL. Reads and writes.

Real-time (v0.9)

@meshql/pubsub + @meshql/sse — field-aware SSE refresh after your mutations. SSE guide.

GraphQL migration

@meshql/codemods converts SDL to MeshQL schema. From GraphQL.

Works everywhere

Node 22+, Bun, Deno, and modern browsers. JSR for source; npm for compiled ESM.