1. Create a Search Database

Follow the instructions in the Getting Started guide to create a Search Database.


2. Project Setup

Let’s create a new Next.js application and install the @upstash/search package:

npx create-next-app@latest search-app
cd search-app
npm install @upstash/search

3. Add Environment Variables

Find the environment variables from your database dashboard and add them to your .env file:

UPSTASH_SEARCH_REST_URL=<YOUR_SEARCH_REST_URL>
UPSTASH_SEARCH_REST_TOKEN=<YOUR_SEARCH_REST_TOKEN>

4. Create an API Route to Upsert Documents

Create an API route in app/api/upsert/route.ts:

title="app/api/upsert/route.ts"
import { Search } from "@upstash/search"

const client = new Search({
  url: process.env.UPSTASH_SEARCH_REST_URL,
  token: process.env.UPSTASH_SEARCH_REST_TOKEN,
})

const index = client.index("my-index")

export async function POST() {
  await index.upsert([
    {
      id: "movie-1",
      content: {
        title: "Inception",
        description: "A thriller about dreams within dreams.",
      },
      metadata: { genre: "sci-fi", year: 2010 },
    },
    {
      id: "movie-2",
      content: {
        title: "The Godfather",
        description: "A story about a powerful Italian-American crime family.",
      },
      metadata: { genre: "crime", year: 1972 },
    },
    {
      id: "movie-3",
      content: {
        title: "The Dark Knight",
        description: "A tale of Batman's fight against the Joker.",
      },
      metadata: { genre: "action", year: 2008 },
    },
  ])

  return new Response("OK")
}

5. Create a Route to Search Documents

Create an API route in app/api/search/route.ts:

title="app/api/search/route.ts"
import { Search } from "@upstash/search"

const client = new Search({
  url: process.env.UPSTASH_SEARCH_REST_URL,
  token: process.env.UPSTASH_SEARCH_REST_TOKEN,
})

const index = client.index("my-index")

export async function POST(req: Request) {
  const { query } = (await req.json()) as { query: string }

  const results = await index.search({ query })

  return new Response(JSON.stringify(results))
}

6. Create a Simple Page

Add the following code in app/page.tsx:

title="app/page.tsx"
"use client";

import { useState } from "react";

interface SearchResult {
  id: string;
  content: Record<string, unknown>;
  metadata: Record<string, unknown>;
  score: number;
}

export default function Home() {
  const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
  const [query, setQuery] = useState("");
  const [isUpserting, setIsUpserting] = useState(false);
  const [upsertSuccess, setUpsertSuccess] = useState(false);

  const upsertData = async () => {
    setIsUpserting(true);
    setUpsertSuccess(false);
    await fetch("/api/upsert", { method: "POST" });
    setIsUpserting(false);
    setUpsertSuccess(true);
  };

  const search = async () => {
    const res = await fetch(`/api/search`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query }),
    });
    const data = await res.json();
    setSearchResults(data || []);
  };

  return (
    <main className="min-h-screen bg-emerald-50 flex flex-col items-center py-10">
      <div className="w-full max-w-2xl bg-white shadow-md rounded-lg p-6">
        <header className="mb-6 text-center">
          <h1 className="text-2xl font-bold text-emerald-800">Search App</h1>
          <p className="text-emerald-600">
            Upsert data and search through the database.
          </p>
        </header>

        <div className="mb-6">
          <button
            onClick={upsertData}
            disabled={isUpserting}
            className={`w-full py-2 px-4 rounded-md text-white ${
              isUpserting
                ? "bg-emerald-300"
                : "bg-emerald-500 hover:bg-emerald-600"
            }`}
          >
            {isUpserting ? "Upserting..." : "Upsert Data"}
          </button>
          {upsertSuccess && (
            <p className="mt-2 text-sm text-emerald-600">
              Data upserted successfully!
            </p>
          )}
        </div>

        <div className="mb-6">
          <div className="flex gap-2">
            <input
              type="text"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="Search..."
              className="flex-1 border border-emerald-300 rounded-md px-4 py-2 text-gray-800 focus:outline-none focus:ring-2 focus:ring-emerald-500"
            />
            <button
              onClick={search}
              className="bg-emerald-500 text-white px-4 py-2 rounded-md hover:bg-emerald-600"
            >
              Search
            </button>
          </div>
        </div>

        <ul className="space-y-2">
          {searchResults.map((result, index) => (
            <li
              key={index}
              className="bg-emerald-100 p-4 rounded-md shadow-sm text-emerald-800"
            >
              <p className="font-bold">ID: {result.id}</p>
              <p className="text-sm">Content: {JSON.stringify(result.content)}</p>
              <p className="text-sm">Metadata: {JSON.stringify(result.metadata)}</p>
              <p className="text-sm">Score: {result.score}</p>
            </li>
          ))}
        </ul>
      </div>
    </main>
  );
}

7. Start the Project

Run the following command to start the development server:

npm run dev

Open your browser and navigate to http://localhost:3000 to test the application.

You can click the Upsert Data button to add three movies to your database and use the search bar to make a query.


Next Steps

Learn more about: