Part 1 - Algolia powered product search with Commerce.js
Tags: tutorial, ecommerce, javascript, nextjs
Published: 2020-04-27 16:19:05

---
title: Part 1 - Algolia powered product search with Commerce.js
published: true
description: Configure Algolia, Setup Next.js and create a serverless function to sync Chec/Commerce.js to Algolia!
tags: tutorial, ecommerce, javascript, nextjs
series: Algolia powered product search with Commerce.js
---

In this series we will create an Algolia powered faceted product search for Chec. [Chec](https://commercejs.com) (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:

* [Next.js](https://nextjs.org/)
* [Algolia React InstantSearch](https://www.algolia.com/products/instantsearch/)
* [Chec webhooks](https://commercejs.com/blog/webhooks-for-commerce)

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`

![Algolia API Keys](https://dev-to-uploads.s3.amazonaws.com/i/kv9myasvklrbh0cu47pp.png)

## 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:

```bash
npm init next-app
# or
yarn create next-app
```

Once your project folder has been created, change directory and open your code editor.

```bash
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:

```bash
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.

```bash
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!**

```js
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:

```js
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...

```bash
npm run dev
# or
yarn dev
```

... and go to [`http://localhost:3000/api/algolia`](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!

![Algolia Indicies Page](https://dev-to-uploads.s3.amazonaws.com/i/y9yvcwy5j2d3ma9z4tfm.png)

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](https://dashboard.chec.io/setup/webhooks) 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`](https://ngrok.com) to get a URL that will handle tunnelling requests for us.

Once you've installed ngrok, inside a new terminal run:

```bash
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**!

![Chec Add Webhook Modal](https://dev-to-uploads.s3.amazonaws.com/i/w1nz2xust19143mpqt9f.png)

---

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

```js
{
  "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:

```js
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:

```js
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.

```js
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!

```js
res.send({
  message: `Event type ${eventType} is not a valid trigger`
})
```

Our `pages/api/algolia.js` function should look like...

```js
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!

```bash
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:

```js
{"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!

```bash
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!

```bash
curl --location --request DELETE 'https://api.chec.io/v1/products/prod_4WJvlK6Rx5bYV1' \
--header 'X-Authorization: SECRET_KEY_HERE'
```

The response will look like:

```js
{
  "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!