In this tutorial we'll build a broadcasting module for AdonisJS which resembles Laravel Broadcasting features (you can even use Laravel Echo). This module will cover up many of the websockets use cases in a simple manner.
It is great for cases where the clients need to receive data in real-time, but don't need to send data in real-time.
Good use cases are:
- Chats
- Live dashboards
- Sport scores
Bad use cases are:
- Games
- Work together platforms
Let's build it!
Scaffolding a new app
Create a new AdonisJS project
$ npm init create-adonis-ts-app broadcasting
$ yarn create adonis-ts-app broadcasting
When prompted which project structure, select web and flag
Configure webpack for compiling frontend assets? true
Setting up our Broadcast server
Our broadcast module will be based in an open-source Pusher compatible server called pWS.
First, we will install it
$ npm i @soketi/pws
$ yarn add @soketi/pws
We can start the server by running
$ npm pws-server start
$ yarn pws-server start
But we need to configure it before running, so we will make a configuration file for it in config/broadcasting.ts
// config/broadcasting.ts
import Env from '@ioc:Adonis/Core/Env'
const broadcastingConfig = {
port: Env.get('BROADCASTING_PORT', 6001),
appId: Env.get('BROADCASTING_APP_ID', 'app-id'),
appKey: Env.get('BROADCASTING_APP_KEY', 'app-key'),
appSecret: Env.get('BROADCASTING_APP_KEY', 'app-secret'),
}
export default broadcastingConfig
The configs won't get magically loaded into pWS, so we will make a command to start it. To start it we will use execa. So install it using:
$ npm i execa
$ yarn add execa
and create a command with
$ node ace make:command StartPws
The command will look like this:
// commands/StartPws.ts
import { BaseCommand } from '@adonisjs/core/build/standalone'
import execa from 'execa'
export default class StartPws extends BaseCommand {
public static commandName = 'start:pws'
public static description = 'Start the pWS server with Adonis Configs'
public static settings = {
loadApp: true,
stayAlive: true,
}
public async run() {
const broadcastingConfig = this.application.config.get('broadcasting')
const command = `
PORT=${broadcastingConfig.port}
DEFAULT_APP_ID=${broadcastingConfig.appId}
DEFAULT_APP_KEY=${broadcastingConfig.appKey}
DEFAULT_APP_SECRET=${broadcastingConfig.appSecret}
yarn pws-server start`
await execa(command, { shell: true }).stdout?.pipe(process.stdout)
}
}
After creating the command, we need to regenerate the ace manifest, so it catches our new command, do it by running:
$ node ace generate:manifest
Then you can run it with
$ node ace start:pws
Broadcasting events
As pWS is a drop-in Pusher replacement, we can interact with it using any Pusher client, as AdonisJS is a node framework, we will use the node Pusher client. Start by installing the node Pusher client:
$ npm i pusher
$ yarn add pusher
Then we will create a service to interact with the pWS server, it can be done as a simple service or as a AdonisJS provider, in this tutorial we will go the service way.
// app/Services/Broadcast.ts
import Pusher from 'pusher'
import broadcastingConfig from 'Config/broadcasting'
import Env from '@ioc:Adonis/Core/Env'
class Broadcast {
private pusher = new Pusher({
host: Env.get('HOST', 'localhost'),
port: broadcastingConfig.port,
appId: broadcastingConfig.appId,
key: broadcastingConfig.appKey,
secret: broadcastingConfig.appSecret,
})
public async broadcast(channel: string | string[], event: string, data: any) {
const response = await this.pusher.trigger(channel, event, data)
return response
}
}
export default new Broadcast()
With this service we can broadcast events by simply using
import Broadcast from 'App/Services/Broadcast'
await Broadcast.broadcast('test-channel', 'event', 'data')
Listening to events
To listen to events in our frontend we can use PusherJS paired with Laravel Echo. Start by installing both:
$ npm i -D laravel-echo pusher-js
$ yarn add -D laravel-echo pusher-js
Set them up in our frontend:
// resources/js/app.js
import '../css/app.css'
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js')
window.Echo = new Echo({
broadcaster: 'pusher',
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
disableStats: true,
key: 'app-key',
namespace: '',
})
Example setup
Append this to the end of resources/js/app.js
// resources/js/app.js
window.Echo.channel('messages').listen('message', (e) => {
alert(JSON.stringify(e))
})
Paste this into the welcome view (resources/views/welcome.edge
)
<!-- resources/views/welcome.edge -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AdonisJS - A fully featured web framework for Node.js</title>
@entryPointStyles('app')
@entryPointScripts('app')
</head>
<body>
<main>
<div>
<!-- Just to show off how it works. You can safely ignore that -->
<form method="POST" action="/message">
<input name="message" type="text" />
<button>Send Message</button>
</form>
</div>
</main>
</body>
</html>
After setting up that, we just need to setup our message route to broadcast a message event:
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
import Broadcast from 'App/Services/Broadcast'
Route.get('/', async ({ view }) => {
return view.render('welcome')
})
Route.post('/message', async ({ request, response }) => {
const message = request.input('message')
await Broadcast.broadcast('messages', 'message', { message })
return response.redirect().back()
})
It's alive!
But it still doesn't works for private or presence channels, we will address that in next tutorial, stay tuned!
Top comments (0)