In this series we will create an Algolia powered faceted product search for Chec. Chec (or also known as Commerce.js) is a eCommerce API that gives you ultimate flexibility when building a frontend for your store.
Throughout this tutorial series we will be using:
Each time a product is created, updated or deleted, we will update Algolia to reflect those changes and searching products super easy for our users!
Because Chec has a hosted storefront (or "space"), we'll link all our products to the storefront page where users can proceed to pay for them!
1. Signup to Algolia
In this first step, create an account with Algolia, or login if you have an account already.
Next create a new application in Algolia. If you're signing up for the first time, this will be part of the onboarding process.
Once we've an application, Algolia will ask you to create an index. Make sure to call this products
, as we'll need this later.
Finally, you'll need your Application ID
, Search-Only API Key
and Admin API Key
2. Setup Next.js
We'll store all our server side and client side code inside our Next.js application. If you're experienced with Next, and have a project already, you can probably skip to installing the algolia dependencies below.
To create a new Next.js project, you can quickly get started using the CLI:
npm init next-app
# or
yarn create next-app
Once your project folder has been created, change directory and open your code editor.
cd your-project-name
3. Configure our webhook endpoint
We'll be configuring Chec next to send our product payload to our Next.js application so we can send this onto Algolia.
Start by installing the algoliasearch
NPM dependency:
npm install algoliasearch
# or
yarn add algoliasearch
Now let's create the file algolia.js
inside a new folder called api
inside the existing pages
folder.
mkdir pages/api
touch pages/api/algolia.js
Inside pages/api/algolia.js
let's first initialize the algoliasearch
dependency and assign it to client
.
You'll need your Application ID
and Admin API Key
to do this!
import algoliasearch from "algoliasearch"
const client = algoliasearch("APPLICATION_ID", "ADMIN_API_KEY")
To confirm this is configured and working, let's initialize the products
index and add a new object to the index.
Below our client
definition, add:
const algoliaHandler = async (req, res) => {
const index = client.initIndex('products')
const product = await index.saveObject({
objectID: 'myId',
name: 'Sneakers',
price: 1000
})
res.send(product)
}
export default algoliaHandler
Now let's try it out! Now start Next.js...
npm run dev
# or
yarn dev
... and go to http://localhost:3000/api/algolia
. You should see your objectID
returned along with a taskID
.
If you open the application on Algolia, and browse the products
Indicies, you should see the record here also!
Great! π We now have a successful serverless function that is connecting to Algolia.
4. Configure Chec webhooks
In order to properly sync data to Algolia from Chec, we need to configure our webhook and listen for the following events:
products.create
products.update
products.delete
Now head to the Chec Merchant Dashboard and go to Setup > Webhooks, and click + Add Webhook
.
First select the Events we need to watch for.
Since we can't forward requests from the outside world to localhost:3000
, we'll use a CLI tool called ngrok
to get a URL that will handle tunnelling requests for us.
Once you've installed ngrok, inside a new terminal run:
ngrok http 3000
Remember: We have Next.js running on port 3000, so that's why we're forwarding requests there.
When ngrok
is running, you'll get a Forwarding
address.
Let's use the http
forwarding address and add append /api/algolia
to the URL, and Add webhook!
Next we'll need to update our /api/algolia.js
function to correctly handle the payload from Algolia and pass it on!
5. Inspecting the Chec payload
Once you create a new product via the Chec API, the URL we provided to ngrok (localhost:3000) will be invoked.
The payload will tell us if a product was created, updated or deleted, and we can use this perform the appropriate action with our Algolia client
.
If a product is created or updated, we'll want to use index.saveObject()
but if a product is deleted, we'll need to use index.deleteObject()
.
Β Example payload
{
"id": "wbhk_QO3bR5XAlnzdjX",
"created": 1587214321,
"event": "products.create",
"response_code": 201,
"payload": {
"id": "prod_4OANwRKEbovYL8",
"created": 1587897613,
"last_updated": 1587897613,
"active": true,
"permalink": "g9eASZ",
"name": "My other test product",
"description": null,
"price": {
"raw": 10,
"formatted": "10.00",
"formatted_with_symbol": "Β£10.00",
"formatted_with_code": "10.00 GBP"
},
"quantity": 0,
"media": [],
"sku": null,
"meta": [],
"conditionals": {
"is_active": true,
"is_free": false,
"is_tax_exempt": false,
"is_pay_what_you_want": true,
"is_quantity_limited": false,
"is_sold_out": false,
"has_digital_delivery": false,
"has_physical_delivery": false,
"has_images": false,
"has_video": false,
"has_rich_embed": false,
"collects_fullname": false,
"collects_shipping_address": false,
"collects_billing_address": false,
"collects_extrafields": false
},
"is": {
"active": true,
"free": false,
"tax_exempt": false,
"pay_what_you_want": true,
"quantity_limited": false,
"sold_out": false
},
"has": {
"digital_delivery": false,
"physical_delivery": false,
"images": false,
"video": false,
"rich_embed": false
},
"collects": {
"fullname": false,
"shipping_address": false,
"billing_address": false,
"extrafields": false
},
"checkout_url": {
"checkout": "https://checkout.chec.io/g99ASZ?checkout=true",
"display": "https://checkout.chec.io/g99ASZ"
},
"categories": [],
"urls": {
"product": "/v1/products/prod_4OANwRKEbovYL8"
}
},
"signature": "60eecbb88efa2c712204e3b0214dffb0317e297cbc01a6b592ca4287c4177cb9"
}
6. Syncing with Algolia
From the example payload above, we'll toggle our behaviour on the event
value, and using JavaScript, we can split on .
and get the action create
, update
or delete
.
The product object is also inside the payload
object which we can use to identify the correct record in our index.
Now, within the algoliaHandler
function inside pages/api/algolia.js
and after we initialize our Algolia client
, replace the example code we wrote before with the following:
const { event, payload } = req.body
const [ _, eventType ] = event.split('.')
const { id: objectID, ...product } = payload
Here we're destructuring id
from payload
, and renaming it to objectID
, then spreading the rest of the payload into product
.
Algolia requires each record in the index to have a objectID
value, so it makes sense to use our product ID for this.
Deleting products from Algolia
Let's first handle deleting our product from the index if the event
type is product.deleted
.
Let's add the following inside our function below the above code:
if (eventType === 'delete') {
await index.deleteObject(objectID)
return res.status(202).end()
}
Adding/Updating products to Algolia
If eventType
is not delete
, then we'll next have to handle calling the saveObject
function.
We'll check if the eventType
is next either create
or update
and call the saveObject
method.
if (['create', 'update'].includes(eventType)) {
await index.saveObject({ objectID, ...product })
return res.status(200).send({ success: true })
}
Next, eventType
is not create
, update
or delete
, then let's return a message. You could do some special logging here to get notified when an index fails, but I'll leave that up to you!
res.send({
message: `Event type ${eventType} is not a valid trigger`
})
Our pages/api/algolia.js
function should look like...
import algoliasearch from "algoliasearch"
const client = algoliasearch("APPLICATION_ID", "ADMIN_API_KEY")
const algoliaHandler = async (req, res) => {
const index = client.initIndex("products")
const { event, payload } = req.body
const [_, eventType] = event.split(".")
const { id: objectID, ...product } = payload
if (eventType === "delete") {
await index.deleteObject(objectID)
return res.status(202).end()
}
if (["create", "update"].includes(eventType)) {
await index.saveObject({ objectID, ...product })
return res.status(200).send({ success: true })
}
res.send({
message: `Event type ${eventType} is not a valid trigger`,
})
}
export default algoliaHandler
Let's test the above works!
7. Triggering our webhook!
Now let's use cURL to create, update and delete Chec products. You could also run the same command with Postman, Insomnia, etc.
Add a new product
π¨ Make sure to replace SECRET_KEY_HERE
with your Chec Secret Key!
curl --location --request POST 'https://api.chec.io/v1/products' \
--header 'X-Authorization: SECRET_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
"product": {
"name": "My test product",
"price": 1000
}
}'
The response will look a little something like:
{"id":"prod_4WJvlK6Rx5bYV1","created":1587897846,"last_updated":1587897846,"active":true,"permalink":"b5BKLR","name":"My test product","description":null,"price":{"raw":1000,"formatted":"1,000.00","formatted_with_symbol":"\u00a31,000.00","formatted_with_code":"1,000.00 GBP"},"quantity":0,"media":[],"sku":null,"meta":[],"conditionals":{"is_active":true,"is_free":false,"is_tax_exempt":false,"is_pay_what_you_want":false,"is_quantity_limited":false,"is_sold_out":false,"has_digital_delivery":false,"has_physical_delivery":false,"has_images":false,"has_video":false,"has_rich_embed":false,"collects_fullname":false,"collects_shipping_address":false,"collects_billing_address":false,"collects_extrafields":false},"is":{"active":true,"free":false,"tax_exempt":false,"pay_what_you_want":false,"quantity_limited":false,"sold_out":false},"has":{"digital_delivery":false,"physical_delivery":false,"images":false,"video":false,"rich_embed":false},"collects":{"fullname":false,"shipping_address":false,"billing_address":false,"extrafields":false},"checkout_url":{"checkout":"https:\/\/checkout.chec.io\/b5BKLR?checkout=true","display":"https:\/\/checkout.chec.io\/b5BKLR"},"categories":[],"urls":{"product":"\/v1\/products\/prod_4WJvlK6Rx5bYV1"}}
If you open the Algolia Indices, you should see a new record for our new Chec product!
Update an existing product
π¨ Make sure to replace SECRET_KEY_HERE
with your Chec Secret Key AND replace prod_4WJvlK6Rx5bYV1
with your product ID!
Using the id
from the response above, let's now update the product price!
curl --location --request PUT 'https://api.chec.io/v1/products/prod_4WJvlK6Rx5bYV1' \
--header 'X-Authorization: SECRET_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
"product": {
"name": "My test product",
"price": 2000
}
}'
If you open the Algolia Indices, you should see the updated product record with our new price!
Delete an existing product
π¨ Make sure to replace SECRET_KEY_HERE
with your Chec Secret Key AND replace prod_4WJvlK6Rx5bYV1
with your product ID!
curl --location --request DELETE 'https://api.chec.io/v1/products/prod_4WJvlK6Rx5bYV1' \
--header 'X-Authorization: SECRET_KEY_HERE'
The response will look like:
{
"id": "prod_4WJvlK6Rx5bYV1",
"deleted": true
}
If you open the Algolia Indices, you will now notice the record has been removed!
What's next?
Now we've a successful function that is triggered every time our Chec product catalog is modified, we can move onto creating a UI using Algolia InstantSearch to filter our products!
Top comments (0)