Based on the good work of verful/adonis-notifications
The @osenco/adonisjs-notifications
package requires @adonisjs/core >= 6.2.0
Also, it relies on @adonisjs/lucid >= 20.5.1
for database notifications and on @adonisjs/mail >= 9.2.1
for mail notifications.
Install the package from the npm registry as follows.
npm i @osenco/adonisjs-notifications
# or
yarn add @osenco/adonisjs-notifications
# or
bun add @osenco/adonisjs-notifications
Next, configure the package by running the following ace command.
node ace configure @osenco/adonisjs-notifications
And then add the path to the tsconfig.json
{
"extends": "@adonisjs/tsconfig/tsconfig.app.json",
"compilerOptions": {
//...other options,
"paths": {
//...other paths,
"#notifications/*": ["./app/notifications/*.js"]
}
}
}
and package.json
{
"name": "adonis-app",
"version": "0.0.0",
"imports": {
//...other imports,
"#notifications/*": "./app/notifications/*.js"
},
...
}
Notifications are represented by a simple class, generally stored in the app/Notifications
directory. If you dont see the directory, dont worry, it will be created when you run the make:notification
ace command.
node ace make:notification TestNotification
The command will create a notification class in the app/notifications
directory. Each notification class contains a via
method and any number of message builder methods, like toMail
or toDatabase
, that convert the notification to a message made for that channel.
Notifications may be sent using the notify
or the notifyLater
methods of the Notifiable
mixin, or using the Notification
module.
First, apply the mixin on the model you are wanting to notify.
import { BaseModel } from '@adonisjs/lucid/orm'
import { compose } from '@adonisjs/core/helpers'
import Notifiable from '@osenco/adonisjs-notifications/mixins/notifiable'
// Notifiable takes the notification table name as it's only param
export default class User extends compose(BaseModel, Notifiable('notifications')){
}
Then use the notify
or the notifyLater
methods to notify the model.
import { TestNotification } from '#/notifiacations/test_notification.js'
user.notify(new TestNotification())
// Uses a in-memory queue to send the notification
user.notifyLater(new TestNotification())
You can also use the Notification
module to send notifications. Sending notifications this way is useful when you need to send a notification to multiple notifiables, like a array of users.
import notification from '@osenco/adonisjs-notifications/services/main'
import { TestNotification } from '#/notifiacations/test_notification.js'
notification.send(users, new TestNotification())
You can also delay notifications using the sendLater
method. This method uses a in-memory queue to send the notifications.
import notification from '@osenco/adonisjs-notifications/services/main'
import { TestNotification } from '#/notifiacations/test_notification.js'
Notification.sendLater(users, new TestNotification())
Every notification class has a via
method that determines which channels will be used to deliver the notification.
If you want to use other delivery channels, you can build your own.
The via
method receives a notifiable
instance, that is a instance of the class which the notification is being sent. You may use the notifiable
to determine which channels to sent the notification to.
class TestNotification implements NotificationContract<User> {
public via(notifiable: User): NotificationChannelName | NotificationChannelName[] {
return notifiable.prefersEmail ? 'mail' : 'database'
}
}
Sending notifications can take some time, to ensure the notification doesn't block HTTP requests, you can use the notifyLater
method of the Notifiable
Mixin and the sendLater
method of the Notification
module to sms notifications to a in-memory queue, ensuring that notifications will be sent after the http request ends.
If you want to send a notification via e-mail, you should define a toMail
method on the notification class. This method receives the notifiable
entity and should return a BaseMailer
instance
If you want to use a mail driver other than default driver to send the notification, you can define it in the mailer class
// app/mails/test_mail.ts
export default class TestMail extends BaseMail {
from = '[email protected]'
subject = 'Test email'
constructor(private user: User) {
super()
}
prepare() {
this.message.to(this.user.email)
}
}
// app/notifications/test_notification.ts
class TestNotification implements NotificationContract<User> {
public toMail(notifiable: User){
return new TestMail(notifiable)
}
}
Mail notifications requires @adonisjs/mail >= 9.2.1
The database
channel stores the notification in a database table. This table contain the notification, and a JSON object that describes the notification
Database notifications requires @adonisjs/lucid >= 20.5.1
You can query the table to display the notifications in your UI. But, before you can do that, you need to create a table to store the notifications. You may use the notifications:table
ace command to generate a migration with the correct table schema.
node ace notifications:table
node ace migration:run
If you want to store a notification in a database, you should define a toDatabase
method on the notification class. This method receives the notifiable
entity and should return a javascript object that can be transformed in JSON
class TestNotification implements NotificationContract<User> {
public toDatabase(notifiable: User){
return {
title: `Hello, ${notifiable.email}, this is a test notification`
}
}
}
In the notification config file the DatabaseChannelData
Interface can be defined
// config/notification.ts
declare module '@osenco/adonisjs-notifications/types' {
interface DatabaseChannelData {
title: string
}
}
After notifications are stored, you can access them from your notifiable model entities. The Notifiable
mixin includes a notifications
Lucid relationship that returns the notifications for that entity. You can use the notifications like any other Lucid relationship. By default, the readNotifications
and unreadNotifications
methods will sort the notifications using the created_at
timestamp, with the most recent at the beginning.
const user = User.findOrFail(1)
for(const notification of await user.readNotifications()){
console.log(notification.data)
}
If you want to retrieve only the unread notifications, you may use the unreadNotifications
method.
const user = User.findOrFail(1)
for(const notification of await user.unreadNotifications()){
console.log(notification.data)
}
The notifications are normal Lucid Models, you can use anything that applies to a Lucid Model on them
Typically, you will want to mark a notification as read when a user views it. The notification model provides a markAsRead method, which updates the read_at column on the notification's database record:
const user = User.findOrFail(1)
for(const notification of await user.unreadNotifications()){
await notification.markAsRead();
}
If you want to mark all notifications of a user as read, you can use the markNotificationsAsRead
method of the Notifiable
mixin
const user = User.findOrFail(1)
await user.markNotificationsAsRead()
There is also
markAsRead
andmarkNotificationsAsUnread
methods to mark notifications as unread.
You may want to deliver notifications using other channels, for that, you can use any class that implements the NotificationChannelContract
// channels/sms.ts
import { NotifiableModel, NotificationChannelContract } from '@osenco/adonisjs-notifications/types'
interface SmsPayload {
text: string
}
export class SmsChannel implements NotificationChannelContract {
async send(data: SmsPayload, to: NotifiableModel) {
// Implement API call to send
}
}
After the channel is created, you must register the channel in the notification config at config/notification.ts
// config/notification.ts
import { SmsChannelContract } from '../app/channels/sms.js' // or "#channels/sms"
const notificationConfig = defineConfig({
channels: {
...
sms: () => new SmsChannel(),
},
})