actions

Call server code straight from a component, like a form submit, without writing an API route.

BackendClient and ServerBuilt onTanStack Start

What it is

An action is a server function you define with defineQuery or defineMutation and call from any component by name. kitr sends the request, validates the input with your schema and returns the result or a typed error.

Use actions when only your own app calls the code. For mobile apps, partners or webhooks, use API routes.

Install

kitr add actions

First time? Run npm i -g @kitr/kitr once. Inside a project, the global kitr runs your project's own version.

Adds @kitr/actions and app/actions/, and runs kitr types once. Adds server and query first if missing. Every change.

Quick start

Define a mutation with its input schema and handler:

// app/actions/projects/rename.ts
import { defineMutation } from '@kitr/actions'
import { z } from 'zod'

export const renameProject = defineMutation('projects.rename', {
  input: z.object({ id: z.string(), name: z.string().min(2) }),
  effects: (project) => ({
    set: [{ key: ['projects', 'detail', project.id], data: project }],
    invalidate: [['projects', 'list']],
  }),
  handler: async ({ input }) => {
    // Update the project in your database and return it.
    return { id: input.id, name: input.name }
  },
})

Call it from a component by name:

// app/components/rename-button.tsx
import { useMutation } from '@kitr/query'

export function RenameButton({ id }: { id: string }) {
  const rename = useMutation('projects.rename')
  return (
    <button disabled={rename.isPending} onClick={() => rename.mutate({ id, name: 'Acme site' })}>
      Rename
    </button>
  )
}

One click sends one request. The server validates, runs the handler and returns the project; the cache effects update every screen that shows it. A missing name is a type error. Next in the guide: Your first mutation.

Built on

kitr adds four things on top:

What kitr adds Why
Input validation with any Standard Schema library (zod, valibot, arktype). The server never trusts the browser, and the schema is also the TypeScript type.
kitr's one error shape, KitrError. A validation failure lands on the right form field in forms, and every other failure looks the same as it does from api or http.
Cache effects on mutations. A mutation declares which query keys it changes, and every screen showing them updates.
A generated registry. useQuery('projects.list') is typed from the action's name.

kitr adds the input schema, the typed error, the generated registry and the cache effects.

Problems it solves

Further reading