Algolia Search with Next.js

Build search with Algolia and Next.js using TypeScript.

Creating search functionality is crucial to a website's user experience. Using Algolia we can easy create a searchable index to help create this experience.

One of the key advantages of Algolia is its real-time indexing and syncing capabilities. As our website evolves and new content is added, Algolia ensures that the search index is always up to date.

Let's build this out with Next.js and Typescript.

Step 1: Create an Algolia Index populated with data

First we'll need to set up and populate an Algolia index in an automated way. To achieve this we'll create a script that runs on every build so we are confident we have the latest searchable items (We are doing this on every build. You may need to change when this runs depending on the type of website/application you are building).

Create an index in Algolia

First create an index in Algolia's dashboard. After creating an index grab the API keys and place them in your .env file like so:

// .env
NEXT_PUBLIC_ALGOLIA_APP_ID={APP_ID}
NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY={SEARCH_ONLY_API_KEY}
ALGOLIA_SEARCH_ADMIN_KEY={ADMIN_API_KEY}

Create a script to build out index

Now that we have an index set up in Algolia we can populate it with any data we want. We can do this in an automated way so we don't have to think about it on every deploy. Create a directory called scripts and add a file called build-algolia-search.ts in it. In this file we'll do this:

// scripts/build-algolia-search.ts

import dotenv from "dotenv"
dotenv.config()
import algoliaSearch from "algoliasearch"

export interface SearchObject {
  objectID: string // objectID is needed for Algolia
  title: string
  description: string
  slug: string
  date: string
  thumbnail?: string
}

async function getAllPostsTransformed(): Promise<SearchObject[]> {
  const allPosts = await fetchAllPosts() // 👈 This function is specific to your application. Grab data from Markdown/Contentful/CMS etc.

  // return an array of objects to be added to Algolia.
  return (
    allPosts?.map((post) => {
      return {
        objectID: post.id, // objectID must be unique and consistent on each build
        title: post.title,
        description: post.subTitle,
        slug: post.slug,
        date: post.createdAt,
        thumbnail: post.image.fields.file.url,
      }
    }) || []
  )
}

;(async function () {
  dotenv.config()
  try {
    const posts = await getAllPostsTransformed()
    const client = algoliaSearch(
      process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
      process.env.ALGOLIA_SEARCH_ADMIN_KEY!
    )
    const searchIndex = client.initIndex("search")
    const algoliaSearchIndexResponse = await searchIndex.saveObjects([...posts]) // Add any more data needed to this array
    console.log(
      `⭐️⭐️ Successfully added ${algoliaSearchIndexResponse.objectIDs.length} records to Algolia search. ⭐️⭐️`
    )
  } catch (err) {
    console.error(err)
  }
})()

Next we can install the needed dependencies with yarn:

yarn add algoliasearch dotenv

And finally we can add an npm script to run:

// package.json
...
"scripts": {
  ...
  "build": "next build && npm run buildsearch",
  "buildsearch": "npx ts-node --skip-project  ./scripts/build-algolia-search.ts",
  ...
},
...

Run yarn buildsearch to run the script and you should see your index populated with data in the Algolia Dashboard. We also updated our build script so that it also runs buildsearch on deployments.

Creating an API route to search from

Now that we have an index with data set up we can now create an API route to search our data from. Create a search api route in the api routes directory

// pages/api/search.ts

import type { NextApiRequest, NextApiResponse } from "next";
import algoliaSearch from "algoliasearch";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const query = req.query;
  const { term } = query;
  const client = algoliaSearch(process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!, process.env.ALGOLIA_SEARCH_ADMIN_KEY!);
  const index = client.initIndex("search");
  index
    .search(term as string)
    .then(({ hits }) => {
      res.status(200).json(hits);
    })
    .catch((err) => {
      console.log(err);
    });
}

Now visiting localhost:3000/api/search?term=test will show a json response with the search results for the term "test". You can create a UI to request search from this new API route.

Have some feedback on this post? .