~/gqlith — gqlith generate Early access

A POSTGRES-TO-API GENERATOR

You don’t need a backend team.
You have Postgres.

Hand-writing CRUD resolvers, bolting auth onto every endpoint, babysitting a vendor engine — skip all of it. gqlith reads your schema and generates a GraphQL, REST and MCP API plus a fully-typed TypeScript client, with one permission model and one filter language, as TypeScript you own and deploy to the edge.

# the old way: weeks of CRUD, a bolted-on auth layer, an engine to operate

~/gqlith $ gqlith generate

read schema · 24 tables, 9 enums

GraphQL + REST + MCP + typed TS client · no gqlith at runtime

done in an afternoon

~/gqlith $

no engine · no sidecar · no gqlith at runtime · cloudflare workers + deno

# try it

~/gqlith $ gqlith generate # example schema: Blog

schema.sql
CREATE TABLE posts (  id          uuid PRIMARY KEY,  author_id   uuid REFERENCES authors(id),  title       text NOT NULL,  body        text NOT NULL,  status      text NOT NULL,         -- 'draft' | 'published'  published_at timestamptz,  created_at  timestamptz DEFAULT now()); COMMENT ON TABLE posts IS '  @gqlith.rbac(reader: read; author: read, write; admin: *)  @gqlith.rowFilter(    reader: status = ''published'';    author: author_id = $userId OR status = ''published'';    admin:  *  )';
query LatestPosts {
  posts(
    where: { status: { eq: "published" } }
    orderBy: [publishedAt_DESC]
    first: 10
  ) {
    edges {
      node {
        id
        title
        author { name }
        publishedAt
      }
    }
  }
}

One schema in. Three working APIs out. Switch between the examples.

# Why it holds together

Author once. Enforced on every surface.

one-permission-model.txt
# author the rule once, in a SQL comment
@gqlith.rbac(member: read; chef: read, write)
@gqlith.rowFilter(member: status = 'published')

# enforced identically on every surface
GraphQL  recipes(where:)        rbac rowFilter tenancy
REST     GET /api/v1/recipes rbac rowFilter tenancy
MCP      graphql_query rbac rowFilter tenancy

# Batteries included

Built in. Not bolted on.

The things every other generator leaves as “do it yourself” — generated for you, and governed by the same permission model as everything else.

  • client SDK

    A typed client, generated too

    A fluent TypeScript client emitted next to the server — return types narrowed to exactly what you select. One schema, no drift.

  • @gqlith.file

    File uploads, governed

    Direct-to-store uploads with mint → confirm, signed downloads, image variants, per-tenant quotas and lifecycle GC — S3, R2, GCS.

  • rls: generate

    Generated RLS policies

    Postgres RLS compiled from the same annotations as the API, with JWT claims propagated per transaction. Defense in depth, one source.

  • @gqlith.jsonSchema

    Typed JSONB

    Declare a JSONB column's shape once: real GraphQL types, validated writes, typed filtering — and a drift-check CLI.

  • crons

    Scheduled jobs

    Cron triggers generated to native primitives — Cloudflare crons, Deno.cron — with a dev-mode interval locally.

  • tenancy: row

    Multi-tenancy, no leakage

    tenant_id injected into every read and write. Cross-tenant access returns 404 — no error leakage.

  • @gqlith.customFields

    Custom fields, tenant-defined

    Let each customer define their own fields at runtime — a governed catalog, typed values, inline atomic writes, filtering and sorting on every surface.

  • @gqlith.version

    Optimistic concurrency

    Lost-update protection with a _version field on GraphQL and ETag / If-Match on REST.

  • one tx

    Atomic by request

    Multiple root mutations run in one transaction. Any error rolls back all of them — automatically.

  • @gqlith.softDelete

    Soft delete + restore

    Deletes become tombstones, auto-filtered from every query, with a generated restore mutation.

  • on("orders.update")

    Durable webhooks & queues

    React to changes through a transactional outbox — at-least-once delivery, retries, DLQ, idempotent effects — plus verified inbound and HMAC-signed outgoing webhooks.

  • nested writes

    Full nested mutations

    create, connect, connectOrCreate, update, upsert, disconnect, delete, set — multi-hop, in a single mutation.

  • @gqlith.rateLimit

    Rate limiting across protocols

    One sliding-window budget shared across GraphQL, REST and MCP per identity — no dodging it by switching protocol.

  • createToken

    Programmatic tokens

    Issue API keys for your own users with custom claims — revocation takes effect on the very next request.

  • @gqlith.audit

    Transactional audit log

    Before/after diffs written inside the same transaction as the change. Roll back the write, roll back the trail.

  • subscriptions

    Real-time, per-row authorized

    SSE subscriptions re-check RBAC, row filters and tenancy at event time — live updates never leak a hidden row.

  • MCP

    AI-ready MCP

    A self-describing agent surface, projected through the caller's permissions — an agent can discover and query only what the caller may see.

  • groupedAggregate

    Aggregates, grouped

    count, sum, avg, min, max, distinctCount — grouped, HAVING-filtered, per-row authorized, and typed in the client SDK.

  • cache

    Pluggable caching

    A cache slot with in-memory, Cloudflare KV and Redis backends — for JWKS and your own reads, swappable by config.

  • telemetry

    OpenTelemetry built in

    Traces and metrics over OTLP to Datadog, Prometheus or Sentry — permission denials, rate-limit hits and audit emits included.

# Filtering

Filtering that goes further

The same expressive grammar in a search box, a URL, or an agent tool — far past the usual eq / in / contains.

filter.txt
Comparisons + ordering status = 'published' AND servings >= 4 ORDER BY createdAt DESC
Across relations + quantifiers cuisine.name icontains 'italian' AND reviews ANY (rating >= 4)
Relative time createdAt >= now-7d createdAt >= now/M
Arrays + typed JSONB tags contains ('vegan', 'gluten-free') payload__tier = 'pro'
One operator, many values title icontains(any) ('quick', 'easy', 'simple')
Aggregates + your own SQL count(*) >= 5 recipes_rated_at_least(4)

= != < > betweenlike / ilikeregex / iregexfts full-textin / not incontains / overlaphasKey (JSONB)some / every / nonedot.notation relationsnow-7d · now/Mcount(*) → HAVING:named params

Read the full pitch on filtering →

# Own it

Delete the generator. Your API keeps running.

gqlith emits self-contained TypeScript with zero runtime dependency on us. No engine to boot, no sidecar, no vendor process — uninstall the generator and nothing changes for the people using your API.

your-app — production

~/app $ npm uninstall gqlith

removed 1 package in 412ms

~/app $ curl -s localhost:8787/api/v1/health

200 OK # the API does not care — it is your code now

# Who it’s for

For people who ship, not operate

Stand up a secure REST + MCP backend over your Postgres in an afternoon — then own every line of it.

Small product teams

A complete, permission-correct backend — files, webhooks, custom fields included — without a platform team or a vendor engine to run.

Solo developers

Generate the boring 80% — CRUD, auth, filtering, uploads, a typed client — and hand-edit the rest. It’s your code.

AI / agent builders

A self-describing MCP server over your data on day one — projected through the caller’s permissions, so agents only ever see what the caller is allowed to.

# Straight answers

Before you hand over your email

Is this real, or vaporware?
It runs in production today — it already generates the REST and MCP surfaces behind BesTest, a live test-management product. The output is plain TypeScript you can read top to bottom.
Can I use it right now?
Not yet. gqlith is in private early access — hand-picked, a small group at a time, and not on npm. Leave your email and we bring you in as we open it up.
What doesn’t it do yet?
Deploy targets are Cloudflare Workers and Deno today — a native Node target is planned. Over MCP, agents write GraphQL through four governed tools; subscriptions over MCP and per-entity typed tools aren’t there. No Relay node(id) global IDs or persisted-operation allow-lists yet either.
Do I depend on you to keep running?
No. There is no gqlith package at runtime — uninstall the generator and your API keeps serving. You own every line.

# Compare

Why not just use what exists?

We evaluated the field. Each tool was strong at one thing and silent on the rest. Compared within category — codegen, runtime engine, router.

CapabilitygqlithTypical alternative
GraphQL + REST + MCP from one schema One permission model across all three GraphQL-only, or a second surface with a different auth story
Typed TypeScript client SDK Generated with the API — selection-typed, no drift Types only, or a separate codegen pipeline you babysit
One filter language across surfaces Object, text DSL and agent string A GraphQL-only dialect per tool
Vendor runtime None — no engine to operate, own the code, deploy to the edge A vendor engine or extension, always running
Postgres RLS Generated from the same annotations (opt-in) Hand-written policies, or a proprietary authz layer instead
File uploads & storage Built in — governed, direct-to-store, signed URLs A separate platform service, or DIY
Tenant-defined custom fields Built in — typed, filterable, discoverable DIY EAV tables or an untyped JSONB bag
Durable webhooks & scheduled jobs Built in — transactional outbox, native crons A separate queue plus glue code
Multi-tenancy (no leakage) Built in DIY — RLS + session vars / manual WHERE
In-transaction audit log Built in DIY trigger or middleware
Rate limiting across protocols Built in, one shared budget Paid tier / GraphQL-only / infra

Compared within category — every claim grounded in each tool’s own docs. See the full comparison.

See the full comparison →

request_access.sh

Be among the first to generate your backend.

gqlith is in private early access. Leave your email and we’ll bring you in as we open it up — a small group at a time.