Skip to Content
ReferenceClient APIMutationAdapter

MutationAdapter

Interface for handling client-to-server mutation requests (create, update, delete).

Import

import type { MutationAdapter } from '@gluonic/client'

Interface Definition

Exact definition from implementation:

export type MutationAdapter = { mutate: ( t: string, id: string, patch: Record<string, any>, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ) => Promise<void> create?: ( t: string, id: string, patch: Record<string, any>, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ) => Promise<void> delete?: ( t: string, id: string, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ) => Promise<void> }

Total: 3 methods (1 required, 2 optional). Default implementation sends HTTP requests to server sync endpoints.


Methods

mutate()

mutate( t: string, id: string, patch: Record<string, any>, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ): Promise<void>

Send mutation (update) to server.

Parameters:

ParameterTypeDescription
tstringModel type name
idstringModel ID
patchRecord<string, any>Fields to update
tokenstringJWT auth token
onUnauthenticated() => void | Promise<void>Callback if 401/403 response
clientTxIdstringOptional client transaction ID

Returns: Promise<void> - Resolves when server confirms

Example:

await mutationAdapter.mutate( 'task', 'abc-123', { title: 'Updated', done: true }, token, () => router.push('/login'), 'tx-001' )

create() (Optional)

create?( t: string, id: string, patch: Record<string, any>, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ): Promise<void>

Send create mutation to server.

Optional: If not provided, falls back to mutate().

Parameters: Same as mutate()

Example:

await mutationAdapter.create( 'task', 'abc-123', { title: 'New Task', done: false }, token, () => router.push('/login'), 'tx-002' )

delete() (Optional)

delete?( t: string, id: string, token: string, onUnauthenticated: () => void | Promise<void>, clientTxId?: string ): Promise<void>

Send delete mutation to server.

Optional: If not provided, falls back to mutate() with empty patch.

Parameters:

ParameterTypeDescription
tstringModel type name
idstringModel ID
tokenstringJWT auth token
onUnauthenticated() => void | Promise<void>Callback if 401/403 response
clientTxIdstringOptional client transaction ID

Example:

await mutationAdapter.delete( 'task', 'abc-123', token, () => router.push('/login'), 'tx-003' )

Default Implementation

The default HTTP-based implementation:

const defaultMutationAdapter: MutationAdapter = { async mutate(t, id, patch, token, onUnauthenticated, clientTxId) { const response = await fetch('/sync/v1/tx', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ ops: [{ t, id, op: 'u', patch, client_tx_id: clientTxId }] }) }) if (response.status === 401 || response.status === 403) { await onUnauthenticated() throw new Error('Unauthenticated') } if (!response.ok) { throw new Error(`Mutation failed: ${response.statusText}`) } } }

Custom Implementation

Implement custom mutation adapter for special use cases:

import type { MutationAdapter } from '@gluonic/client' class GraphQLMutationAdapter implements MutationAdapter { async mutate(t, id, patch, token, onUnauthenticated, clientTxId) { const mutation = ` mutation UpdateModel($type: String!, $id: String!, $patch: JSON!) { updateModel(type: $type, id: $id, patch: $patch) { success } } ` const response = await fetch('/graphql', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: mutation, variables: { type: t, id, patch } }) }) if (response.status === 401) { await onUnauthenticated() throw new Error('Unauthenticated') } const result = await response.json() if (!result.data?.updateModel?.success) { throw new Error('Mutation failed') } } } // Use in SyncClient const client = SyncClient({ server: 'https://api.example.com', storage, models, mutations: new GraphQLMutationAdapter() })

Batching Behavior

Mutations are automatically batched within the same tick:

// Multiple mutations await store.save('task', '1', { done: true }) await store.save('task', '2', { done: true }) await store.save('task', '3', { done: true }) // Single request sent: mutate(/* batched ops array */)

Error Handling

Authentication Errors

const mutationAdapter: MutationAdapter = { async mutate(t, id, patch, token, onUnauthenticated) { const response = await fetch('/sync/v1/tx', { headers: { 'Authorization': `Bearer ${token}` }, // ... }) // Handle 401/403 if (response.status === 401 || response.status === 403) { await onUnauthenticated() // Trigger logout/re-auth throw new Error('Unauthenticated') } } }

Network Errors

// Mutations automatically retry on network failure // Failed mutations stay in transaction queue // Retried when network returns

Use Cases

Custom Protocol

Implement for non-HTTP backends:

class WebSocketMutationAdapter implements MutationAdapter { async mutate(t, id, patch, token, onUnauthenticated, clientTxId) { // Send over WebSocket instead of HTTP ws.send(JSON.stringify({ type: 'mutation', t, id, patch, clientTxId })) // Wait for server confirmation await waitForAck(clientTxId) } }

Custom Authentication

class CustomAuthMutationAdapter implements MutationAdapter { async mutate(t, id, patch, token, onUnauthenticated) { const response = await fetch('/sync/v1/tx', { headers: { 'X-Custom-Auth': token, // Custom header 'X-API-Key': process.env.API_KEY }, // ... }) } }

See Also

  • Store - Uses MutationAdapter for mutations
  • SyncClient - Configure custom mutation adapter
  • SyncFrame - Frame format sent to server
Last updated on