Wrapping NPM Packages as a Service Provider in AdonisJs

AdonisJs makes creating APIs fun, it makes dependency importing into a file easy with the use global method. It gives the developer a central point to configure their dependencies and bind it to the IOC container. To read about the IOC container click here.
This article will show you how to use AdonisJs service providers to achieve all the benefits mentioned earlier, including configuring different dependencies serving a similar purpose to have a single interface and a central point to switch from one dependency to the other without altering the code.
I will be demonstrating using the Twilio Node.js helper library, I will assume you are already working with AdonisJs but if you need to learn about adonisJs you can follow this link and have a basic understanding of object-oriented programming.
Getting Started
First, we install the Twilio helper library using npm, if you prefer you can use yarn. npm install twilio into the project.
Setting up Configuration
So we open up the config folder and create a file in it called sms.js. The configuration object will be created in such a way that it can contain various SMS service providers configuration details. The details will be fed from the environment file.
const Env = use('Env')
module.exports = {
/*
|--------------------------------------------------------------------------
| Connection
|--------------------------------------------------------------------------
|
| Connection to be used for sending SMS.
| A connection needs to have a corresponding object below.
|
*/
connection: Env.get('SMS_CONNECTION'),
sms_sender: Env.get('SMS_SENDER'),
/*
|--------------------------------------------------------------------------
| TWILIO
|--------------------------------------------------------------------------
|
| Here we define the configuration for sending SMS via TWILIO.
|
*/
twilio: {
driver: 'twilio',
accountSid: Env.get('TWILIO_SMS_ACCOUNTS_ID'),
auth_token: Env.get('TWILIO_SMS_AUTH_TOKEN')
},
}
The connection determines which SMS service provider the system is using, sms_sender defines the number sending the SMS, twilio is the object containing the configuration details for the Twilio SMS service provider, any other service provider needed can be configured by adding the configuration object like the twilio object.
Setting up the Providers Folder
The providers folder will contain all providers and will live in the root of your app. In the providers folder we create a folder called Sms, here is where all the magic will happen.
|-- app
| |--config
| |-- providers
| |-- sms
| |-- start
Setting the Source folder
In the newly created providers/sms folder, we need to have a folder where the code interacts with the npm package. the folder will be named src, which will contain an index.js file and other files for the different SMS service providers. In this demo, we will call it a Twilio.js file which will contain the class that interacts with the Twilio node.js helper function.
const Twilio = require('twilio')
class TwilioDriver {
constructor (config, sender) {
this.config = config
this.messageBody = null
this.sendTo = null
this.sender = sender
}
to (to) {
this.sendTo = to
return this
}
body (body) {
this.messageBody = body
return this
}
async sendSms () {
const client = Twilio (this.config.accountSid, this.config.auth_token)
return await client.messages.create ({
body: this.messageBody,
from: this.sender,
to: this.sendTo
})
}
}
module.exports = TwilioDriver
Next, we create an index.js file in the src folder, the file will require other files in the src folder, pass it into an object and export the object. The index.js file will look like this:
module.exports = {
twilio: require('./Twilio')
}
Creating the Service Provider index file
This file takes in the config file and the index.js file from the src folder, it will reside in the Sms folder. It takes in the connection property of the config file and decides which SMS service provider to instantiate.
const Driver = require('./src')
class Sms {
constructor (Config) {
this.Config = Config
}
prepare () {
/**
* Read connection name using Config
* provider
*/
const SmsConfig = this.Config.get('sms')
const name = SmsConfig.connection
const sender = SmsConfig.sms_sender
const config = SmsConfig[name]
const SmsDriver = Driver[name]
/**
* Return the instance back
*/
return new SmsDriver (config, sender)
}
}
module.exports = Sms
BInding to the IOC container
Here we use service provider to bind all our previous work to the IOC container. The service provider is a class we extend, it comes with a register and a boot method but we only need to use the register method. In the register method, the configuration from the config file is passed into the Sms class located in the sms/index.js file. The first parameter of this.app.bind defines the name used to address the provider when importing it later for use.
const { ServiceProvider } = require('@adonisjs/fold')
class SmsProvider extends ServiceProvider {
register () {
this.app.bind('Sms', () => {
const Config = this.app.use('Adonis/Src/Config')
return new (require('.'))(Config).prepare()
})
}
}
module.exports = SmsProvider
this.app references the ioc object, so we can use this.app.bind or this.app.singleton and it will be the same with ioc.bind or ioc.singleton
Registering the Provider
To register the newly created provider, open the app.js file in the start folder in the root of the app.
- require the path module
const path = require('path') - Add the path to the new provider in the
providers arrayconst providers = [ path.join(__dirname, '..', 'providers', 'sms/Provider'), ]Using the Provider
At this point, we can import the provider into any part of the project usingconst Sms = use('Sms')Moving Further
So if there is a need to use another SMS service provider nodeJs helper library, we add the configuration to theconfig/Sms.jsfile, we add the class to interact with the helper library in theproviders/sms/srcfolder to handle it. The class will contain thetomethod, thebodymethod and thesendSmsmethod. So as not to break existing code. Finally, remember to define the new name of the service provider in the.envfile and let it be the name of one of the properties in theconfig/Sms.jsfile holding a configuration object.
In conclusion, we have seen have to configure an NPM package as a service provider for AdonisJs, the principles remain the same and a Provider.js file is always necessary, the rest are defined based on the needs of the developer.

