---
title: "Pylon Server Functions: typed queries, mutations, and actions"
description: "Write backend logic in TypeScript with v.* validators. Filename is the RPC name. ctx.db honors policies, writes broadcast to live queries automatically."
url: "https://www.pylonsync.com/product/functions"
---

Server functions

# Backend logic in TypeScript.

Write queries, mutations, and actions with v.\* validators. The filename is the RPC name. Call them from React with a typed client, and writes broadcast through the sync engine automatically.

[Create your account →](https://cloud.stack0.dev/signup)[Read the docs](https://docs.pylonsync.com)

functions/sendMessage.ts

```


import { mutation, v } from "@pylonsync/functions";

 

export default mutation({

  args: { roomId: v.id("Room"), body: v.string() },

  async handler(ctx, args) {

    if (!ctx.auth.userId) throw ctx.error("UNAUTHENTICATED", "log in first");

    const id = await ctx.db.insert("Message", {

      roomId: args.roomId,

      authorId: ctx.auth.userId,

      body: args.body.trim(),

      createdAt: new Date().toISOString(),

    });

    return ctx.db.get("Message", id);   // broadcasts to subscribers

  },

});

```

- query / mutation / action with validated, typed arguments
- The filename is the RPC name: functions/sendMessage.ts is callable as sendMessage
- ctx.db honors policies; ctx.auth carries the caller; ctx.error throws typed failures
- Writes emit change events, so live queries update with zero extra wiring
- Test logic and components with \`pylon test\`, Bun, and @testing-library

## How it works.

01Validated arguments, end to end02The context object is the whole backend03Writes are live by default04Test it without standing anything up

server functions

getOrdersquery4ms

createOrdermutation11ms

sendReceiptaction62ms

v.\* validated · typed end to end

v.string(), v.id("Room"), v.object({…}) describe the shape once. Pylon validates inbound calls and infers the handler's argument types, turning a mistyped field into a local type error instead of a production 500.

## What you’d otherwise wire up.

Each line is a piece teams normally assemble for this one capability. On Pylon it is part of the runtime.

- An RPC or REST layer and the client that calls it
- Request validation written out per endpoint
- A generated API client you re-sync after every change
- A separate serverless target to deploy backend logic to

## Apps built with it.

Scaffold any of these with `npm create @pylonsync/pylon`.

[All examples →](/developers/examples)

SaaS starter

A complete SaaS product with a marketing site, onboarding, a multi-tenant dashboard, and Stripe billing. Reach for this one to build a product.

Stripe billingMarketing + dashboardOnboarding

template: saas

Barebones

One entity, one function, one page. Start from a clean slate.

SchemaOne functionSSR page

template: barebones

Shop

A DTC store with a product grid, live inventory, a cart, and real Stripe checkout with a no-key fallback.

Stripe checkoutLive inventoryCart

template: shop

Keep exploring

[Database

Typed schema. Migrations on save.](/product/database)

[Auth & policies

Auth and access rules, built in.](/product/auth)

[Workflows & jobs

Durable workflows and background jobs.](/product/workflows)

## Build it on Pylon.

One framework for your schema, sync, auth, functions, realtime, and SSR. Free to start.

[Create your account →](https://cloud.stack0.dev/signup)[Read the docs](https://docs.pylonsync.com)
