30
loading...
This website collects cookies to deliver better user experience
NEXT_PUBLIC_ALGOLIA_APP_ID={Application ID}
NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY={Search-Only API Key}
ALGOLIA_SEARCH_ADMIN_KEY={Admin API Key}
NEXT_PUBLIC_
. Just like the Contentful Content Delivery API keys, these keys provide read-only access to your search results, so it’s okay to expose them. NEXT_PUBLIC_
prefix.postbuild
command to run the script. This will run node build-search.js
in the build pipeline after the build
command has completed.// package.json
"scripts": {
"dev": "next dev",
"build": "next build",
"postbuild": "node ./scripts/build-search.js",
"start": "next start"
},
npm install dotenv algoliasearch
// build-search.js
const dotenv = require("dotenv");
(async function () {
// initialize environment variables
dotenv.config();
console.log("Schnitzel! Let's fetch some data!");
})();
node ./scripts/build-search.js
// build-search.js
const dotenv = require("dotenv");
async function getAllBlogPosts() {
// write your code to fetch your data
}
(async function () {
// initialize environment variables
dotenv.config();
try {
// fetch your data
const posts = await getAllBlogPosts();
}
} catch (error) {
console.log(error);
}
})();
sys.id
from each blog post in Contentful, I chose to insert the posts with the IDs I had to hand.// build-search.js
const dotenv = require("dotenv");
async function getAllBlogPosts() {
// write your code to fetch your data
}
function transformPostsToSearchObjects(posts) {
const transformed = posts.map((post) => {
return {
objectID: post.sys.id,
title: post.title,
excerpt: post.excerpt,
slug: post.slug,
topicsCollection: { items: post.topicsCollection.items },
date: post.date,
readingTime: post.readingTime,
};
});
return transformed;
}
(async function () {
dotenv.config();
try {
const posts = await getAllBlogPosts();
const transformed = transformPostsToSearchObjects(posts);
// we have data ready for Algolia!
console.log(transformed);
} catch (error) {
console.log(error);
}
})();
readingTime
, topics
and date
to display an already-existing UI component in my search results on the front end (we’ll look at this later). This is the beauty of the flexible schema of the search objects!algoliasearch
client with the environment variables we added earlier. Then, initialize the index with the name of the index you set up when you onboarded to Algolia, and call the saveObjects
function with your transformed data. Make sure to import the algoliasearch
dependency! Also, let’s log out the objectIDs from the response to make sure everything has gone smoothly.// build-search.js
const dotenv = require("dotenv");
const algoliasearch = require("algoliasearch/lite");
async function getAllBlogPosts() {
// write your code to fetch your data
}
function transformPostsToSearchObjects(posts) {
// ...
}
(async function () {
dotenv.config();
try {
const posts = await getAllBlogPosts();
const transformed = transformPostsToSearchObjects(posts);
// initialize the client with your environment variables
const client = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
process.env.ALGOLIA_SEARCH_ADMIN_KEY,
);
// initialize the index with your index name
const index = client.initIndex("my_awesome_content");
// save the objects!
const algoliaResponse = await index.saveObjects(transformed);
// check the output of the response in the console
console.log(
`🎉 Sucessfully added ${algoliaResponse.objectIDs.length} records to Algolia search. Object IDs:\n${algoliaResponse.objectIDs.join(
"\n",
)}`,
);
} catch (error) {
console.log(error);
}
})();
postbuild
command to your package.json file, you are safe to commit these changes to your project. If your project is live and hosted on a hosting provider like Vercel, check out the build console to confirm the search results are sent to Algolia after your project has been built.algoliasearch
that we installed earlier and react-instantsearch-dom
. Run the following command in your terminal at the root of your project.npm install react-instantsearch-dom
algoliasearch
dependency.// ./components/Search/index.js
// “algoliasearch/lite” is the search-only version of the API client — optimized for size and search
import algoliasearch from "algoliasearch/lite";
export default function Search() {
return (
// Our search components will go here!
)
}
// ./pages/blog/index.js
import ContentfulApi from "./lib/ContentfulApi";
import PostList from "./components/PostList";
import Search from "./components/Search";
export default function BlogIndex({ posts }) {
return (
<>
<Search />
<PostList posts={posts} />
</>
);
}
export async function getStaticProps() {
const posts = await ContentfulApi.getPostSummaries();
return {
props: {
posts,
},
};
}
algoliasearch
client with the public environment variables you set up earlier.// .components/Search/index.js
import algoliasearch from "algoliasearch/lite";
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY,
);
export default function Search() {
return (
// Our search components will go here!
)
}
searchClient
and the indexName
you set up with Algolia as props into the InstantSearch component.// .components/Search/index.js
import algoliasearch from "algoliasearch/lite";
import { InstantSearch, SearchBox, Hits } from "react-instantsearch-dom";
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY,
);
export default function Search() {
return (
<>
<InstantSearch
searchClient={searchClient}
indexName="my_awesome_content">
<SearchBox />
<Hits />
</InstantSearch>
</>
);
}
connectSearchBox
higher-order component from react-instant-search-dom
— this is the function that will connect the custom search box to the InstantSearch client. Read more about higher-order components in React.refine
prop to manage the onChange
of the input field. I chose to add a label element alongside the input field for accessibility reasons.connectSearchBox
.// .components/Search/CustomSearchBox.js
import { connectSearchBox } from "react-instantsearch-dom";
function SearchBox({ refine }) {
return (
<form action="" role="search">
<label htmlFor="algolia_search">Search articles</label>
<input
id="algolia_search"
type="search"
placeholder="javascript tutorial"
onChange={(e) => refine(e.currentTarget.value)}
/>
</form>
);
}
export default connectSearchBox(SearchBox);
// .components/Search/index.js
import algoliasearch from "algoliasearch/lite";
import { InstantSearch, Hits } from "react-instantsearch-dom";
import CustomSearchBox from "./CustomSearchBox";
const searchClient = algoliasearch(...);
export default function Search() {
return (
<>
<InstantSearch searchClient={searchClient} indexName="p4nth3rblog">
<CustomSearchBox />
<Hits />
</InstantSearch>
</>
);
}
connectStateResults
higher-order component from react-instant-search-dom
— this is the function that will connect the custom hits to the InstantSearch client.searchState
and searchResults
as props in the component function declaration.searchResults
prop to manage the onChange
of the input field.connectStateResults
.searchResults.hits
. I used the same component that displays my recent blog posts on my home page!searchState.query
to process some logic to only render results to the DOM if the length of the search query is greater than or equal to three characters in length.
// ./components/Search/CustomHits.js
import { connectStateResults } from "react-instantsearch-dom";
function Hits({ searchState, searchResults }) {
const validQuery = searchState.query?.length >= 3;
return (
<>
{searchResults?.hits.length === 0 && validQuery && (
<p>Aw snap! No search results were found.</p>
)}
{searchResults?.hits.length > 0 && validQuery && (
<ol>
{searchResults.hits.map((hit) => (
<li key={hit.objectID}>{hit.title}</li>
))}
</ol>
)}
</>
);
}
export default connectStateResults(Hits);
// .components/Search/index.js
import algoliasearch from "algoliasearch/lite";
import { InstantSearch } from "react-instantsearch-dom";
import CustomSearchBox from "./CustomSearchBox";
import CustomHits from "./CustomHits";
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY,
);
export default function Search() {
return (
<>
<InstantSearch searchClient={searchClient} indexName="p4nth3rblog">
<CustomSearchBox />
<CustomHits />
</InstantSearch>
</>
);
}