# Step 5 - Subscribe to webhook events

By
The Quable Team

Webhook handling
1.Register webhook events
2.Listen to webhook events


# Webhook handling

In this tutorial, you'll discover how to subscribe to and handle Quable PIM webhook events. Webhooks serve as HTTP requests sent by applications to inform other systems about particular events in real-time. This enables seamless communication between different applications, allowing them to trigger actions on events as they occur.


# Requirements

We'll use the quable-pim-js library to interact with the Quable PIM API.


# 1. Register webhook events

# Define webhooks events

We'll start by defining the webhook events we want to listen to.
Open the quable.app.yml file located at the root of your project and add the following lines:

webhook_events:
  - document.create
  - document.update

To ensure that our application receives notifications of document creation and updates, we have defined two webhook events:

  • document.create: This event is triggered every time a new document is created.
  • document.update: This event is triggered every time an existing document is modified or updated.

webhook event

NOTE : A maximum of 5 events can be registered per webhook.


# Create the register method

To register the webhooks we've defined, create the following method in the app.service.ts file. This method will handle the registration process with the Quable PIM.

import { QuableInstance } from "@prisma/client";
import { QuablePimClient } from "@quable/quable-pim-js";
  private registerWebhooks = async (instance: QuableInstance, events: string[]) => {
    const quablePimClient = new QuablePimClient({
      apiKey: instance.authToken,
      instanceName: instance.name,
    });

    const url = `${process.env.QUABLE_APP_HOST_URL}/webhook/events`;
    const name = 'Quable App Template - Webhook';
    const payload = {
      active: true,
      name,
      url,
      events,
    };

    const webhooks = await quablePimClient.PimApi.Webhook.getAll({
      url,
      name,
    });

    return webhooks?.length > 0
      ? quablePimClient.PimApi.Webhook.update(webhooks[0].id, payload)
      : quablePimClient.PimApi.Webhook.create(payload);
  };

The registerWebhooks method manages the registration of webhooks on a Quable PIM instance. It accepts the instance details and a list of events to be registered. The method checks if a corresponding webhook with the same URL and name already exists. If it does, the existing webhook is updated with the provided data. Otherwise, a new webhook is created.


# Update app service

We're going to update the installApp method to register webhooks automatically during installation of the app:

public installApp = async (payload: Record<string, any>, events = []) => {
  const response = {
    statusCode: 201,
    message: 'OK',
    data: null,
  };


  try {
    const { quableInstanceName, quableAuthToken } = payload.data;

    if (!(await this.checkAuthToken(quableInstanceName, quableAuthToken))) {
      response.statusCode = 500;
      response.message = 'QuableApp could not be installed on your PIM. Please check your information and try again.';
      return response;
    }

    let instance = await databaseService.quableInstance.findFirst({
      where: { name: quableInstanceName },
    });

    const instanceData = {
      authToken: quableAuthToken,
      name: quableInstanceName,
    };

    instance = instance
      ? await databaseService.quableInstance.update({
        where: { id: instance.id },
        data: instanceData,
      })
      : await databaseService.quableInstance.create({ data: instanceData });

    await this.registerWebhooks(instance, events);

    response.message = `QuableApp installed on ${instance.name}.quable.com`;
  } catch (error) {
    response.statusCode = 500;
    response.message = error.message;
  }

  return response;
};

The updated installApp method will automatically register webhooks for the given instance on successfull installation.


# Update app controller

Update installApp method with the following :

public installApp = async (req: any, res: Response) => {
  const webhookEvents = req.app.settings?.webhook_events || [];
  const response = await appService.installApp(req.body, webhookEvents);
  return res.status(response.statusCode).send(response);
};

This updated implementation will retrieve the webhooks defined in the quable.app.yml file and pass them to the appService.installApp method for registration.


# 2. Listen to webhook events

Once we've configured the webhook events, we need to set up a listener to capture the events triggered by the webhooks. This means creating a controller and routes in our application.


# Webhook controller

We'll create a controller for handling incoming requests. Create a file named webhook.controller.ts inside the controllers folder and paste the following code:

import { Request, Response } from "express";

class WebhookController {
  public onWebhookReceived = async (req: Request, res: Response) => {
    console.log(req.body);
    return res.status(200).send({});
  };
}

export const webhookController = new WebhookController();

# Webhook routes

Create a new file named webhook.routes.ts in the routes directory and paste the following code:

import { webhookController } from "src/controllers/webhook.controller";
import { Router } from "express";
const webhookRouter = Router();

webhookRouter.post("/events", webhookController.onWebhookReceived);

export default webhookRouter;

After defining the routes, you must register the route in index.ts:

import webhookRouter from "./routes/webhook.routes";
/*
... code ...
*/
app.use("/webhook", webhookRouter);

Note: To ensure our webhook route is accessible without authentication, we must add it to the list of unprotected routes. Locate the helper/session/route.ts file and update the unProtectedRoutes variable to include webhook route:

const unProtectedRoutes = [
  "/install",
  "/uninstall",
  "/permission",
  "/webhook/events",
];

Open your partner portal dashboard application page to uninstall and reinstall you application to register webhooks

[Re-install appliation list

Once a document has been updated or created, we'll receive a request webhook endpoint with a similar payload.

{
  uuid: '0963114b-c3d9-4a5e-9a33-ccc493b73649',
  code: 'document.update',
  links: [
    {
      rel: 'self',
      href: '/api/events/0963114b-c3d9-4a5e-9a33-ccc492b73629'
    }
  ],
  created_at: '2023-11-03T08:58:00+00:00',
  created_by: 'quable',
  resource: {
    code: 'vans-sh-8-hi',
    type: 'document',
    document_type_code: 'product',
    links: [ [Object] ],
    locales: { updated: [Array], inherited: [Array] },
    updated_items: []
  },
  process: { correlationId: 'bca5bc9d-6250-4049-8o22-7bfd2f9ce9f1' }
}