CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tanstack--react-start

Modern full-stack React framework with SSR, streaming, server functions, and API routes powered by TanStack Router and Vite.

Pending
Overview
Eval results
Files

rpc-system.mddocs/

RPC System

The RPC (Remote Procedure Call) system provides type-safe client-server communication with automatic serialization and request handling. It enables seamless function calls between client and server environments with full type safety and error handling.

Capabilities

Create Client RPC

Creates client-side RPC functions that can communicate with server-side endpoints.

/**
 * Creates a client-side RPC function for server communication
 * @param functionId - Unique identifier for the RPC function
 * @param fetcher - Function that handles the actual HTTP request
 * @returns ClientRpc instance for making server calls
 */
function createClientRpc(
  functionId: string,
  fetcher: (...args: any[]) => Promise<any>
): ClientRpc;

interface ClientRpc {
  (...args: any[]): Promise<any>;
  functionId: string;
  url: string;
}

Usage Examples:

import { createClientRpc } from "@tanstack/react-start/client-rpc";

// Basic RPC function
const getUserRpc = createClientRpc(
  "getUser",
  async (id: string) => {
    const response = await fetch(`/api/functions/getUser`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ id })
    });
    return response.json();
  }
);

// Use in React component
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    getUserRpc(userId).then(setUser);
  }, [userId]);

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

// Advanced RPC with error handling
const createUserRpc = createClientRpc(
  "createUser",
  async (userData: { name: string; email: string }) => {
    try {
      const response = await fetch("/api/functions/createUser", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(userData)
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      return response.json();
    } catch (error) {
      console.error("RPC call failed:", error);
      throw error;
    }
  }
);

Create Server RPC

Creates server-side RPC functions that can be called from client code.

/**
 * Creates a server-side RPC function with split import functionality
 * @param functionId - Unique identifier for the RPC function
 * @param splitImportFn - Function that handles dynamic imports and execution
 * @returns ServerRpc instance with URL and function metadata
 */
function createServerRpc(
  functionId: string,
  splitImportFn: (...args: any[]) => any
): ServerRpc;

interface ServerRpc {
  (...args: any[]): any;
  url: string;
  functionId: string;
  [TSS_SERVER_FUNCTION]: true;
}

Usage Examples:

import { createServerRpc } from "@tanstack/react-start/server-rpc";

// Basic server RPC
const getUserServerRpc = createServerRpc(
  "getUser",
  async (id: string) => {
    // Dynamic import for server-only code
    const { db } = await import("./database");
    return db.user.findUnique({ where: { id } });
  }
);

// RPC with complex business logic
const processOrderServerRpc = createServerRpc(
  "processOrder",
  async (orderData: {
    items: Array<{ id: string; quantity: number }>;
    customerId: string;
    paymentMethod: string;
  }) => {
    // Split imports for server-only dependencies
    const [
      { db },
      { paymentProcessor },
      { inventoryManager },
      { emailService }
    ] = await Promise.all([
      import("./database"),
      import("./payment"),
      import("./inventory"),
      import("./email")
    ]);

    // Process order with full server-side logic
    const order = await db.order.create({
      data: {
        customerId: orderData.customerId,
        items: {
          create: orderData.items.map(item => ({
            productId: item.id,
            quantity: item.quantity
          }))
        }
      }
    });

    // Process payment
    const payment = await paymentProcessor.charge({
      amount: order.total,
      method: orderData.paymentMethod,
      customerId: orderData.customerId
    });

    // Update inventory
    await inventoryManager.decrementStock(orderData.items);

    // Send confirmation email
    await emailService.sendOrderConfirmation(orderData.customerId, order);

    return { order, payment };
  }
);

Server Function Fetcher

Utilities for handling server function requests and responses.

/**
 * Server function fetcher for handling RPC requests
 * @param url - The URL to fetch from
 * @param options - Fetch options including method and body
 * @returns Promise resolving to the response
 */
interface ServerFnFetcher {
  (url: string, options: ServerFnFetchOptions): Promise<Response>;
}

interface ServerFnFetchOptions {
  method: string;
  headers?: HeadersInit;
  body?: BodyInit;
}

RPC Configuration and Environment

Environment Variables

The RPC system uses environment variables for configuration:

// Server configuration
process.env.TSS_APP_BASE = "/"; // Base path for the application
process.env.TSS_SERVER_FN_BASE = "api/functions"; // Base path for server functions

URL Generation

RPC functions automatically generate URLs based on configuration:

// Generated URL structure
const baseUrl = `${TSS_APP_BASE}/${TSS_SERVER_FN_BASE}/`;
const functionUrl = baseUrl + functionId;

// Example: /api/functions/getUser

Advanced Usage Patterns

RPC with Authentication

import { createClientRpc } from "@tanstack/react-start/client-rpc";

// Authenticated RPC calls
const createAuthenticatedRpc = (functionId: string) =>
  createClientRpc(functionId, async (...args) => {
    const token = localStorage.getItem("auth-token");

    const response = await fetch(`/api/functions/${functionId}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${token}`
      },
      body: JSON.stringify(args)
    });

    if (response.status === 401) {
      // Handle authentication error
      window.location.href = "/login";
      throw new Error("Authentication required");
    }

    return response.json();
  });

const getUserRpc = createAuthenticatedRpc("getUser");
const updateUserRpc = createAuthenticatedRpc("updateUser");

RPC with Caching

import { createClientRpc } from "@tanstack/react-start/client-rpc";

// Cached RPC calls
const cache = new Map();

const createCachedRpc = (functionId: string, cacheTTL = 5 * 60 * 1000) =>
  createClientRpc(functionId, async (...args) => {
    const cacheKey = `${functionId}:${JSON.stringify(args)}`;
    const cached = cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < cacheTTL) {
      return cached.data;
    }

    const response = await fetch(`/api/functions/${functionId}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(args)
    });

    const data = await response.json();
    cache.set(cacheKey, { data, timestamp: Date.now() });

    return data;
  });

const getUserRpc = createCachedRpc("getUser", 10 * 60 * 1000); // 10 minute cache

Server RPC with Validation

import { createServerRpc } from "@tanstack/react-start/server-rpc";

const validateUserInput = (data: unknown) => {
  if (!data || typeof data !== "object") {
    throw new Error("Invalid input");
  }

  const { name, email } = data as any;
  if (!name || typeof name !== "string") {
    throw new Error("Name is required");
  }
  if (!email || !email.includes("@")) {
    throw new Error("Valid email is required");
  }

  return { name, email };
};

const createUserServerRpc = createServerRpc(
  "createUser",
  async (input: unknown) => {
    // Validate input
    const userData = validateUserInput(input);

    // Import server dependencies
    const { db } = await import("./database");
    const { hashPassword } = await import("./crypto");
    const { sendWelcomeEmail } = await import("./email");

    // Create user with validated data
    const user = await db.user.create({
      data: {
        name: userData.name,
        email: userData.email,
        passwordHash: await hashPassword("temporary")
      }
    });

    // Send welcome email asynchronously
    sendWelcomeEmail(user.email, user.name).catch(console.error);

    return { user: { id: user.id, name: user.name, email: user.email } };
  }
);

RPC Error Handling

import { createClientRpc } from "@tanstack/react-start/client-rpc";

const createRobustRpc = (functionId: string, maxRetries = 3) =>
  createClientRpc(functionId, async (...args) => {
    let lastError;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const response = await fetch(`/api/functions/${functionId}`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(args)
        });

        if (!response.ok) {
          if (response.status >= 500 && attempt < maxRetries) {
            // Retry on server errors
            await new Promise(resolve =>
              setTimeout(resolve, Math.pow(2, attempt) * 1000)
            );
            continue;
          }

          const errorData = await response.json().catch(() => ({}));
          throw new Error(errorData.message || `HTTP ${response.status}`);
        }

        return response.json();
      } catch (error) {
        lastError = error;
        if (attempt === maxRetries) break;

        // Exponential backoff for retries
        await new Promise(resolve =>
          setTimeout(resolve, Math.pow(2, attempt) * 1000)
        );
      }
    }

    throw lastError;
  });

const getUserRpc = createRobustRpc("getUser");

Types

// RPC function types
interface ClientRpc {
  (...args: any[]): Promise<any>;
  functionId: string;
  url: string;
}

interface ServerRpc {
  (...args: any[]): any;
  url: string;
  functionId: string;
  [TSS_SERVER_FUNCTION]: true;
}

// Server function marker
declare const TSS_SERVER_FUNCTION: unique symbol;

// Fetch options for server functions
interface ServerFnFetchOptions {
  method: string;
  headers?: HeadersInit;
  body?: BodyInit;
}

// Server function fetcher type
interface ServerFnFetcher {
  (url: string, options: ServerFnFetchOptions): Promise<Response>;
}

// RPC stream types
interface RscStream {
  // React Server Component stream handling
  read(): Promise<any>;
  pipe(destination: any): any;
}

// Fetcher data types
interface FetcherData {
  url: string;
  method: string;
  headers: Headers;
  body?: any;
}

interface FetcherBaseOptions {
  method?: string;
  headers?: HeadersInit;
}

// Compiled fetcher types
interface CompiledFetcherFnOptions {
  method: string;
  headers?: HeadersInit;
  body?: BodyInit;
}

interface CompiledFetcherFn {
  (url: string, options: CompiledFetcherFnOptions): Promise<Response>;
}

interface Fetcher {
  fn: CompiledFetcherFn;
  options: CompiledFetcherFnOptions;
}

// Optional and required fetcher types
interface OptionalFetcher {
  fetcher?: Fetcher;
}

interface RequiredFetcher {
  fetcher: Fetcher;
}

Install with Tessl CLI

npx tessl i tessl/npm-tanstack--react-start

docs

index.md

isomorphic-functions.md

middleware.md

request-response.md

rpc-system.md

server-functions.md

server-utilities.md

ssr-components.md

vite-plugin.md

tile.json