Guides / Building Search UI / Going further / Backend search

Implement backend search with React InstantSearch

This is the React InstantSearch v7 documentation. React InstantSearch v7 is the latest version of React InstantSearch and the stable version of React InstantSearch Hooks.

If you were using React InstantSearch v6, you can upgrade to v7.

If you were using React InstantSearch Hooks, you can still use the React InstantSearch v7 documentation, but you should check the upgrade guide for necessary changes.

If you want to keep using React InstantSearch v6, you can find the archived documentation.

You may wish to query Algolia’s servers from your backend instead of the frontend, while still being able to use InstantSearch widgets. Possible motivations could be for security restrictions, for SEO purposes or to enrich the Algolia data with data from your servers.

Keep in mind though that Algolia recommends frontend search for performance and high availability reasons.

How backend search works

InstantSearch is the UI on top of a search client. Its state is managed by the Algolia search helper. However, you can use a different search client.

Algolia search client

Algolia’s search client queries Algolia’s backend whenever users refine the search.

The response from Algolia’s search client is a JSON object that includes:

  • Results already formatted for display (full record content, embedded HTML, image URLs, and so on).
  • Highlighting and snippeting
  • Pagination.

Custom search client

You can implement your own search client to query your backend which, in turn, uses the Algolia search client to query Algolia’s backend. To create your own client, you must implement an interface that receives and returns formatted data that InstantSearch can understand.

On the backend: create the necessary routes

This guide assumes that you have an existing server running on http://localhost:3000 with the route POST /search that takes the default Algolia query parameters as JSON. This backend could be using the JavasScript API client to query Algolia, on top of any other operations you want to perform.

The algoliasearch package lets you query Algolia from your backend. Here’s an example using Express:

1
2
3
4
5
6
7
8
9
10
// Instantiate an Algolia client
const algoliasearch = require('algoliasearch');
const algoliaClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');

// Add the search endpoint
app.post('/search', async ({body}, res) => {
  const { requests } = body;
  const results = await algoliaClient.search(requests);
  res.status(200).send(results);
});

Supporting search for facet values

The search for facet values feature makes your refinement lists searchable. If your frontend uses this feature, a search box with refinement lists, you must create an endpoint POST /sffv. Add a new route to support that:

1
2
3
4
5
app.post('/sffv', async ({body}, res) => {
  const { requests } = body;
  const results = await algoliaClient.searchForFacetValues(requests);
  res.status(200).send(results);
});

Once your new route is ready, get back to the frontend and create a search client that can communicate with this server

On the frontend: call your new backend routes

Searching for results

A search client implements the search() method every time users search and refine results. Since the server accepts the InstantSearch format as an input, you only need to pass these requests to your backend in this method and return the response.

1
2
3
4
5
6
7
8
9
10
11
const customSearchClient = {
  search(requests) {
    return fetch('http://localhost:3000/search', {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ requests }),
    }).then(res => res.json());
  }
};

This example uses the Fetch API to query the server. Check the browser compatibility requirements before using it in production.

If you want to transform the data to be passed to your server, you can learn more about all the parameters that the search() method supports in the Algolia API reference.

Searching for facet values

If your server exposes a Search For Facet Values endpoint, you can implement this feature with the searchForFacetValues() method.

The implementation is the same as the search() method, except that it targets POST /sffv.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const customSearchClient = {
  search(requests) {
    /* ... */
  },
  searchForFacetValues(requests) {
    return fetch('http://localhost:3000/sffv', {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ requests }),
    }).then(res => res.json());
  }
};

You can search within your refinement lists from your custom backend.

You must set the option searchable to true in your refinement list to make it searchable.

Using the search client with InstantSearch

Now, you need to tell InstantSearch to use the search client you created. This is possible with the searchClient option. This parameter turns off all Algolia requests coming from the frontend and proxies them to your backend and displays the UI.

1
2
3
4
5
6
7
8
9
10
import { InstantSearch } from 'react-instantsearch';

const App = ({children}) => (
  <InstantSearch
    searchClient={customSearchClient}
    indexName="YourIndexName"
  >
    {children}
  </InstantSearch>
);

Going further: enriching data from the backend

Now that InstantSearch is querying your backend before fetching results from Algolia, you could merge Algolia’s data with yours (to offer your users more exhaustive results).

A recurring problem with ecommerce websites using Algolia managing the remaining stock for each product. It’s sometimes hard to keep track of the exact number of items. An approach made possible with a custom backend is to only store the item’s availability on each Algolia record (none, low, medium, high) and to fetch the exact stock on your database.

You need to make a few changes to your backend:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.post('/search', async ({body}, res) => {
  const requests = body;
  const algoliaResults = await algoliaClient.search(requests);
  const results = {
    ...algoliaResults,
    results: algoliaResults.results.map(result => ({
      ...result,
      hits: result.hits.map(async hit => ({
        ...hit,
        // `getStock()` retrieves a product's exact stock from your database
        stock: await getStock(hit.productID),
      })),
    })),
  };

  res.status(200).send(results);
});

You can now access the property stock on each hit with InstantSearch on the frontend.

Conclusion

In this guide, you learned how to:

  • Handle Algolia requests coming from InstantSearch on your backend
  • Create a custom search client calling this server
  • Plug the search client into InstantSearch
Did you find this page helpful?