To function, your application needs to have the ability to validate input data and send texts. It also needs to display different pages depending on whether a user is scheduling a message or has already submitted the message details. This is where routing comes in. Routing is your application's traffic control system, guiding users along their journey by shunting them to appropriate pages. To make your project well organized, let's put your application's routing and business logic in routes.js
.
The completed code to be copied into routes.js
is displayed on the right panel. We'll describe the different sections of code below.
The routes.js
file begins with this code:
const express = require("express");
const router = express.Router();
const { DateTime } = require("luxon");
const sessionStorage = require("sessionstorage-for-nodejs");
const { SinchClient } = require("@sinch/sdk-core");
const dotenv = require("dotenv");
dotenv.config();
const sinchClient = new SinchClient({
projectId: process.env.PROJECT_ID,
keyId: process.env.KEY_ID,
keySecret: process.env.KEY_SECRET,
region: process.env.SMS_REGION,
});
This acts as the setup for routes.js
. It loads in the environment variables, which include your Sinch credentials, from the .env
file and uses them to initialise an SDK client object, called SinchClient
. This is going to play a key role in your application.
Now we'll review the remind_patient
function, whose job is to send the SMS:
async function remind_patient(to, message, send_at) {
let response = await sinchClient.sms.batches.send({
sendSMSRequestBody: {
to: [to],
from: process.env.FROM_NUMBER,
body: message,
send_at: send_at,
},
});
console.log(JSON.stringify(response));
}
This function is the keystone of your application. It uses the SinchClient instance you just created to call the SDK send method. What makes this method powerful is the send_at
parameter. Because the Sinch SMS REST API has message scheduling built in, it's a one-stop shop for any application that requires sending SMS messages at particular times. This reduces the amount of dependencies you need. Even better, it means you don't need your app to be running all the time!
In addition to send_at
, the send method has a number of other parameters, including message expiry and delivery reports. These are available on the REST API reference.
We're now going to review the route paths. You can think of route paths as railway tracks in a shunting yard. Each track is the URL of a particular page. By switching users onto appropriate tracks, the application can control which page they see at a given point on their user journey.
The first route path redirects from the root URL. This means that users can simply type http://localhost:3000/
and they will be taken to the appointment scheduling form at http://localhost:3000/appointment
. The second route path displays the message scheduling form:
router.get("/", function (req, res) {
res.redirect("/appointment");
});
router.get("/appointment", function (req, res) {
res.render("patient_details", { message: sessionStorage.getItem("message") });
sessionStorage.removeItem("message");
});
The third route path contains the meat of of your application's business logic:
router.post("/appointment", async function(req, res, next){
let appointment_datetime= DateTime.fromISO(req.body.appointment_date)
let [hours, minutes] = req.body.appointment_time.split(":")
appointment_datetime=appointment_datetime.set({hour: hours, minutes: minutes}).setZone('system')
let now=DateTime.now()
let reminder_datetime=appointment_datetime.setZone('utc').minus({hours: 2, minutes: 5})
sessionStorage.setItem('appointment_datetime', appointment_datetime.toLocaleString(DateTime.DATETIME_MED))
if(appointment_datetime.minus({hours: 2, minutes: 5})<now){
req.flash('message', 'Appointment must be at least 2:05 hours from now');
sessionStorage.setItem('message', req.flash('message'))
res.redirect('/appointment')
return;
}
let country_code=process.env.COUNTRY_CODE_US;
if(process.env.SMS_REGION=='eu'){
country_code=process.env.COUNTRY_CODE_EU
}
let to = country_code + req.body.phone;
sessionStorage.setItem('phone', req.body.phone)
const message="Hi "+req.body.patient+", you have a Sinch Hospital appointment with Dr "+req.body.doctor+" scheduled for "+appointment_datetime.toLocaleString(DateTime.DATETIME_MED);
console.log(message)
sessionStorage.setItem('patient', req.body.patient);
sessionStorage.setItem('doctor', req.body.doctor);
remind_patient(to, message, reminder_datetime)
res.redirect('/success')
})
This is a post
method. It receives form data from the front end and sends an SMS message by calling remind_patient
. Once a confirmation message is sent, the user is redirected to the success.html
page. The route path also contains validation, to make sure any appointment is booked at least a couple of hours into the future. This also stops users from accidently booking an appointment in the past. The Sinch SDK requires phone numbers to have a country code, so the code checks the SMS region and adds a US/EU country code to the phone number.
Now we'll review the success
route path:
router.get("/success", function (req, res) {
res.render("success", {
patient: sessionStorage.getItem("patient"),
doctor: sessionStorage.getItem("doctor"),
phone: sessionStorage.getItem("phone"),
appointment_datetime: sessionStorage.getItem("appointment_datetime"),
});
});
This renders the success page. It is what the previous route path redirects to when an appointment reminder is successfully submitted. It uses sessionStorage
to pull information such as patient name and phone number to display a success message.
Finally you need to add the following line of code.
module.exports=router;
This makes routes.js
available to your application by exporting the router
. This allows it to be mounted on the application server in app.js
.
Congratulations! That's the core of your application completed.