oakoss/open-saas-kit · Archived

server-functions

Use createServerFn for RPC-style functions. Use when writing server functions with validators, middleware chains, or form action handlers.

First seen Aug 23, 2026

Installation

$ npx skills add oakoss/open-saas-kit --skill server-functions

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from oakoss/open-saas-kit · top by installs.

npx skills add oakoss/open-saas-kit

Browse all from oakoss/open-saas-kit

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 1
License LICENSE
Default branch main
Open issues 1
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,380 B
  • docs SUMMARY.md 162 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Server Functions

Quick Start

import { createServerFn } from '@tanstack/react-start';
import { z } from 'zod';
import { db } from '@oakoss/database';

const getUsers = createServerFn({ method: 'GET' }).handler(async () => {
  return await db.query.users.findMany();
});

const createUser = createServerFn({ method: 'POST' })
  .inputValidator(z.object({ name: z.string().min(1), email: z.email() }))
  .handler(async ({ data }) => {
    const [user] = await db.insert(users).values(data).returning();
    return user;
  });

Pattern Overview

Pattern Use Case Method
Data fetching Route loaders, queries GET
Form submission Create/update operations POST
Authenticated action Protected mutations POST + auth check

GET Functions

import { db, eq } from '@oakoss/database';
import { posts } from '@oakoss/database/schema';

const getAllPosts = createServerFn({ method: 'GET' }).handler(async () => {
  return await db.query.posts.findMany();
});

const getPost = createServerFn({ method: 'GET' })
  .inputValidator(z.object({ id: z.string() }))
  .handler(async ({ data }) => {
    return await db.query.posts.findFirst({
      where: eq(posts.id, data.id),
    });
  });

export const Route = createFileRoute('/posts/$id')({
  loader: async ({ params }) => await getPost({ data: { id: params.id } }),
});

POST Functions

const createPost = createServerFn({ method: 'POST' })
  .inputValidator(
    z.object({
      title: z.string().min(1),
      content: z.string(),
    }),
  )
  .handler(async ({ data }) => {
    const [post] = await db.insert(posts).values(data).returning();
    return post;
  });

const handleSubmit = async (formData: FormData) => {
  const result = await createPost({
    data: { title: formData.get('title'), content: formData.get('content') },
  });
};

Authenticated Functions

import { auth } from '@oakoss/auth/server';
import { db, eq } from '@oakoss/database';
import { posts } from '@oakoss/database/schema';

const deletePost = createServerFn({ method: 'POST' })
  .inputValidator(z.object({ id: z.string() }))
  .handler(async ({ data, request }) => {
    // Get session from request headers
    const session = await auth.api.getSession({ headers: request.headers });

    if (!session) {
      return { error: 'Authentication required', code: 'AUTH_REQUIRED' };
    }

    // Check ownership
    const post = await db.query.posts.findFirst({
      where: eq(posts.id, data.id),
    });

    if (post?.authorId !== session.user.id) {
      return { error: 'Not authorized', code: 'FORBIDDEN' };
    }

    await db.delete(posts).where(eq(posts.id, data.id));
    return { success: true };
  });

Error Handling

Standard error response format:

type ApiResult<T> = { data: T } | { error: string; code: string };

const updateUser = createServerFn({ method: 'POST' })
  .inputValidator(schema)
  .handler(async ({ data, request }): Promise<ApiResult<User>> => {
    try {
      const [user] = await db.update(users).set(data).returning();
      return { data: user };
    } catch (error) {
      console.error('updateUser failed:', error);
      return { error: 'Update failed', code: 'INTERNAL_ERROR' };
    }
  });

Request Context

Access request details:

const serverFn = createServerFn({ method: 'POST' }).handler(
  async ({ request }) => {
    // Headers
    const authHeader = request.headers.get('Authorization');

    // URL info
    const url = new URL(request.url);

    // Cookies (via Better Auth)
    const session = await auth.api.getSession({ headers: request.headers });
  },
);

Common Mistakes

Mistake Correct Pattern
Missing auth check Always verify session from request headers
Throwing instead of returning error Return { error, code } format
Not validating input Always use .inputValidator(schema)
Using deprecated Zod v3 syntax Use z.email() not z.string().email()
Missing error code Return { error: 'message', code: 'ERROR_CODE' }
Not logging server errors Use console.error() before returning
Returning raw DB errors Catch and return user-friendly messages
Using GET for mutations Use POST for create/update/delete

Delegation

  • Complex queries: For database patterns, see database skill
  • Auth patterns: For authentication, see auth skill
  • API routes: For REST endpoints, see tanstack-start skill
  • Code review: After creating server functions, delegate to code-reviewer agent

References

  • Middleware patterns: [reference.md](reference.md)
  • File uploads: [reference.md](reference.md)
  • Streaming: [reference.md](reference.md)