Curiosity keeps leading us down new paths. ~ Walt Disney
It is often said that in programming there's always more than one way of doing things. The maxim is that to grow we must be open to new ideas...new ways of doing things.
As developers, we always try to explore different ways of doing things.
In this second piece of implementing events and mailing in Adonis, I'll demonstrate another technique we can use to capture different events in our applications.
A reminder that the entire source code for this project can be found here.
Let's start.
Using the built-in Adonis event emitter module
For this guide, we'll use the built-in Event emitter module that comes in AdonisJs. You can peruse the official event documentation to have a better understanding of this concept.
We'll implement the same flow we did in our previous article, where the user receives a notification email upon registration to activate their account, so be sure to check it out!
What is the AdonisJs event module?
According to the adonis documentation, "The AdonisJS event emitter module is built on top of emittery".
Emittery is a modern async event emitter for node.js.
Some basic knowledge of the node.js event loop would help you better understand it but is not necessary.
Usage
node ace make:prldfile events
This command creates a new events.ts file in the contracts directory. This file is the entry point for all events in our application. Select all options prompted by the CLI.
import User from 'App/Models/User'
declare module '@ioc:Adonis/Core/Event' {
interface EventsList {
'new:user': { newUser: User }
}
}
The Event.on
method registers a new event listener. It accepts the name of the event, in our case new:user
, followed by a method to handle the events as the arguments.
Listener Classes
Listener classes define the logic for our events. This is similar to the model function we defined in the last post.
Conventionally event listeners are stored inside the app/Listeners directory. However, you can customize the namespace inside the .adonisrc.json
file.
To make a new event listener class that will handle the emailing, run the following command.
node ace make:listener User
A new User.ts
file will be created under the app/Listeners/
directory. Open the newly created file and paste this code.
import Mail from '@ioc:Adonis/Addons/Mail'
import Env from '@ioc:Adonis/Core/Env'
import { EventsList } from '@ioc:Adonis/Core/Event'
import Route from '@ioc:Adonis/Core/Route'
export default class User {
public async onNewUser({ newUser }: EventsList['new:user']) {
const appDomain = Env.get('APP_URL')
const appName = Env.get('APP_NAME')
const defaultFromEmail = Env.get('DEFAULT_FROM_EMAIL')
const currentYear = new Date().getFullYear()
const url = Route.builder()
.params({ email: newUser.email })
.prefixUrl(appDomain)
.makeSigned('verifyEmail', { expiresIn: '24hours' })
await Mail.send((message) => {
message
.from(defaultFromEmail)
.to(newUser.email)
.subject('Please verify your email')
.htmlView('emails/auth/verify', { user: newUser, url, appName, appDomain, currentYear })
})
}
}
As you can see, the code above is very similar to what we'd defined earlier as the sendVerificationEmail()
function. For a more detailed explanation, head over to that article and check out the description.
However, just as a recap, we're defining the mail sending capability and building a URL that will encode our user token. The token expires in 24 hours and is tied down to a named URL, verifyEmail
. Now, onto the new stuff.
public async onNewUser({ newUser }: EventsList['new:user'])
We're defining an async function named, onNewUser
inside the default User class that takes the newUser
as an argument. The newUser argument is tied to the event we just defined before. There, it'll always ensure that the parameters passed will match the ones defined in the event declaration.
If you wish to pass more than one argument, you can always define them in the events.ts
file by separating them with semicolons.
'new:user': { newUser: User; <arg2>: <modelName; <arg3>: <modelName;... }
Then calling the same arguments on the function declaration
public async onNewUser({
newUser,
arg2,
arg3,
....
})
Finally, we can emit our event on the authController.
Import the events module
import Event from '@ioc:Adonis/Core/Event'
Then right below the validation in the register function
const data = await request.validate({ schema: validations })
const newUser = await User.create(data)
Event.emit('new:user', {
newUser,
})
We're calling the new:user
event we defined in the events.ts
file as this is the event name, the second parameter is the arguments to take.
Note that the argument names must be named similarly to the arguments we passed to the event declaration. By this I mean that you have to name the variable newUser just as it's declared in the event declaration. The same follows when having multiple arguments.
Now, we'll test the event.
Testing
Remember to turn on the development server by running,
node ace serve --watch
`
Registration
Account activated
Resources
- The complete source code can be accessed on my Github profile, here.
- Frontend code for the email template can be found in this GitHub gist
- official adonis events documentation
- Node.js event loop.
Conclusion
In this article, we've learned another way of working with events in AdonisJs. It's entirely up to you to decide which method best works for you. That being said, always employ the most optimal approach.
If you have any queries, comment and insight, don't hesitate to reach out on my Twitter, personal website or simply by leaving a comment below.
I'll be going over models, migrations, and relationships in my next article. Till then...cheers!
Top comments (1)
you must bind the onNewUser method as the event listener inside the
start/events.ts
Event.on('new:user', 'User.onNewUser')