DataFn
Server

Routes

HTTP endpoints provided by the DataFn server.

Envelope Pattern

All DataFn endpoints use a consistent response envelope:

// Success
{ ok: true, result: { /* endpoint-specific data */ } }

// Error
{ ok: false, error: { code: string, message: string, details: { path: string } } }

All POST endpoints accept application/json request bodies.


GET /datafn/status

Returns server metadata, capabilities, limits, and a schema hash for client-server schema agreement.

Response:

{
  ok: true,
  result: {
    schemaHash: "sha256:abc123...",
    capabilities: [
      "dfql.query",
      "dfql.mutation",
      "dfql.transact",
      "dfql.sync",
      "dfql.seed"
    ],
    limits: {
      maxLimit: 100,
      maxTransactSteps: 100,
      maxPayloadBytes: 5242880
    },
    serverTimeMs: 1709251200000
  }
}

The serverTimeMs value is rounded to the nearest minute to prevent server fingerprinting. Returns HTTP 500 if the database health check fails.


POST /datafn/query

Execute a DFQL query against a resource. Supports single queries or batch queries (array).

Request (single):

{
  resource: "tasks",
  version: 1,
  select: ["id", "title", "status", "assignee.*"],
  filters: { status: { $eq: "active" } },
  sort: ["createdAt:desc", "id:asc"],
  limit: 25,
  offset: 0
}

Request (batch):

[
  { resource: "tasks", version: 1, filters: { status: { $eq: "active" } } },
  { resource: "users", version: 1, select: ["id", "name"] }
]

Response (single):

{
  ok: true,
  result: {
    data: [{ id: "task_1", title: "Write docs", status: "active" }],
    hasMore: false
  }
}

Response (batch):

{
  ok: true,
  result: [
    { data: [...], hasMore: false },
    { data: [...], hasMore: false }
  ]
}

POST /datafn/mutation

Execute a single DFQL mutation or a batch of mutations.

Request:

{
  resource: "tasks",
  version: 1,
  operation: "merge",
  id: "task_1",
  clientId: "client_abc",
  mutationId: "mut_001",
  record: {
    title: "Updated title",
    status: "done"
  }
}

Valid operations: insert, merge, replace, delete, relate, modifyRelation, unrelate.

Response:

{
  ok: true,
  result: {
    id: "task_1",
    serverSeq: 42
  }
}

The clientId and mutationId fields enable idempotency. Replaying the same mutation returns the cached result.


POST /datafn/transact

Execute multiple query and mutation steps atomically.

Request:

{
  steps: [
    {
      type: "mutation",
      mutation: {
        resource: "accounts",
        version: 1,
        operation: "merge",
        id: "acc_1",
        clientId: "c1",
        mutationId: "m1",
        record: { balance: 900 }
      }
    },
    {
      type: "mutation",
      mutation: {
        resource: "accounts",
        version: 1,
        operation: "merge",
        id: "acc_2",
        clientId: "c1",
        mutationId: "m2",
        record: { balance: 1100 }
      }
    }
  ]
}

Response:

{
  ok: true,
  result: {
    results: [
      { id: "acc_1", serverSeq: 43 },
      { id: "acc_2", serverSeq: 44 }
    ]
  }
}

The number of steps is bounded by limits.maxTransactSteps (default: 100).


POST /datafn/seed

Initialize a dataset for a client. Idempotent -- repeated calls with the same namespace return success without side effects.

Request:

{
  clientId: "client_abc"
}

Response:

{ ok: true, result: { ok: true } }

POST /datafn/clone

Clone the full dataset for initial sync. Returns all records and the current server sequence number.

Request:

{
  clientId: "client_abc"
}

Response:

{
  ok: true,
  result: {
    data: {
      tasks: [{ id: "task_1", title: "...", ... }],
      users: [{ id: "user_1", name: "...", ... }]
    },
    serverSeq: 42
  }
}

POST /datafn/pull

Pull changes since a given cursor. Returns incremental changes for the client to apply.

Request:

{
  clientId: "client_abc",
  cursor: "38"
}

Response:

{
  ok: true,
  result: {
    changes: [
      { resource: "tasks", id: "task_1", operation: "merge", record: { ... }, serverSeq: 39 },
      { resource: "tasks", id: "task_2", operation: "delete", serverSeq: 40 }
    ],
    cursor: "40",
    hasMore: false
  }
}

The number of changes returned per pull is bounded by limits.maxPullLimit (default: 1000). When hasMore is true, the client should pull again with the new cursor.


POST /datafn/push

Push local mutations from the client to the server. Mutations are validated, executed, and tracked for sync.

Request:

{
  clientId: "client_abc",
  mutations: [
    {
      resource: "tasks",
      version: 1,
      operation: "insert",
      id: "task_3",
      mutationId: "mut_003",
      record: { title: "New task", status: "pending" }
    }
  ]
}

Response:

{
  ok: true,
  result: {
    results: [{ id: "task_3", serverSeq: 43 }],
    cursor: "43"
  }
}

After a successful push, connected WebSocket clients in the same namespace receive a cursor notification.


POST /datafn/reconcile

Reconcile client state with the server. Used to resolve conflicts after offline operations.

Request:

{
  clientId: "client_abc",
  clientState: {
    tasks: [{ id: "task_1", version: 3 }]
  }
}

Response:

{
  ok: true,
  result: {
    resolved: [
      { resource: "tasks", id: "task_1", record: { ... }, serverSeq: 42 }
    ]
  }
}