We do not advise exposing your Elasticsearch instance to the public. Fortunately with NextJS, we do not need to do this. NextJS makes it very simple to build an API route so we can move the connector to the server side.

To start, we create a connector that will send the requestState and queryConfig over to the client side.

// located in <root>/services/APIConnector.js

class APIConnector {
  constructor() {}

  onResultClick() {
    // optional. Called when a result has been clicked
  }
  onAutocompleteResultClick() {
    // optional. Called when an autocomplete result has been clicked
  }

  async onSearch(requestState, queryConfig) {
    const response = await fetch("/api/search", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        requestState,
        queryConfig
      })
    });
    return response.json();
  }

  async onAutocomplete(requestState, queryConfig) {
    const response = await fetch("api/autocomplete", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        requestState,
        queryConfig
      })
    });
    return response.json();
  }
}

export default APIConnector;

then add an api folder within <root>/pages folder. Add two files, autocomplete.js and search.js

// located in <root>/pages/api/search.js
import ElasticsearchAPIConnector from "@elastic/search-ui-elasticsearch-connector";

const connector = new ElasticsearchAPIConnector({
  host: "<elasticsearch host>",
  index: "<elasticsearch index>",
  apiKey: "<api key>" // optional
});

export default async function handler(req, res) {
  const { requestState, queryConfig } = req.body;
  const response = await connector.onSearch(requestState, queryConfig);
  res.json(response);
}
// located in <root>/pages/api/autocomplete.js
import ElasticsearchAPIConnector from "@elastic/search-ui-elasticsearch-connector";

const connector = new ElasticsearchAPIConnector({
  host: "<elasticsearch host>",
  index: "<elasticsearch index>",
  apiKey: "<api key>" // optional
});

export default async function handler(req, res) {
  const { requestState, queryConfig } = req.body;
  const response = await connector.onAutocomplete(requestState, queryConfig);
  res.json(response);
}

then lastly swap the elasticsearch connector to use the APIConnector. With this change, when a user searches, the search will be sent to the server and the API routes will fetch the results from Elasticsearch and will be sent back to the client.

import Connector from "../services/APIConnector";

const apiConnector = new Connector();

const searchConfig = {
  apiConnector: apiConnector
  // ...truncated search configuration
};

and then restart your nextjs app. To confirm that this is working, you should see network requests within chrome developer tools performing requests with requestState and queryConfig to the /api/search & /api/autocomplete API routes and results being returned back to the client.

Below is an example of this running within codesandbox.