v1.0 now available

Type-safe Server Actions
for Next.js

A cleaner, type-safe way to create Next.js server actions with built-in Zod schema validation and middleware support.

npm install next-kitte
actions.tstypescript
1import { Kitte } from "next-kitte"
2import { z } from "zod"
3
4const createUser = new Kitte()
5  .schema(z.object({ name: z.string(), email: z.string().email() }))
6  .use(async ({ ctx }) => ({ user: await getUser() }))
7  .action(async ({ input, ctx }) => {
8    return { success: true, user: ctx.user }
9  })

Everything you need for server actions

A clean, chainable API that makes working with Next.js server actions a breeze.

Type-safe Actions

Full TypeScript support with automatic type inference. Your inputs and outputs are always type-safe.

Zod Validation

Built-in schema validation using Zod. Validate client inputs before they reach your action handler.

Middleware Support

Chain middleware functions to add context like authentication, permissions, or database connections.

React Hook

useKitteAction hook for easy client-side execution with loading states, error handling, and callbacks.

Quick Start

Get up and running with next-kitte in under a minute.

1

Install the package

npm install next-kitte zod
2

Create your first action

actions.tstypescript
1// 1. Create your first action
2// actions.ts
3import { Kitte } from "next-kitte"
4import { z } from "zod"
5
6const helloAction = new Kitte()
7  .schema(z.object({ name: z.string() }))
8  .action(async ({ input }) => {
9    return { message: `Hello, ${input.name}!` }
10  })
11
12export { helloAction }
3

Use it in a component

components/hello-form.tsxtypescript
1// 2. Use it in a component
2// components/hello-form.tsx
3"use client"
4
5import { useKitteAction } from "next-kitte"
6import { helloAction } from "../actions"
7
8export function HelloForm() {
9  const { execute, data, status } = useKitteAction(helloAction)
10
11  return (
12    <form action={async (formData) => {
13      await execute({ name: formData.get("name") as string })
14    }}>
15      <input name="name" placeholder="Your name" />
16      <button type="submit">
17        {status === "loading" ? "..." : "Say Hello"}
18      </button>
19      {data && <p>{data.message}</p>}
20    </form>
21  )
22}

API Reference

Complete API documentation for next-kitte.

classKitte

The core class for defining server actions with validation and middleware.

1const action = new Kitte()
2  .schema(zodSchema)   // Optional: Zod schema for input validation
3  .use(middleware)     // Optional: Add context middleware
4  .action(handler)     // Required: Define the action handler

Methods

new Kitte(schema?)

Creates a new Kitte instance. Optionally accepts a Zod schema for input validation.

.schema(schema)

Sets or updates the Zod schema for input validation. Returns the Kitte instance for chaining.

.use(middlewareFn)

Adds middleware that receives { input, ctx } and returns { ctx: TNewCtx }. Can be chained multiple times.

.action(handlerFn)

Defines the action handler. Receives { input, ctx } with all middleware context merged.

hookuseKitteAction

React hook for executing server actions on the client with loading states and callbacks.

1const {
2  data,    // TOutput | null - the action result on success
3  status,  // "idle" | "loading" | "success" | "error"
4  error,   // unknown | null - the error on failure
5  execute, // (input: TInput) => Promise<ActionResult<TOutput>>
6} = useKitteAction(action, {
7  onSuccess?: (data) => void,
8  onError?: (error) => void,
9})

typesType Definitions

Core types used throughout next-kitte.

1// Return type for all actions
2type ActionResult<T> = [T, null] | [null, PossibleError]
3
4// Parameter passed to middleware and handlers
5type Params<Schema, TCtx> = { 
6  input: z.infer<Schema>, 
7  ctx: TCtx 
8}
9
10// Possible error types
11type PossibleError = Error | ZodError

Examples

Common patterns and use cases for next-kitte.

validation.tstypescript
const validatedAction = new Kitte()
  .schema(z.object({
    email: z.string().email(),
    password: z.string().min(8),
  }))
  .action(async ({ input }) => {
    // input is type-safe and validated
    // { email: string, password: string }
    return { success: true }
  })