Back to homepage
#nextjs#typesense#docker-compose#javascript#typescript#search-engine

Integrate Typesense Seamlessly into Your Next.js App with Docker-Compose

6 min read

This article provides a comprehensive guide on integrating Typesense, a fast and typo-tolerant search engine, into a Next.js application using Docker-Compose. It walks through the complete setup process, from configuring Docker services and initializing Typesense with sample data to implementing a search component with features like instant search, filtering, and sorting. The tutorial demonstrates how to create a seamless development environment where both the Next.js application and Typesense service run in containers, making it easy to develop and deploy search functionality in web applications.

Integrate Typesense Seamlessly into Your Next.js App with Docker-Compose

As web applications evolve, so does the need for intuitive and fast search functionalities. Enter Typesense, an ultra-fast, typo-tolerant search engine. If you're familiar with Next.js and want to harness the power of Typesense, you're in the right place.

By the end of this guide, you'll integrate Typesense into your Next.js project using Docker-Compose, streamlining the entire setup process.

Let's dive in!

Prerequisites

  • A basic understanding of Next.js.
  • Docker and Docker-Compose installed on your machine.
  • A Next.js project to integrate with (if you don't have one, create it using npx create-next-app).

1. Setting Up Docker-Compose

Firstly, we need a docker-compose.yml file at the root of our Next.js project. This file will define and configure our app's services.

yaml
1version: '3.8'
2services:
3 web:
4 build:
5 context: .
6 dockerfile: Dockerfile
7 ports:
8 - "3000:3000"
9 depends_on:
10 - typesense
11 typesense:
12 image: typesense/typesense:0.24.1
13 ports:
14 - "8108:8108"
15 command: "command: --data-dir /data --api-key=YourApiKeyHere"
16 volumes:
17 - typesense-data:/data
18
19volumes:
20 typesense-data:

A quick breakdown:

  • web: This is your Next.js app. You'll need a Dockerfile for this which we'll discuss shortly.
  • typesense: This is the Typesense service using its official image.

2. Dockerfile for Next.js App

Create a Dockerfile in the root of your Next.js project:

dockerfile
1FROM node:18
2
3WORKDIR /app
4COPY package*.json ./
5RUN npm install
6COPY . .
7
8CMD ["npm", "run", "dev"]

This is a simple Dockerfile that takes the Node.js image, sets the work directory, copies your app, installs the dependencies, and runs the development server.

3. Configuring Typesense in Next.js

Install the necessary package:

bash
1npm i typesense react-instantsearch-dom typesense-instantsearch-adapter

Now, in your Next.js application, initialize the Typesense client in a typesense.js file:

typescript
1import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";
2
3const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
4 server: {
5 apiKey: 'YourApiKeyHere', // Use the same you've defined in docker-compose
6 nodes: [
7 {
8 host: 'localhost',
9 port: '8108',
10 protocol: 'http',
11 },
12 ],
13 },
14 additionalSearchParameters: {
15 query_by: "title",
16 },
17});
18
19export default typesenseInstantsearchAdapter;

Make sure you replace ‘YourApiKeyHere' with the API key defined in your docker-compose file.

4. Running Your Services

With everything in place, navigate to your project root and run:

docker compose up

Both your Next.js app and Typesense service should now be up and running!

docker-compose

5. Populating Data in Your Typesense Container

Now that you have Typesense running, it would be great to have some data inside to play around with. In this step, we'll populate your Typesense instance with sample data.

Create a Script File

Create a file inside the utils folder of your Next.js project named initializeTypesense.js.

Add the Typesense Client and Data

Inside initializeTypesense.js, you'll first initialize the Typesense client, then define and run functions to populate data:

typescript
1const Typesense = require('typesense');
2
3const client = new Typesense.Client({
4 nodes: [
5 {
6 host: 'localhost',
7 port: '8108',
8 protocol: 'http',
9 }
10 ],
11 apiKey: 'YourApiKeyHere',
12 connectionTimeoutSeconds: 2
13});
14
15async function createCollection() {
16 const collectionSchema = {
17 name: "posts",
18 fields: [
19 { name: "title", type: "string" },
20 { name: "description", type: "string" },
21 { name: "author", type: "string" },
22 { name: "tags", type: "string[]", optional: true},
23 { name: "published_date", type: "int32" }
24 ],
25 default_sorting_field: "published_date"
26 };
27
28 return await client.collections().create(collectionSchema);
29}
30
31async function insertData() {
32 const samplePost = {
33 id: "1",
34 title: "Integrating Typesense with Next.js",
35 description: "A deep dive into integrating Typesense search engine with Next.js using Docker.",
36 author: "John Doe",
37 tags: ["Typesense", "Next.js", "Docker"],
38 published_date: Math.floor(new Date("2023-08-24").getTime() / 1000) // Convert to Unix timestamp. In Typesense, only integer or float fields can be used as sorting fields.
39 };
40
41 return await client.collections("posts").documents().create(samplePost);
42}
43
44async function main() {
45 await createCollection();
46 await insertData();
47 console.log("Typesense data initialization complete!");
48}
49
50main();

This code is quite simple. We initialize Typesense, create a Collection called “posts” and the populate this Collection with a post.

Execute the Script

Before you run the script, make sure your docker-compose services (especially Typesense) are up and running.

To execute the script, run:

bash
1// Depending of your path
2node utils/initializeTypesense.js

You should see:

bash
1Typesense data initialization complete!

Things to Note

  1. Environment Configuration: In a real-world scenario, you might want to move configuration details, like the Typesense API key, to environment variables for better security and flexibility.
  2. Error Handling: The above script is a basic setup and does not handle possible errors (like trying to create a collection that already exists). Depending on your needs, you might want to add error handling or checks to make the script more robust.
  3. Script Execution: Since this is a one-time setup script, it's run manually from the command line. If you ever needed similar functionality in your app, you'd typically integrate such actions in response to specific user actions or events.

6. Using Typesense in your Components

With data in your Typesense collection, you can integrate search functionality into your Next.js components.

Create a BlogSearchComponent.jsx file:

typescript
1import React from 'react';
2import {
3 InstantSearch,
4 SearchBox,
5 Hits,
6 RefinementList,
7 Stats,
8 SortBy
9} from 'react-instantsearch-dom';
10import typesenseInstantsearchAdapter from 'utils/typesense'; // adjust the path based on your directory structure
11
12const BlogHitComponent = ({ hit }) => {
13 return (
14 <div>
15 <h3>{hit.title}</h3>
16 <p>{hit.description}</p>
17 <a href={`/blog/${hit.id}`}>Read More</a>
18 </div>
19 );
20};
21
22const BlogSearchComponent = () => {
23 const postCollection = `posts`;
24
25 return (
26 <InstantSearch indexName={postCollection} searchClient={typesenseInstantsearchAdapter.searchClient}>
27 <div style={{ padding: '2%' }}>
28 <SearchBox />
29 <Stats />
30 </div>
31
32 <div className='flex'>
33 <main style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
34 <div style={{ padding: '2%' }}>
35 <RefinementList attribute='tags' />
36 <SortBy
37 defaultRefinement={postCollection}
38 items={[
39 { value: postCollection, label: 'Latest' },
40 { value: `${postCollection}_title_asc`, label: 'Title A-Z' },
41 { value: `${postCollection}_title_desc`, label: 'Title Z-A' },
42 ]}
43 />
44 </div>
45 <Hits hitComponent={BlogHitComponent} />
46 </main>
47 </div>
48 </InstantSearch>
49 );
50};
51
52export default BlogSearchComponent;

This component does the following:

  • Allows users to search blog entries.
  • Displays a list of blog entries (title, description, and a link to the full post).
  • Lets users refine search results based on tags.
  • Offers sorting options (by latest, title A-Z, and title Z-A).

For the blog entries to be displayed correctly:

  1. Ensure the posts collection in Typesense has fields like id, title, description, and tags.
  2. Adjust the path for the typesenseInstantsearchAdapter import based on your directory structure.
  3. Implement routing for individual blog posts by creating dynamic routes. For example, create a /blog/[id].js file structure for individual blog post pages.

After creating the component, you can then use it in your desired page to display your posts collection with search capabilities.

7. Let's Search something!

Head over to localhost:3000 and give the search input a try. Every keystroke initiates an API call to the Typesense container, fetching results at blazing-fast speeds.

Within moments, you'll see the responsive search results appear!

search

In our proof-of-concept, we've added only a single entry, but envision the potential when scaled to thousands of products for an e-commerce platform or countless posts on a blog.

The possibilities are vast!

If you encounter any issues while following this tutorial, please don't hesitate to contact me. I'll do my best to assist you and help find a solution!

Typesense Documentation

GitHub - typesense/typesense-instantsearch-adapter: A JS adapter library to build rich search interfaces with Typesense and InstantSearch.js

Showcase for React InstantSearch widgets | Algolia

Next.js Documentation

Docker Compose overview