Zero
Zero
Back

Use Pusher to Implement Real-Time Notifications

Pusher makes it easy to add pub-sub functionality to your web apps, allowing you to implement chat, notifications, and more.

Sam Magura

Sam Magura

A door handle

Most rich web applications have real-time features, in which the server notifies the frontend that an event or update has occurred. Pushing from the server is necessary to implement many common use cases, such as chat, in-app notifications, and live updates to data displays like charts.

Real-time features have traditionally been implemented using a web socket connection between the server and the web browser. This approach generally works well, but it adds complexity when you have a pool of servers, since each web socket connection must be owned by a specific server. In this case, relying on an external service to implement real-time features can simplify your architecture.

This post will demonstrate how to use one such service, Pusher . With Pusher, your server publishes an event to Pusher, and your web or mobile client subscribes to receive events via the Pusher client SDK. The event is routed through Pusher's infrastructure, so you don't have to worry about scaling your own resources.

Secure your secrets conveniently

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.

Zero dashboard

What We're Building

To demonstrate how to use Pusher, we'll implement real-time in-app notifications with Pusher in a Next.js app. Our app will display a form that allows you to send a notification. The backend will then broadcast this notification to all browsers that have the app open.

The Pusher server SDK requires a secret API key, which we will manage securely via the Zero secrets manager.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Signing Up for Pusher

Click here  to sign up for a Pusher account. Create a new application using the Channels service. Then choose the technologies and cluster for your application. Select JavaScript for the frontend, and Node.js for the backend.

This will display a getting started guide that includes your Pusher appId, key, secret, and cluster. The key and cluster are not secret, so we will provide them to our app via a .env file. Go ahead and create that .env file on your computer — we will copy it into the project in the next section.

.env
NEXT_PUBLIC_PUSHER_KEY="YOUR_PUSHER_KEY" NEXT_PUBLIC_PUSHER_CLUSTER="YOUR_PUSHER_CLUSTER"

The environment variables are prefixed with NEXT_PUBLIC to indicate that they are not secret and should be made available to client-side code.

The secret value should not be committed to the .env file, since secrets must not be committed to git or made available to client-side code. Instead, we'll provide the secret to the app securely via the Zero secrets manager. Log in to Zero, and create a new project. Then add a new secret that includes appId and secret:

Creating the Pusher secret in Zero
Creating the Pusher secret in Zero

Creating the Next.js App

Now, let's go ahead and create a new Next.js app to house our code:

Terminal
npx create-next-app@latest pusher-app

Make sure to select the App Router at the prompt. The .env file you created in the previous section can now be copied into the root directory of the Next.js app.

cd into the project's directory and install the necessary dependencies:

Terminal
npm install pusher pusher-js @types/pusher-js @zerosecrets/zero

pusher is the server-side SDK, pusher-js is the JavaScript client-side SDK, and @zerosecrets/zero is the Zero TypeScript SDK  which will be used to retrieve the Pusher secret at runtime.

Creating Notifications

Our app will simply allow the user to manually send a notification that will be broadcast to all clients. To do this, we'll write an HTML form that accepts the notification text as input. When the form is submitted, it will POST to an /api/notify API method which will publish an event to Pusher from the server.

app/page.tsx
export default function Home() { const [inputValue, setInputValue] = useState('') async function submit() { await fetch('/api/notify', { method: 'POST', body: JSON.stringify(inputValue), }) } return ( <main className="p-24"> <h2 className="text-2xl mb-4 font-bold">Send a Notification</h2> <form noValidate onSubmit={(e) => { e.preventDefault() submit() }} className="mb-10" > <input className="input input-bordered mr-2" value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> <button className="btn btn-primary">Send notification</button> </form> <NotificationDisplay /> </main> ) }

<NotificationDisplay /> is a component we'll implement later to display the notifications that have been received.

Initializing the Pusher Server SDK

Before implementing the notify API method, we should initialize the Pusher SDK on the server. This will require the key and cluster from the .env file, and the appId and secret from Zero.

The code for this will go in a new file called util/pusher.ts. In this file, we implement the standard pattern for fetching secrets from Zero and passing them to a 3rd party SDK:

util/pusher.ts
import Pusher from 'pusher' import {zero} from '@zerosecrets/zero' let pusher: Pusher | undefined export async function getPusher() { if (pusher) { return pusher } if (!process.env.ZERO_TOKEN) { throw new Error('Did you forget to set the ZERO_TOKEN environment variable?') } const secrets = await zero({ token: process.env.ZERO_TOKEN, pick: ['pusher'], }).fetch() if (!secrets.pusher) { throw new Error('Did not receive Pusher secret.') } pusher = new Pusher({ appId: secrets.pusher.app_id, key: process.env.NEXT_PUBLIC_PUSHER_KEY!, secret: secrets.pusher.secret, cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!, useTLS: true, }) return pusher }

Now, our API method can call getPusher to get a fully-initialized Pusher client.

Implementing the API Method

In Next.js with the App Router, API methods are implemented via Route Handlers . To create the /api/notify method, place the following code in the file app/api/notify/route.ts:

app/api/notify/route.ts
import {getPusher} from '@/util/getPusher' export async function POST(request: Request) { const content = await request.json() const pusher = await getPusher() pusher.trigger('notifications', 'notification', content) return Response.json(null) }

The code here is very simple. The request content is expected to be a JSON string that contains the notification content. Then, we call getPusher to get an instance of the Pusher server SDK. Next is the key line of code, where we trigger an event in Pusher. Here, notifications is the name of the channel, and notification is the name of the event.

Subscribing to Notifications

On the frontend, the Pusher client SDK can be initialized like this, using the variables from .env:

app/NotificationDisplay.tsx
'use client' import Pusher from 'pusher-js' const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, { cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!, })

Then, we can subscribe to the notifications channel in a useEffect. When a notification event is received, we append its data (the notification content) to an array in React state. We can then display this array as a list, to show the notifications that have been received.

app/NotificationDisplay.tsx
export function NotificationDisplay() { const [notifications, setNotifications] = useState<string[]>([]) useEffect(() => { const channel = pusher.subscribe('notifications') channel.bind('notification', (data: string) => { setNotifications((n) => [...n, data]) }) return () => { channel.unbind_all() pusher.unsubscribe('notifications') } }, []) return ( <div> <h2 className="text-2xl mb-4 font-bold">Received Notifications</h2> <ul> {notifications.map((n) => ( <li key={n}>{n}</li> ))} </ul> </div> ) }

To run the app, provide your Zero token as an environment variable:

Terminal
ZERO_TOKEN='YOUR_ZERO_TOKEN' npm run dev

Here's what my app looks like:

The demo app, showing notifications that have been received
The demo app, showing notifications that have been received

To test that it's really working, open multiple browser tabs to the same page, and verify that each notification appears in every tab, not just the one that created the notification.

Next Steps

This post showed the basic usage of Pusher in a Next.js application, using Zero to securely manage the Pusher secret key. To build this type of functionality into a real app, you'll need to get more creative with the channel names provided to Pusher. In my demo project, each notification is sent to all clients, but in a real application, each user should receive completely personalized notifications. To implement this, you would likely include the user's ID in the name of the channel. Then, the backend needs to know which users should receive a given notification so it can publish to the appropriate channels.


Other articles

A 3D render of abstract shapes

Integrating Auth0 with Next.js for Authentication

Quickly set up authentication in a Next.js app with Auth0 so you don't have to code it yourself.

Old-school mailboxes at a post office

Using Notion as a Human-Readable Database

Capture form submissions from your web app and store them where your team works.

Secure your secrets

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.