Error integrating Algolia with NextJS and Sanity
I can see from the error you're encountering that you're using the sanity-algolia package, which is actually no longer the recommended approach for integrating Algolia with Sanity. The package has been deprecated because both Sanity webhooks and the Algolia client have improved significantly since it was created, making the package unnecessary.
The error Cannot read property 'created' of undefined is occurring because the deprecated package expects a specific payload structure that doesn't match modern webhook implementations. The GitHub repository explicitly states: "Both Sanity webhooks and the Algolia client have significantly improved since the sanity-algolia package was originally released, so much so that the package is no longer necessary."
The Modern Recommended Approach
Instead of using the sanity-algolia package, you should integrate Algolia directly using either:
1. Sanity Functions (Recommended)
Sanity Functions are the modern, serverless compute solution built into Sanity. They're perfect for this use case because they run on Sanity's infrastructure and can react to document changes automatically:
// sanity.blueprint.ts
import { defineBlueprint } from 'sanity'
import algoliasearch from 'algoliasearch'
const client = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
)
const index = client.initIndex('your_index_name')
export default defineBlueprint({
functions: {
algoliaSync: {
on: 'document.create,document.update,document.delete',
filter: '_type in ["post", "page"]', // Specify your document types
handler: async ({ event, sanityClient }) => {
const { _id, operation } = event
if (operation === 'delete') {
await index.deleteObject(_id)
return { success: true, action: 'deleted' }
}
// Fetch the full document with the fields you need
const document = await sanityClient.fetch(
`*[_id == $id][0]{
objectID: _id,
title,
slug,
content,
// Add other fields you want to index
}`,
{ id: _id }
)
if (document) {
await index.saveObject(document)
return { success: true, action: 'indexed' }
}
return { success: false, reason: 'document not found' }
}
}
}
})2. GROQ-Powered Webhooks with Next.js API Route
If you prefer to keep using your Next.js API route approach, modernize it by using GROQ-powered webhooks and the Algolia JavaScript client directly:
// pages/api/algolia-sync.js (or app/api/algolia-sync/route.ts for App Router)
import algoliasearch from 'algoliasearch'
import { createClient } from '@sanity/client'
import { isValidSignature, SIGNATURE_HEADER_NAME } from '@sanity/webhook'
const algolia = algoliasearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_ADMIN_KEY
)
const index = algolia.initIndex('your_index_name')
const sanityClient = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
apiVersion: '2024-01-01',
useCdn: false
})
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' })
}
// Verify webhook signature
const signature = req.headers[SIGNATURE_HEADER_NAME]
const isValid = isValidSignature(
JSON.stringify(req.body),
signature,
process.env.SANITY_WEBHOOK_SECRET
)
if (!isValid) {
return res.status(401).json({ message: 'Invalid signature' })
}
try {
const { _id, operation, value } = req.body
// Handle delete operation
if (operation === 'delete') {
await index.deleteObject(_id)
return res.status(200).json({ success: true, action: 'deleted' })
}
// For create/update, the value from the webhook projection contains the data
if (value) {
await index.saveObject(value)
return res.status(200).json({ success: true, action: 'indexed' })
}
return res.status(400).json({ error: 'No value provided' })
} catch (error) {
console.error('Algolia sync error:', error)
return res.status(500).json({ error: error.message })
}
}Setting Up the GROQ-Powered Webhook
In your Sanity webhook settings, configure:
Filter:
_type == 'post'Projection:
{
"transactionId": _rev,
"projectId": sanity::projectId(),
"dataset": sanity::dataset(),
_id,
"operation": delta::operation(),
"value": {
"objectID": _id,
"title": title,
"slug": slug.current,
"body": pt::text(content),
"_type": _type,
"coverImage": coverImage.asset->url,
"date": date,
"_createdAt": _createdAt,
"_updatedAt": _updatedAt
}
}Migration Steps
- Remove the deprecated package:
npm uninstall sanity-algolia - Install the Algolia client:
npm install algoliasearch @sanity/webhook - Implement one of the approaches above
- Update your webhook configuration in Sanity to use the GROQ projection
- Test with a document update to verify it indexes correctly
Why This Approach is Better
- No deprecated dependencies: You're using the official, maintained Algolia JavaScript client
- More control: You define exactly what data gets indexed using GROQ
- Clearer error handling: Direct integration makes debugging easier
- Modern architecture: Especially with Sanity Functions, you get automatic scaling and no external hosting needed
The sanity-algolia package served its purpose when it was created, but the ecosystem has evolved. The direct integration approach gives you more flexibility and uses current best practices for both Sanity and Algolia. You can find a complete implementation guide in the official Sanity + Algolia guide and reference implementation in the sanity-algolia repository.
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.