Skip to content

y7ya-com/trpc-server-functions

Repository files navigation

tRPC Server Functions

trpc-server-functions brings co-located server functions to tRPC + Vite apps.

Define a server function anywhere in your frontend, and generate matching tRPC procedures for your backend router.

Expected Structure

This package is designed for a split setup: a Vite client app and a separate server app.

your-app/
  client/
    src/
      App.tsx
      main.tsx
    vite.config.ts
  server/
    src/
      db.ts
      trpc.ts
      router.ts
      generated/
        trpc-server-functions.ts

The generated registry is written by the client-side Vite plugin or by the CLI.

1-File Counter Example

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

import { db } from "../../server/src/db";
import { createServerFn } from "trpc-server-functions";

export const getCount = createServerFn().query(async () => {
  return db.getCount();
});

export const incrementCount = createServerFn().mutation(async () => {
  return db.increment();
});

export function Counter() {
  const queryClient = useQueryClient();
  const countQuery = useQuery(getCount.queryOptions());
  const incrementMutation = useMutation({
    ...incrementCount.mutationOptions(),
    onSuccess: async () => {
      await queryClient.invalidateQueries({
        queryKey: getCount.queryOptions().queryKey,
      });
    },
  });

  return (
    <main>
      <h1>Counter</h1>
      <p>{countQuery.data ?? "..."}</p>
      <button
        type="button"
        disabled={incrementMutation.isPending}
        onClick={() => incrementMutation.mutate(undefined)}
      >
        {incrementMutation.isPending ? "Incrementing..." : "Increment"}
      </button>
    </main>
  );
}

At build time:

  • the client keeps typed RPC proxies
  • the real handlers are removed from the browser bundle
  • generated server modules turn these exports into normal tRPC procedures

Setup

Install the package:

npm install trpc-server-functions

1. Add the Vite plugin

import path from "node:path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

import { trpcServerFunctionsPlugin } from "trpc-server-functions/vite";

export default defineConfig({
  plugins: [
    react(),
    trpcServerFunctionsPlugin({
      procedure: {
        importPath: path.resolve("../server/src/trpc.ts"),
        exportName: "publicProcedure",
      },
      generatedModulePath: "../server/src/generated/trpc-server-functions.ts",
    }),
  ],
});

2. Generate the server module once

trpc-server-functions generate \
  --root ./client \
  --generated-module-path ../server/src/generated/trpc-server-functions.ts \
  --procedure-import-path ../server/src/trpc.ts \
  --procedure-export-name publicProcedure

3. Use the generated module in the server router

import { trpcServerFunctions } from "./generated/trpc-server-functions";

import { router } from "./trpc";

export const appRouter = router({
  ...trpcServerFunctions(),
});

4. Connect the client transport

import { createTRPCUntypedClient, httpBatchLink } from "@trpc/client";
import {
  createTRPCClientTransport,
  setServerFnTransport,
} from "trpc-server-functions";

const trpcClient = createTRPCUntypedClient({
  links: [httpBatchLink({ url: "/api/trpc" })],
});

setServerFnTransport(createTRPCClientTransport(trpcClient));

After that, queryOptions(), mutationOptions(), and call() use your existing tRPC endpoint.

How Generation Works

The generator produces two kinds of output:

  • server/src/generated/trpc-server-functions.ts
    • the registry that imports extracted server functions and returns a tRPC procedure record
  • server/src/generated/__trpc_server_functions__/src/...
    • mirrored backend-safe extracted modules for the colocated server-function exports found in the client app

The mirrored lib and routes folders are expected. They preserve the source layout of your client app so the backend can execute colocated handlers without importing raw route/component modules directly.

Public Procedure Keys

The public tRPC paths generated by this package are stable hash-only route keys, for example:

/api/trpc/sf_a19175ccef5b6fcd5059e41b

They are derived from the source file path and export name, but the public API surface does not expose those source paths directly.

Internally, the generated manifest still keeps metadata such as relativePath and exportName so the build can map each hashed key back to the correct server-function export.

Colocation Requirements

  • Server functions must be exported.
  • The plugin only discovers top-level exported createServerFn(...) calls.
  • Keep colocated server functions in the files where they are used; the generated backend modules are build artifacts and should not be edited manually.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors