Skip to content
GitHubXDiscordRSS

AI Search

Learn how to deploy a knowledge base with Cloudflare AI Search and a Worker using Alchemy.

This guide walks you through creating an AI Search knowledge base with a Cloudflare Worker that provides semantic search and RAG-powered chat completions.

  1. Install Dependencies

    Terminal window
    bun add alchemy
  2. Credentials

    Set your Cloudflare API token in your environment:

    Terminal window
    export CLOUDFLARE_API_TOKEN=your-api-token

    You can create a user API token at dash.cloudflare.com/profile/api-tokens with the AI Search Edit and Workers Scripts Edit permissions. (This is the token Alchemy uses to manage AI Search instances; it is separate from the service token Cloudflare provisions internally to grant AI Search access to an R2 bucket — see Service Token in the AiSearch reference.)

  3. Create alchemy.run.ts

    Create a deployment script that provisions an AI Search instance and a Worker:

    alchemy.run.ts
    import alchemy from "alchemy";
    import { AiSearch, AiSearchNamespace, Worker } from "alchemy/cloudflare";
    const app = await alchemy("ai-search-app");
    // Create a namespace for organizing instances
    const ns = await AiSearchNamespace("docs", {
    name: "my-docs",
    adopt: true,
    });
    // Create an AI Search instance (built-in storage, no R2 needed)
    const search = await AiSearch("knowledge-base", {
    name: "knowledge-base",
    adopt: true,
    });
    // Deploy a Worker with both binding types.
    export const worker = await Worker("api", {
    entrypoint: "./src/worker.ts",
    url: true,
    bindings: {
    SEARCH: search, // Single instance binding
    DOCS: ns, // Namespace binding for dynamic access
    },
    });
    console.log({ url: worker.url });
    await app.finalize();
  4. Implement the Worker

    Create a Worker that handles search queries, file uploads, and chat completions:

    src/worker.ts
    import type { worker } from "../alchemy.run";
    export default {
    async fetch(request: Request, env: typeof worker.Env): Promise<Response> {
    const url = new URL(request.url);
    // Upload a document
    if (url.pathname === "/upload" && request.method === "POST") {
    const formData = await request.formData();
    const file = formData.get("file") as File;
    if (!file) return new Response("No file", { status: 400 });
    const item = await env.SEARCH.items.upload(file.name, file);
    return Response.json({ uploaded: item });
    }
    // Search — simple lookup uses `query`.
    if (url.pathname === "/search") {
    const query = url.searchParams.get("q");
    if (!query) return new Response("?q= required", { status: 400 });
    const results = await env.SEARCH.search({ query });
    return Response.json(results);
    }
    // Chat completions (streaming) — chat-style context uses `messages`.
    if (url.pathname === "/chat") {
    const query = url.searchParams.get("q");
    if (!query) return new Response("?q= required", { status: 400 });
    const stream = await env.SEARCH.chatCompletions({
    messages: [{ role: "user", content: query }],
    stream: true,
    });
    return new Response(stream, {
    headers: { "content-type": "text/event-stream" },
    });
    }
    // List instances in namespace
    if (url.pathname === "/instances") {
    const list = await env.DOCS.list();
    return Response.json(list);
    }
    return new Response(
    "Endpoints:\n" +
    " POST /upload — Upload a document\n" +
    " GET /search?q= — Search documents\n" +
    " GET /chat?q= — Chat with documents (streaming)\n" +
    " GET /instances — List AI Search instances\n",
    );
    },
    };
  5. Deploy

    Run the alchemy.run.ts script to deploy:

    Terminal window
    bun ./alchemy.run

    It should log out the Worker URL:

    Terminal window
    { url: "https://api.your-subdomain.workers.dev" }
  6. Upload and Search

    Upload a document and search it:

    Terminal window
    # Upload a document
    curl -F "file=@guide.pdf" https://api.your-subdomain.workers.dev/upload
    # Search (wait a moment for indexing)
    curl "https://api.your-subdomain.workers.dev/search?q=how+does+caching+work"
    # Chat with streaming response
    curl "https://api.your-subdomain.workers.dev/chat?q=explain+caching"
  7. Tear Down

    Terminal window
    bun ./alchemy.run --destroy