Command Palette

Search for a command to run...

Mutations

Create, update, and delete entities from the server client (createPyloServer / createPyloNode) or from Client Components with the hooks.

Server

Upsert

Create or update with the __search_value pattern. Pylo updates the record that matches the search, or creates one when nothing matches.

example.ts
1 2 3 4 5 6 7 8 9 10 11 const result = await pylo.invoice.upsert({ __search_value: { field: 'invoice_number', value: 'INV-001', not_found_behavior: 'create', }, invoice_number: 'INV-001', amount_net: 1000, }); // result: { id: string }

With relations

Use _set to connect related entities during upsert:

example.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 await pylo.invoice.upsert({ __search_value: { field: 'invoice_number', value: 'INV-001', not_found_behavior: 'create', }, invoice_number: 'INV-001', amount_net: 1500, customer_set: { __search_value: { field: 'customer_number', value: 'C-001', not_found_behavior: 'create', }, name: 'Acme Corp', }, });

Search values

__search_value targets the record by a field instead of an id. It works at the top level and inside nested relation upserts (as in the example above).

OptionDefaultDescription
fieldrequiredField to search by
valueValue to match
not_found_behavior'create''create' inserts a new record, 'error' fails, 'ignore' skips (nested relation upserts only)
search_in_all_field_variantsfalseSearch across all variants of the field instead of only the requested ones
multiple_results_allowedfalseAllow more than one match; without it, multiple matches fail the mutation
multiple_results_use_latesttrueWith multiple matches allowed, use the newest record (false uses the oldest)

Bulk upsert

Upserts many rows in one all-or-nothing transaction. Rows without an id or __search_value are created. The ids come back in input order.

example.ts
1 2 3 4 5 6 const results = await pylo.invoice.bulkUpsert([ { invoice_number: 'INV-002', amount_net: 500 }, { id: 'invoice-uuid', amount_net: 750 }, ]); // results: { id: string }[]

Delete

example.ts
1 2 const result = await pylo.invoice.delete(['invoice-uuid-1', 'invoice-uuid-2']); // result: { success: boolean }

Client hooks

usePyloUpsert

Invalidates the entity's list queries on success.

example.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 'use client'; import { pylo } from "@/lib/pylo-hooks"; const { mutateAsync } = pylo.usePyloUpsert('invoice'); await mutateAsync({ __search_value: { field: 'invoice_number', value: 'INV-001', not_found_behavior: 'create', }, invoice_number: 'INV-001', amount_net: 1000, });

usePyloBulkUpsert works the same way with an array input.

usePyloDelete

example.ts
1 2 3 const { mutateAsync } = pylo.usePyloDelete('invoice'); await mutateAsync(['invoice-uuid-1']);

Query invalidation

Mutation hooks invalidate all queries for the same entity, so list and detail views refetch. Query key structure:

  • Lists: ['pylo', entityName, 'list', options]
  • Infinite lists: ['pylo', entityName, 'infiniteList', options]
  • By ID: ['pylo', entityName, 'byId', id, options]

Mutations invalidate the prefix ['pylo', entityName], which covers all of the above.