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
VerbPathMeaning
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
MethodPathPurpose
GET/mesh/{entity}List — selection in X-Mesh-Query
GET/mesh/{entity}/{id}Point read
POST/meshComplex read only (query in body — not a mutation RPC)
GET /mesh/user/1
X-Mesh-Query: <base64 selection>
TargetToday in @meshql/http
PUT /mesh/{entity}/{id} updateStill 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-patternWhy not
GraphQL mutation { updateUser { … } }Wrong model for MeshQL
Nested $connect / $disconnect / $createMany in a bodyURLs already express that
POST /mesh/write as the product write APISecond mental model; not REST resource-per-URL