Skip to content

tRPC Integration Guide

Fast-Crawl now includes full tRPC integration for end-to-end type safety, while maintaining backward compatibility with the existing REST API.

Overview

The server exposes both:

  • tRPC API at /trpc - Type-safe, schema-validated endpoints
  • REST API at /v1/* - Legacy endpoints for backward compatibility

tRPC API Endpoints

Available Procedures

  1. health.query() - Health check
  2. search.mutation(input) - Search aggregation
  3. results.mutation(input) - Search results only (no metadata)
  4. scrap.mutation(input) - URL content scraping

Client Setup

1. Install Dependencies

bash
npm install @trpc/client @trpc/server zod

2. Create Type-Safe Client

typescript
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import type { AppRouter } from "./src/client-types";

const client = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: "http://localhost:3000/trpc",
      headers: {
        "x-api-key": "your-api-key", // if API_KEY is set
      },
    }),
  ],
});

3. Type-Safe API Calls

typescript
// Health check
const health = await client.health.query();
// Type: { status: 'ok', version: string }

// Search with full type safety
const searchResults = await client.search.mutate({
  query: "typescript tutorial",
  providers: ["google", "bing"], // Strongly typed
  count: 10,
  scrape: true,
  rerank: false,
});
// Type: SearchOutputType with full schema validation

// Get results only (no metadata)
const results = await client.results.mutate({
  query: "nodejs best practices",
  count: 5,
});
// Type: SearchResultType[]

// Scrape URLs
const scrapedContent = await client.scrap.mutate({
  urls: ["https://example.com", "https://github.com"],
});
// Type: ScrapOutputType

Input Validation

All inputs are automatically validated using Zod schemas:

typescript
// This will throw a validation error
await client.search.mutate({
  query: "", // Error: Query cannot be empty
  count: 150, // Error: Must be max 100
  providers: ["invalid"], // Error: Must be 'google' or 'bing'
});

React/Vue Integration

React with @tanstack/react-query

typescript
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from './client-types';

const trpc = createTRPCReact<AppRouter>();

function SearchComponent() {
  const searchMutation = trpc.search.useMutation();

  const handleSearch = async () => {
    const result = await searchMutation.mutateAsync({
      query: 'react hooks',
      count: 10,
    });
    // Fully typed result
  };

  return (
    <div>
      <button onClick={handleSearch}>Search</button>
      {searchMutation.data?.results.map(result => (
        <div key={result.link}>
          <h3>{result.title}</h3>
          <p>{result.snippet}</p>
        </div>
      ))}
    </div>
  );
}

Vue with @trpc/vue-query

vue
<script setup lang="ts">
import { trpc } from "./trpc-client";

const { mutate: search, data: searchResults } = trpc.search.useMutation();

const handleSearch = () => {
  search({
    query: "vue composition api",
    providers: ["google"],
    count: 5,
  });
};
</script>

<template>
  <div>
    <button @click="handleSearch">Search</button>
    <div v-for="result in searchResults?.results" :key="result.link">
      <h3>{{ result.title }}</h3>
      <p>{{ result.snippet }}</p>
    </div>
  </div>
</template>

Benefits of tRPC Integration

  1. End-to-End Type Safety: Types are shared between client and server
  2. Automatic Validation: Input/output validation with Zod schemas
  3. Better DX: IDE autocompletion and error detection
  4. Performance: Efficient serialization and batching
  5. Framework Agnostic: Works with React, Vue, Svelte, etc.

Migration from REST API

The REST API endpoints remain unchanged and fully functional:

typescript
// REST API (still works)
const response = await fetch("/v1/search?q=test");
const data = await response.json();

// tRPC API (type-safe)
const data = await client.search.mutate({ query: "test" });

You can migrate gradually by:

  1. Setting up the tRPC client
  2. Converting endpoints one by one
  3. Removing REST calls when ready

Error Handling

tRPC provides structured error handling:

typescript
try {
  const result = await client.search.mutate({
    query: "test query",
  });
} catch (error) {
  if (error instanceof TRPCError) {
    console.log("Validation error:", error.message);
    console.log("Error code:", error.code);
  }
}

API Documentation

The tRPC procedures map to the existing API functionality:

tRPC ProcedureREST EndpointDescription
health.query()GET /v1/healthServer health check
search.mutation()GET/POST /v1/searchSearch with metadata
results.mutation()POST /v1/resultsSearch results only
scrap.mutation()POST /v1/scrapURL content scraping

All procedures maintain the same functionality and response structure as their REST counterparts.