Prisma released a new feature that allows you to listen for database changes in a neat way that allows you to separate your code better.
It lets you to move logic like notification handling away from your business logic. I used it to send myself a Discord notification, but it could work for emails, or any other code that needs to run whenever your database is updated.
This is basically all you need:
const stream = await prisma.feedback.stream();
for await (const event of stream) {
console.log("New event:", event);
// do something here...
}
Not bad! I expected it to be much more complicated when I first learned about this.
Whenever an update in the database happens, you get an event like this:
New event: {
action: 'create',
created: {
id: 'clz71qaoiqmnbso12xm4s',
content: 'This is my feedback message!!!',
evaluation: 'positive',
createdAt: 2024-07-30T04:26:38.74Z,
updatedAt: 2024-07-30T04:26:38.74Z,
ownerId: 'clz82jndfgiuso23me3o'
},
id: '0kjansd9887sdfhjkasd98',
modelName: 'Feedback'
}
It’s a feature that you can enable by adding the Prisma Pulse extension. This takes a few lines of code when initializing your Prisma client by extending it using withPulse()
:
import { remember } from "@epic-web/remember";
import { Pool, neonConfig } from "@neondatabase/serverless";
import { PrismaNeon } from "@prisma/adapter-neon";
import { PrismaClient } from "@prisma/client";
import { withPulse } from "@prisma/extension-pulse";
import ws from "ws";
import { sendDiscordMessage } from "./discord";
neonConfig.webSocketConstructor = ws;
const connectionString = `${process.env.DATABASE_URL}`;
const pool = new Pool({
connectionString,
});
const adapter = new PrismaNeon(pool);
export const prisma = remember("prisma", () => {
const prisma = new PrismaClient({
adapter,
}).$extends(
withPulse({
apiKey: process.env["PULSE_API_KEY"] as string,
})
);
client.$connect();
return client;
});
Note: If you’re using Neon too, you should enable Logical Replication for your database. Prisma has a guide for this here: https://www.prisma.io/docs/pulse/database-setup/neon.
I used this to listen for updates on my database whenever someone submits a feedback form:
The Prisma schema for that looks as follows:
model Feedback {
id String @id @default(cuid())
content String
evaluation String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade, onUpdate: Cascade)
ownerId String
@@index([ownerId])
@@index([ownerId, updatedAt])
}
The great thing about this is that it’s fully type safe! All thanks to your Prisma schema. Here is an example of listening to the updates and sending a Discord notification when it happens:
async function prismaFeedbackStream() {
// wait for new database events
const stream = await prisma.feedback.stream();
const emojiMap = {
positive: "👍",
negative: "👎",
};
// for each new event
for await (const event of stream) {
// omly check for new table entries
if ("created" in event) {
const { content, evaluation } = event.created;
const emoji = emojiMap[evaluation] || "💬";
await sendDiscordMessage(`**${emoji} new feedback**\n\n${content}`);
}
}
}
prismaFeedbackStream();
The demo application is getting a lot of positive feedback as you can see 😁
And this is how to send a Discord notification:
export async function sendDiscordMessage(message: string) {
const payload = {
content: message,
};
try {
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
} catch (error) {
console.error("Error sending Discord message:", error);
}
}
Now this Discord example is pretty simple. That is intentional. In the real world it could be much more complex using the same tools.
For example, you could also have one application update the database, and have another application listen for the changes using Prisma Pulse.
If you’re writing Lambda functions, you could also keep your functions smaller by removing any sort of notification logic from the function.
At the very least it will let you decouple some code for sending notifications like Discord messages or emails. But maybe you have much more complex use-cases in mind already.
How is this possible?
Prisma connects to your Postgres database so they can check for updates. When Prisma sees something has changed, it will send an update to any listening clients connected via Prisma Pulse.
To connect Prisma to your database, you need to create a Prisma account.
Getting started with Prisma Pulse
To get started with Prisma Pulse, you can sign up for an account here https://www.prisma.io/?via=eddy (affiliate link)
If you want to use any paid offerings from Prisma, my link should give you a discount.
After you’ve created a workspace, you can create a project environment and select “Manage Pulse”
Then you’ll have to give Prisma your database connection string so that they can connect and listen for changes on your database:
The dashboard should show you a notification if it was setup correctly. (If not, please check if your database supports logical replication)
Example form in Remix (using the Epic Stack)
I open sourced my example on GitHub here: https://github.com/EddyVinck/remix-prisma-feedback-form
As a base, the repository uses Kent C. Dodds’ Epic Stack which comes with Prisma pre-configured except for the Pulse part.
The following files were modified to create this feedback form and the listener:
- The schema was modified to collect feedback in
prisma/schema.prisma
- The feedback form is rendered on the app root
app/root.tsx
- The feedback form component and server action can be found in the
app/routes/feedback/
directory - You can find the Prisma setup and feedback listener in
app/utils/db.server.ts
- The Discord message function is in
app/utils/discord.ts
Conclusion
I’m pretty happy with how it turned out. I can see the use-cases, especially if you have multiple separate applications connecting to the same database. The code to use Pulse itself is really straightforward and easy to understand, in my opinion. The hardest part for me was finding a suitable hosted database for the demo.
I really like how you can decouple pieces of code where you just want to insert something in a database and some other code that is supposed to send an email or something like I did with Discord.
If you’re already using Prisma, this functionality to listen for database updates might open up some possibilities for you. And if you’re not, it’s worth trying it out and seeing for yourself!
Please use my affiliate link if you’re going to try it! https://www.prisma.io/?via=eddy