DEV Community

Cover image for How to use Novu with Nuxt.js
Michał Dziuba
Michał Dziuba

Posted on • Updated on

How to use Novu with Nuxt.js

Note: Tutorial is outdated

In this article, I will guide you through integrating Novu, an open source notification infrastructure, with Nuxt.js, the popular Vue.js framework.

TL;DR

We will integrate Novu notification center with HMAC encryption enabled and then we will trigger notification in Nuxt.js using Novu SDK.
Full code is available in my GitHub repository.

Novu dashboard

Let's say you have defined Novu workflow. If you click "Trigger notification" button, you will find subscriberId of your test user.

In Novu, Subscriber is an entity that will receive notifications, so in real app you would use users from your database as subscribers.

Novu testing workflow

You can find credentials in "Settings" page. Application ID can be stored safely on client-side but API Key must be secret and stored on server-side only.

Novu credentials

For security reasons - enable HMAC encryption for Novu In-App integration on "Integration Store" page.

HMAC encryption will make sure that a subscriberId is encrypted using the secret API key, and those will prevent malicious actors from impersonating users.

Novu HMAC

Nuxt.js setup

Create a new Nuxt.js project by running the code snippet below:

npx nuxi@latest init app
Enter fullscreen mode Exit fullscreen mode

Install Novu notification center integration for Vue.js:

npm install @novu/notification-center-vue
Enter fullscreen mode Exit fullscreen mode

Create .env file and paste your values from Novu dashboard:

NOVU_SUBSRIBER_ID='<YOUR_SUBSCRIBER_ID>'
NOVU_API_KEY='<YOUR_NOVU_API_KEY>'
VITE_NOVU_APP_ID='<YOUR_NOVU_APP_ID>'
Enter fullscreen mode Exit fullscreen mode

I use TailwindCSS for styling - you can find Nuxt.js configuration guide on official Tailwind docs.

Now, define client-side plugin:

plugins/novu.client.ts

import NotificationCenterPlugin from '@novu/notification-center-vue';
import '@novu/notification-center-vue/dist/style.css';

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(NotificationCenterPlugin);
});
Enter fullscreen mode Exit fullscreen mode

Nuxt.js automatically recognizes the .client.vue extension and ensures that the plugin is only included in the client-side bundle.

In the next step, we wrap Novu notification center in client-side component.

We simulate request for user session data - we want to obtain subscriberId (user's id from database) and HMAC hash generated on the server.
We will add missing API endpoint handler later.

components/NotificationCenter.client.vue

<script lang="ts" setup>

const { data, status } = await useAsyncData('session', () =>
  $fetch('/api/user')
)

const applicationIdentifier = import.meta.env.VITE_NOVU_APP_ID;
const sessionLoaded = () => {
  console.log('Notification center session loaded successfully!');
};

</script>
<template>
  <NotificationCenterComponent
    v-if="status == 'success' && data"
    v-slot="slot"
    :subscriber-id="data.userId"
    :subscriber-hash="data.hmacHash"
    :application-identifier="applicationIdentifier"
    :session-loaded="sessionLoaded"
  >
    <button 
      class="relative w-8 h-8 m-auto"
      :class="{'before:bg-red-700 before:w-2.5 before:h-2.5 before:rounded-full before:absolute before:top-0 before:right-0': slot.unseenCount > 0}"
      >
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/>
        <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>
      </svg>
    </button>
  </NotificationCenterComponent>
</template>
Enter fullscreen mode Exit fullscreen mode

Now put our notification center component inside the header:

Header.vue

<template>
<Container>
  <header class="w-full pt-6 lg:pt-8 flex gap-4 items-center justify-between">
    <h1 class="font-black text-lg">App</h1>
    <nav class="flex items-center gap-8 font-medium text-sm">
      <nuxt-link to="/">Home</nuxt-link>
      <nuxt-link to="/about">About</nuxt-link>
      <NotificationCenter />
    </nav>
  </header>
</Container>
</template>
Enter fullscreen mode Exit fullscreen mode

Container.vue

<template>
  <div class="px-6 mx-auto max-w-[1200px]">
    <slot />
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Create hero section with button to trigger notification:

Trigger.vue

<script setup>
function triggerNotification() {
  fetch('/api/trigger', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
  });
}
</script>

<template>
<Container>
  <div class="py-24 md:py-32 flex flex-col items-center">
    <a href="https://docs.novu.co/getting-started/send-your-first-notification"
      target="_blank"
      class="px-3 py-1 text-sm font-medium border border-[#e500c0] text-[#e500c0] rounded-full text-center mb-4">
      Check Novu docs 🚀
    </a>
    <h1 class="text-5xl md:text-6xl font-extrabold text-center">Trigger notification</h1>
    <p class="text-center text-slate-900 text-lg mt-4">
      Click the trigger button and then check the notification center by clicking the bell.
    </p>
    <div class="flex justify-center gap-4 mt-8">
      <button @click="triggerNotification" class="bg-black hover:bg-black/80 text-white font-medium px-8 py-3 rounded-md">Trigger</button>
    </div>
  </div>
</Container>
</template>
Enter fullscreen mode Exit fullscreen mode

Server-side integration

Install Novu Node.js SDK (SDK is intended for use exclusively in server-side applications):

npm install @novu/node
Enter fullscreen mode Exit fullscreen mode

Now, add our missing API handlers:

server/api/trigger

import { Novu } from '@novu/node';

const novu = new Novu({
  apiKey: process.env.NOVU_API_KEY,
});

export default defineEventHandler(async (event) => {
  console.log(`Received event: ${event}`);

  await novu.trigger('sample-workflow', {
    to: {
      subscriberId: process.env.NOVU_SUBSRIBER_ID!,
    },
    payload: {}
  });
});
Enter fullscreen mode Exit fullscreen mode

Last step - encrypt subscriberId with HMAC and return user data. In real app you would fetch currently authenticated user from database.

server/api/user

import { createHmac } from "crypto";

export default defineEventHandler(async (event) => {
  console.log(`Received event: ${event}`);

  const userId = process.env.NOVU_SUBSRIBER_ID!;

  const hmacHash = createHmac("sha256", process.env.NOVU_API_KEY!)
    .update(userId)
    .digest("hex");

  return {
    userId,
    hmacHash,
  }
});
Enter fullscreen mode Exit fullscreen mode

What next?

If you want to customize your notification center, there is Novu Headless Library. Novu Headless is plain-JS library and it abstracts underlying real-time WebSockets events and REST API calls.

You can also define your workflows programatically (code-first approach), using Novu Echo. Echo supports Nuxt.js out of the box, so check it out:

Top comments (0)