Skip to content

Reads and writes

MeshQL’s pitch is REST underneath, GraphQL-style field selection on top. That applies to the whole API:

  • Reads — resource URLs + X-Mesh-Query for which fields/joins to return
  • Writes — the same resource URLs with PUT / POST / DELETE
  • No GraphQL-style nested mutation body ($connect / $disconnect / $createMany)
  • No parallel write RPC like /mesh/write

One mental model: resource-per-URL. Selection is a read concern. Mutations are normal REST.

  1. Consistency — reads are already resource-per-URL and cacheable. Writes match, or the library teaches two models.
  2. Positioning — “REST underneath, field selection on top.” A nested mutation DSL would break that on the write side.
  3. Simplicity — REST doesn’t need $connect / $disconnect / $createMany.
    POST to a sub-resource collection is create.
    DELETE on a sub-resource URL is disconnect / delete.
    Expressiveness lives in URLs, not new operators.
# Update user's own fields — body is the patch; header selects the response shape
PUT /mesh/user/1
X-Mesh-Query: <base64 $select for the response shape>
Content-Type: application/json
{ "name": "Ada Lovelace" }
# Create a new token under this user
POST /mesh/user/1/tokens
Content-Type: application/json
{ "accessToken": "tok_new" }
# Delete one specific token
DELETE /mesh/user/1/tokens/42
# Connect an existing business (replace the to-one link)
PUT /mesh/user/1/business
Content-Type: application/json
{ "entityId": "biz_42" }
# Disconnect it
DELETE /mesh/user/1/business
Verb Path Meaning
GET /mesh/{entity} List read (+ X-Mesh-Query)
GET /mesh/{entity}/{id} Point read
PUT /mesh/{entity}/{id} Update scalars; optional X-Mesh-Query for shaped response
POST /mesh/{entity}/{id}/{relation} Create a many child (or link for M2M)
DELETE /mesh/{entity}/{id}/{relation}/{childId} Delete / unlink one many child
PUT /mesh/{entity}/{id}/{relation} Connect / replace a one relation (entityId)
DELETE /mesh/{entity}/{id}/{relation} Disconnect a one relation

Many-to-many uses the same URLs (junction insert/delete under the hood when the join has through):

POST /mesh/post/1/tags
{ "entityId": 7 }
DELETE /mesh/post/1/tags/7
Method Path Purpose
GET /mesh/{entity} List — selection in X-Mesh-Query
GET /mesh/{entity}/{id} Point read
POST /mesh Complex read only (query in body — not a mutation RPC)
GET /mesh/user/1
X-Mesh-Query: <base64 selection>
Target Today in @meshql/http
PUT /mesh/{entity}/{id} update Still a point-read transport (no body patch) — will become the update above
POST /mesh/{entity}/{id}/{relation} Path used for file upload (:field) today — relation create is the mutation profile
DELETE /mesh/... 405 until writes land

Until core writes ship, implement these same /mesh/... paths in your app (or a thin adapter) so clients already match the contract. Do not introduce a second write API.

Anti-pattern Why not
GraphQL mutation { updateUser { … } } Wrong model for MeshQL
Nested $connect / $disconnect / $createMany in a body URLs already express that
POST /mesh/write as the product write API Second mental model; not REST resource-per-URL