Building a Secure Real-Time Wellness Platform with PubNub
The wellness and mental health technology market is growing rapidly, expected to reach $30.98 billion by 2030. While healthcare applications managing clinical data must adhere strictly to regulations such as NHS guidelines in Europe and HIPAA in the U.S., wellness applications—though typically less regulated—still require security, privacy, and real-time interactions. PubNub’s infrastructure meets these standards, providing developers a secure, reliable entry point into building wellness platforms.
This blog will explain how developers can leverage PubNub features to create an engaging, scalable wellness application. We will also explore different use cases and run through how to implement PubNub into a web application.
Industry Context: The Importance of Security and Real-Time Experiences
There are many types of wellness applications, the most common being fitness, therapy and patient applications. All these applications heavily rely on real-time interactions, from chat to synchronizing group therapy sessions.
While these applications serve different uses, they all have one major thing in common: their security standards. While some apps don’t handle sensitive electronic health records (EHR), they still need to meet data security standards such as being SOC2 and HIPAA compliant, ensuring these platforms can handle sensitive information.
PubNub helps secure your application, as our platform provides:
HIPAA-compliant infrastructure
<30ms latency with global delivery and unlimited concurrency
Robust presence and scalability features
Ease of integration across multiple platforms
We will now begin implementing PubNub into a well-being application to show how you can set up these real-time interactions while fully securing your application. Here are some use cases you will know how to implement by the end of this tutorial.
Immediate Therapist-Patient Communication
The first thing we will build out throughout this tutorial is the therapist-patient chat functionality. With PubNub’s real-time messaging and presence, we will be able to enable therapists and patients to not only communicate with each other, but also identify when they are online or offline. This chat will be secure and the patients data will be protected.
Community Support Groups
Next, we will implement community support groups for our application, enabling patients to have a global chat where they can share their stories. This will differ from the therapist and patient communication because everyone will have access to this community as long as they are authenticated on the application.
Synchronized Meditation Sessions
The last feature we will implement is synchronized meditation sessions for patients. Patients will be able to receive guided instructions, ensuring the experience is consistent for everyone. Think about a Zoom call or an online yoga class. We aim to make those features inside the application more immersive by providing features that allow our users to engage in real-time.
Channel Organization Explained
PubNub uses channels to manage and secure communications effectively. Clear and consistent naming conventions help maintain data privacy and ensure efficient real-time interactions. Later in this blog, we will explain how these channels are used and why this matters.
const CHANNEL_PATTERNS = {
direct: (user1, user2) => `direct_${[user1, user2].sort().join('_')}`,
journal: patientId => `journal_${patientId}`,
notes: therapistId => `notes_${therapistId}`,
community: type => `community_${type}`,
meditation: sessionId => `meditation_${sessionId}`,
presence: userId => `wellness_user_${userId}`
};
In short, the channel structure ensures that:
Private patient data stays private (journal_123 only goes to patient 123)
Group sessions are synchronized (meditation_session456 reaches all participants in that session)
Presence updates are efficient (wellness_user_123 only tracks user 123)
Initializing the PubNub ChatSDK with Access Manager
PubNub's Chat SDK provides the foundation for real-time communication in wellness applications. The PubNub Chat SDK automatically handles all the complexity of real-time messaging, presence, and message history.
ChatSDK Initialization
This tutorial will show you how to use the ChatSDK to create real-time interactions in your wellness application. Let’s first look at how you would initialize the ChatSDK for your project. The code below is how you initialize the ChatSDK for any JavaScript application.
import { Chat } from '@pubnub/chat';
// Initialize Chat SDK with user context
const chat = new Chat({
publishKey: “publishKey”,
subscribeKey: “subscribeKey”,
userId: user.id,
}
});
However, we must retrieve our publish and subscribe keys from the PubNub dashboard by creating an application and inserting the keys into the “publishKey” and “subscribeKey” values. Don’t worry, it is very easy to create. Navigate to PubNub and sign up for an account. Once you have an account, navigate to Apps & Keysets and select Create App. After creating an App on the PubNub Dashboard, you must configure your keyset. For this application, we will need to enable a couple of things. However, you can configure your application's keyset to fit your needs.
Enabling Presence First, we will enable presence on our keyset. This will allow us to detect when people join and leave channels. In short, it will let people on our application know when patients or therapists are online. To do this, scroll down to the presence tab under configuration when you are on the Apps & Keysets page.
Enabling Access Manager At the start of this tutorial, we discussed security as the common trait of all wellness applications. To achieve a this level of data privacy and security in out application application, we will need to enable Access Manager. This means we will also need to set up an external server later on to set permissions depending on the user. For example, patients could have different permissions to data than therapists.
Enabling App Context App context allows us to store user metadata and pull in updates in real time. For example, if they earned an award on the application or we needed details about how much they journaled throughout the week, we can store this type of data in PubNub AppContext. Therapists can then pull in this data and see the most up-to-date results.
Different events are available depending on what you want to see updated in real time. For example, binding to User Metadata Events will allow you to see the most up-to-date information about a user's metadata. Channel Metadata Events will allow you to bind to channel metadata. For example, a channel can be in the form of a meditation session and allow you to see the most up-to-date metadata about it.
We will need these three configurations for this tutorial in our PubNub keyset.
Initializing Access Manager
Access manager provides security through token-based authentication. This is crucial for a protecting your users data to ensure patients only access their own data. However, Access Manager requires a secure server to generate tokens. We must make an API to create a token using the PubNub SDK.
const express = require('express');
const cors = require('cors');
const PubNub = require('pubnub');
const app = express();
app.use(express.json());
app.use(cors());
// PubNub configuration with your keys
const pubnub = new PubNub({
publishKey: 'your-publish-key-here',
subscribeKey: 'your-subscribe-key-here',
secretKey: 'your-secret-key-here', // Required for Access Manager
userId: 'server-admin'
});
// Token generation endpoint
app.post('/grant-token', async (req, res) => {
const { UUID, role } = req.body;
// Generate token configuration based on user role
const tokenConfig = generateTokenConfig(UUID, role);
// Grant token using PubNub
pubnub.grantToken(tokenConfig, (status, token) => {
// Parse token to verify it was created correctly
const parsed = pubnub.parseToken(token);
res.json({
success: true,
token: token,
expiresAt: parsed.exp * 1000,
permissions: parsed
});
});
});
app.listen(3001, () => {
console.log('🔐 Access Manager server running on port 3001');
});
The generateTokenConfig function will grant different permissions depending on who is trying to access the application. These are permissions for channels or other users' metadata referring to AppContext. We can either grant more access or limit users' access depending on who the user is. This is an example of what we used for our wellbeing application we implemented with PubNub. To read more about Access Manager and how you can grant permissions be sure to check out the Access Manager documentation.
function generateTokenConfig(userId, userRole) {
const config = {
ttl: 60, // Token valid for 1 hour
authorized_uuid: userId,
resources: { channels: {} },
patterns: { channels: {} }
};
switch (userRole) {
case 'patient':
// Patients access their own private channels
config.resources.channels[`journal_${userId}`] = {
read: true, write: true, get: true, join: true
};
config.resources.channels['community-support'] = {
read: true, write: true, join: true
};
// Can join meditation sessions but not control them
config.patterns.channels['meditation_*'] = {
read: true, write: false, join: true
};
break;
case 'therapist':
// Therapists get broader access
config.resources.channels[`notes_${userId}`] = {
read: true, write: true, manage: true, join: true
};
config.resources.channels['crisis-alerts'] = {
read: true, join: true
};
// Can read patient journals and control meditation sessions
config.patterns.channels['journal_*'] = {
read: true, write: false, join: true
};
config.patterns.channels['meditation_*'] = {
read: true, write: true, manage: true, join: true
};
break;
case 'admin':
// Admins get full access to everything
config.patterns.channels['*'] = {
read: true, write: true, manage: true, delete: true
};
break;
}
return config;
}
Understanding Permission Types
Each permission serves a specific purpose in wellness applications:
read: Can receive messages from the channel
write: Can send messages to the channel
get: Can retrieve message history
manage: Can modify channel settings and metadata
update: Can modify channel/user metadata
join: Can subscribe to the channel
delete: Can delete messages or channels (usually admin-only)
Why This Security Model Works for Wellness Apps
Patient Privacy: Patients can only access their own journal and presence channels
Therapist Oversight: Therapists can read patient journals but not modify them
Session Control: Only therapists can manage meditation sessions
Crisis Management: All therapists see crisis alerts, but patients cannot generate false alerts
Audit Trail: Every action is logged with the user's token information
Client-Side Token Management
Now that we have our private server set up, which will grant us server tokens, we will need to request these tokens from our server.
// Request a token from your server
async requestToken(userId, userRole) {
const response = await fetch(`${this.serverEndpoint}/grant`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
UUID: userId,
role: userRole
})
});
const data = await response.json();
this.currentToken = data.token;
this.scheduleTokenRefresh(data.expiresAt);
return data.token;
}
Now that we have our access manager token based on the signed-in user's role, we can finish initializing the ChatSDK. This time, we will insert the authKey into the initialization to use the ChatSDK.
import { Chat } from '@pubnub/chat';
// Initialize Chat SDK with user context
const chat = new Chat({
publishKey: “publishKey”,
subscribeKey: “subscribeKey”,
userId: user.id,
authKey: token
});
Creating and Managing Channels
This section will develop secure communication between a patient and a therapist. Think of channels as rooms enabling conversations or engagement, each with its own functionality and security.
Let’s get into what channels we will create and their functionality.
Therapy channels in this application will be private, secure, and shared between patients and therapists. The channel will automatically encrypt messages, and no other user on the app will have access to this channel.
Community channels will be a moderated group space where patients can support each other. This channel will allow every application user to participate. You can see how this channel has less security than the therapy channels.
However, channels don’t have to just revolve around chat functionality.
Meditation Channels coordinate live group sessions. They synchronize timing across all participants, displaying whether the session is paused or ended and how much time is left. This channel enables therapists to host any type of session for their patients and increase engagement through chat and guidance messages.
Creating a Therapy Chat
Since we have Access Manager set up, this simple code will create a secure channel that only the specified patient and therapist can access assuming everyone on your application has differend IDs.
This is achieved by putting the patientId and therapistId into the channel name. Assuming no two users have the same ID, no other user should have access to this channel.
// Create a private therapy conversation between patient and therapist
const therapyChannel = await chat.createDirectConversation({
user: therapistUser, // The other participant
channelData: {
name: 'Therapy Session',
description: 'Private therapy conversation',
custom: {
type: 'therapy',
isConfidential: true,
sessionCount: 0
}
}
});
The ChatSDK can also support end-to-end encryption if the Crypto module is specified during initialization of the Chat object.
import { Chat } from '@pubnub/chat';
// Initialize Chat SDK with user context
const chat = new Chat({
publishKey: “publishKey”,
subscribeKey: “subscribeKey”,
userId: user.id,
authKey: token,
cryptoModule: CryptoModule.aesCbcCryptoModule({
cipherKey: "wellness-app-encryption-key-2024"
}),
});
Now, sending a message is very simple. We can use the therapyChannel object to achieve this. We can even send custom metadata with these messages to include more information, such as the emotion level or if the message is confidential.
// Send a therapy message with emotion tracking
await therapyChannel.sendText('I had a difficult day today', {
meta: {
messageType: 'therapy',
emotionLevel: 'negative', // Helps therapist prioritize
isConfidential: true,
requiresResponse: true
}
});
In a real-world scenario, you can see how this could be important. If therapists knew their patients needed an immediate response, it could lead to greater response times and a more detailed response, depending on how the user sent the message.
The key idea here is that every message carries context. When a therapist sees messages now, they will immediately understand the patient’s emotional state and can respond appropriately.
Next, we need to be able to receive the message. To do this, we can take the therapyChannel and connect to it, which means we can receive any message sent through that channel. We can also add custom functionality, such as detecting our patients' emotional states.
// Listen for different types of events
await therapyChannel.connect((message) => {
const messageData = {
text: message.content.text,
emotionLevel: message.custom?.emotionLevel,
requiresResponse: message.custom?.requiresResponse
};
// Handle crisis messages immediately
if (messageData.emotionLevel === 'crisis') {
handleCrisisMessage(messageData);
}
});
This creates a responsive, human-feeling experience. Patients see immediate feedback when therapists respond, and the system automatically prioritizes urgent messages. The result is digital communication that feels as immediate and supportive as an in-person conversation.
The ChatSDK can be used for much more functionality, such as typing indicators and read receipts, and this is just the starting point of what you can develop. To expand this functionality, please check out the documentation.
Community Chat and Public Forums
Community channels operate differently from private therapy chat systems in that they are open to everyone on the application. Any verified user on your system should be able to join these channels as long as they have authenticated themselves. Messages are also visible to all participants and stored for newcomers to read previous discussions.
const communityResult = await chat.createGroupConversation({
users: [moderatorUser1, moderatorUser2], // Initial moderators
channelId: 'community_anxiety-support',
channelData: {
name: 'Anxiety Support Group',
description: 'Peer support and discussions for anxiety management',
custom: {
type: 'community',
supportType: 'anxiety',
isModerated: true,
isPublic: true
}
},
membershipData: {
custom: {
role: 'moderator' // Initial users are moderators
}
}
});
The key difference is that the public channels use createGroupConversation() instead of createDirectConversation() because it supports multiple participants. In this case, I am setting the initial users who created the community to moderators by setting their Membership for the channel to a specific role.
In the same way, we can still send messages in the community as previously explained for the therapist and patient chat.
// Send a supportive message with category
await communityResult.sendText('You\'re not alone in this journey!', {
custom: {
messageType: 'community',
supportType: 'encouragement', // Helps others understand intent
isPublic: true,
allowReactions: true // Enable emoji reactions
}
});
Live Meditation Sessions
Channels in PubNub don't have to be chats. As explained before, you can think about them as rooms that increase your user engagement. Meditation channels are a perfect example of this concept. Instead of just exchanging messages, these channels coordinate live group sessions where timing, instructions, and participant presence are synchronized across all users.
In this case, we will be setting up a meditation session to track messages and sync session metadata across all users, including duration, current phase, and participant count. This is where custom metadata inside the channel becomes important.
// Create a meditation session channel
const meditationResult = await chat.createGroupConversation({
users: [instructorUser], // Therapist who will guide the session
channelId: `meditation_${sessionId}`,
channelData: {
name: 'Morning Mindfulness Session',
description: 'Guided meditation for anxiety relief',
custom: {
type: 'meditation',
sessionId: sessionId,
instructor: instructorUser.name,
duration: 20, // 20 minutes
phase: 'waiting', // waiting, active, paused, completed
startTime: null,
}
}
});
The key to synchronized meditation is publishing timing updates that all participants receive simultaneously. Instead of each device running its own timer, one authoritative device (In this case, the therapist) will keep everyone in sync. We achieve this in the same way as just sending a message through a chat or community channel.
// Publish timing update to all participants
await meditationChannel.sendText('', {
custom: {
messageType: 'session_timing',
elapsed: 300, // 5 minutes elapsed
remaining: 900, // 15 minutes remaining
phase: 'meditation',
progress: 25 // 25% complete
}
});
This is not a chat message; it is a signal to coordinate everyone's timers that are currently in the session, ensuring everyone is seeing the same progress bar and phase transitions. Participants can listen to these timing signals to set their own clock on their side. This way, they will constantly know what phase and time is left during the meditation session.
// Listen for meditation session events
await meditationChannel.connect((message) => {
const messageType = message.custom?.messageType;
switch (messageType) {
case 'session_timing':
updateProgressBar(message.custom.progress);
updateTimer(message.custom.remaining);
break;
case 'phase_change':
transitionToPhase(message.custom.newPhase);
break;
}
});
The channel becomes a synchronization hub that transforms individual meditation into a shared, guided experience globally. The result is that, instead of separate meditation apps, users experience a live, synchronized session where everyone breathes together, receives guidance at the same moments, and feels connected to the group - all coordinated through PubNub's real-time messaging infrastructure. This has proven to increase overall user engagement.
Continue Building with PubNub
This tutorial covered setting up a secure, scalable, real-time infrastructure by utilizing the PubNub ChatSDK. We implemented direct one-on-one therapist and patient chat, community forums, and live meditation sessions with relatively short code snippets. There is a lot of internal logic and playing around with different features of the ChatSDK that still needs to be done. However, this blog showcased the ease of implementation when building out a full-scale, fully distributed application.
To continue building secure wellness applications, check out the following links:
Start building now with PubNub by signing up for a free account or to experience the demo contact our sales team.