Build a realtime app with Adonis
You will need Node and npm installed on your machine. Some knowledge of JavaScript will be helpful.
Adonis is a full stack, open source MVC framework for Node.js. The framework is inspired by the PHP framework Laravel. As at the time of this tutorial, it has over 2.8k stars on GitHub.
Adonis focuses on the developers productivity and efficiency over anything else. It also comes with a lot of features including the following:
- Lucid ORM
- Database Migrations
- Authentication System
- OAuth
- Mailing System
- Data Validator
Adonis has very detailed documentation and a supportive community of users who are engaging on Twitter.
In this tutorial we are going to see some of the interesting features of Adonis by building an app that pushes realtime messages to all connected clients using Pusher.
Demo
Here is what the final result of the app would look like:
Prerequisites
To get started, you need knowledge of Node.js and JavaScript. The following must be installed on your machine:
- Node.js
- NPM(Bundled with Node.js installer)
Node.js is an open-source, cross-platform JavaScript run-time environment for executing JavaScript on a server.
Set up an Adonis project
Open your terminal and type this command to install Adonis CLI and create a new adonis app:
# if you don't have Adonis CLI installed on your machine.
$ npm install -g @adonisjs/cli
# Create a new adonis app and move into the app directory
$ adonis new adonis-event-pusher && cd adonis-event-pusher
Start the server and test if it’s working:
$ adonis serve --dev
2017-10-18T09:09:16.649Z - info: serving app on http://127.0.0.1:3333
Open your browser and make a request to http://127.0.0.1:3333. You should see the following:
Install the Pusher SDK
For Adonis to work with Pusher, we need to install pusher
with npm or yarn into our project. To do so, run:
#if you want to use npm
$ npm install pusher -save
#if you want to use yarn
$ yarn add pusher
Update the welcome view
Go to the resources/views
directory and replace the content of welcome.edge
file with:
// welcome.edge
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Adonis & Pusher</title>
<!-- Styles -->
{{ css('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css') }}
{{ css('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css') }}
{{ css('style') }}
</head>
<body id="app-layout">
<nav class="navbar navbar-expand-md navbar-dark fixed-top">
<a class="navbar-brand" href="{{ route('welcomePage') }}"><i class="fa fa-cube"></i> Adonis & Pusher</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container" style="margin-top: 160px">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<div class="card">
<div class="card-header">Broadcast a Message</div>
<div class="card-body">
<div class="container">
<div class="row justify-content-md-center">
<div class="col col-md-10">
@if(old('status'))
<div class="alert alert-success" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{ old('status') }}
</div>
@endif
<form method="POST" action="{{ route('sendMessage') }}">
{{ csrfField() }}
<div class="form-group row">
<label class="col-md-3 col-form-label" for="email">
Message
</label>
<div class="col-md-9">
<input type="text" name="message" placeholder="Send Messege"
autocomplete="off"
class="form-control" required>
</div>
</div>
<div class="form-group row">
<div class="col-md-3"></div>
<div class="col-md-6">
<button type="submit" class="btn btn-primary btn-block">
<i class="fa fa-btn fa-paper-plane"></i> Send Message
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{ script('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.slim.min.js') }}
{{ script('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/js/bootstrap.min.js') }}
</body>
</html>
As you can see, we are importing the CSS files using the css
method. We do a similar thing with JavaScript, we use script
method to import .js
. Flash messages are used to display incoming messages from the server. Flash messages are messages stored temporarily in sessions by the server to display as browser notifications.
Refresh your browser:
Basic routes
We are going to define 3 basic routes for our application. One for displaying the form which you will use to collect the user message, another to render a sample frontend view and the last for broadcasting a messages via Event.
Go to the start/routes.js
file and replace the content with:
// routes.js
'use strict'
const Route = use('Route')
const Event = use('Event')
Route.on('/').render('welcome')
Route.on('/home').render('home')
Route.post('/send', async ({request, session, response}) => {
const message = request.input('message')
Event.fire('send.message', message)
session.flash({ status: 'Message sent' })
return response.redirect('back')
}).as('sendMessage')
The block pulls in Event
and Route
service providers.
The first route renders the welcome.edge
file in the resources/views
directory (which is where views are stored in Adonis).
The second route renders the home.edge
file (which we will create soon) and the last route accepts a message from browser requests, fires an event and redirects the user back with a flash message.
Pusher setup
Pusher is a hosted service that makes it super-easy to add realtime data and functionality to web and mobile applications. Pusher is an abstracted real-time layer between clients and servers.
Let’s setup pusher for our application. Head over to Pusher and create an account. You can sign in if you already have a account.
Register a new Pusher app instance. This registration provides credentials which can be used to communicate with the created Pusher instance. Copy the App ID, Key, Secret, and Cluster from the App Keys section and put them in the .``env
file:
// .env
PUSHER_KEY=<APP_KEY>
PUSHER_SECRET=<APP_SECRET>
PUSHER_APP_ID=<APP_ID>
PUSHER_CLUSTER=<APP_CLUSTER>
Connecting Adonis and Pusher
Create a file named events.js
file inside the start
directory. In this file, create an event which will be fired every time we need to send a message via the pusher channel:
// events.js
const Event = use('Event')
const Pusher = require('pusher')
const Env = use('Env')
let pusher = new Pusher({
appId: Env.get('PUSHER_APP_ID', ''),
key: Env.get('PUSHER_KEY', ''),
secret: Env.get('PUSHER_SECRET', ''),
cluster: Env.get('PUSHER_CLUSTER'),
encrypted: true
});
Event.when('send.message', async (message) => {
pusher.trigger('adonis-channel', 'send-message', {
message
});
})
We need to pull in the Event
and env
service providers. We are also importing the pusher
module. Next, we create a Pusher instance and configured with the credentials that were received after creating a Pusher account.
Next, we registered a listener for the send.message
event, after which we initialize and configure Pusher. This event was registered in the routes we created above to handle the message request.
When we are done with the pusher configuration, we trigger a send-message
event on the adonis-channel
with the trigger
method.
Subscribing to Pusher events
The client needs to start listening to these events being emitted by Pusher. To do so, setup an Adonis view that displays incoming messages. Go to the resources/views
directory and create a file called home.edge
which would look like the following:
// home.edge
<!DOCTYPE html>
<html>
<head>
<title>Adonis and Pusher awesomeness</title>
{{ css('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css') }}
</head>
<body>
<div class="container">
<div class="content">
<h1>Adonis and Pusher</h1>
<ul id="messages" class="list-group">
</ul>
</div>
</div>
{{ script('https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js') }}
<!-- Include the Pusher Client library -->
{{ script('https://js.pusher.com/4.1/pusher.min.js') }}
<script>
//Open a connection to Pusher
let pusher = new Pusher('APP_KEY', {
cluster: 'APP_CLUSTER',
encrypted: true
});
//Subscribe to the channel we specified in our Adonis Application
let channel = pusher.subscribe('adonis-channel')
//Listen for events on the channel
channel.bind('send-message', (data) => {
let listItem = $("<li class='list-group-item'>" + data.message + "</li>")
$('#messages').prepend(listItem)
})
</script>
</body>
</html>
First, we include jQuery and the Pusher JavaScript client library which will enable us subscribe to Pusher events from the server.
Next, we initialize the Pusher service by passing in our App Key (replace with your actual keys), and some other options (cluster, encrypted). The initialized instance is used to subscribe to the adonis-channel
channel.
Finally, we listen to the send-message
event and update the view using jQuery, based on the content received via the event listener.
Navigate to http://127.0.0.1:3333/home to see the new view:
Conclusion
In summary, Pusher makes it easy to add realtime capabilities to your Adonis app. Install the SDK, import it, create an instance and start triggering realtime events. Whatever language domain (JS, Android, iOS, etc) it still uses the same subscription pattern to listen to realtime events in order to update a user interface. This is why it fits right into the Adonis framework, which is picking up momentum in the Node ecosystem. You can find the source code for this tutorial on GitHub.
11 May 2018
by Christian Nwamba