How to use Serverless and Twilio to automate your communication channels

Sep 11, 2019

The way people communicate has changed over the last few years. When was the last time you called a service-number because you had a problem? For me, this is already years ago, and I most often use chat interfaces when they’re available.

These new interfaces are usually automated to a certain extent – it’s hard to tell if a human or a machine is replying to your question. Developers have the power to build interfaces that go beyond what we’re used to. Alexa, Whatsapp, emails, SMS – you can automate all these channels.

Twilio is a communications API that enables you to tailor the experience to your needs. Want to do an SMS poll? No problem! Need a custom chatbot on your landing pages? Sure thing! Want to bring all your friends into a group phone call? Easy peasy!

Webhooks – the foundation of future interfaces

As a developer, you probably won’t build the infrastructure to send SMS or make phone calls. You’ll use SDKs and APIs for that. The way it works is that you can use Twilio’s RESTful API to initiate outbound communications. Phone calls, SMS, WhatsApp messages are only one HTTP call away.

The other way around is a little bit trickier. How do you react to incoming messages or phone calls when you’re not controlling the infrastructure receiving these events? Webhooks build the foundation for that.

If someone sends a message, makes a phone call, or uses any other channel a webhook is sent to a URL you define. The response of the webhook controls what happens next.

SMS, Twilio, App

In the above example, you see the flow for an incoming SMS. A user sends an SMS, Twilio handles this event and makes an HTTP request to your app to find out what to do next.

But do you want to build a whole app to respond to an HTTP request? Or could a serverless function do the job?

The Serverless Framework now supports deploying Twilio Functions

Serverless functions are a perfect fit for responding to HTTP calls. The Twilio Runtime gives you a way to write serverless functions today. We’re happy to announce that you can now deploy Twilio Functions using the Serverless Framework.

SMS, Twilio, Serverless

If you’re used to working with the Serverless Framework, there is no need to learn a new API. You can continue using the Serverless Framework to control your Twilio communications!

Deploy Twilio serverless functions in two minutes

Starting with the Serverless CLI v1.50.0, you can bootstrap a Twilio Runtime project with a single command. In this post you’ll learn how to do that.

Make sure you have the Serverless CLI installed globally and run serverless create with the Twilio Node.js template.

serverless create -t twilio-nodejs -p my-twilio-project
serverless create -t twilio-nodejs -p my-twilio-project

Navigate into the new directory my-twilio-project and run npm install. Before you deploy a new serverless service, you need to authenticate. Head over to your Twilio Console and copy your account credentials (the Account SID and Auth token).

Define the two authentication values as environment variables and deploy the new project using serverless deploy.

cd my-twilio-project npm install TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless deploy
cd my-twilio-project npm install TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless deploy

The command output should look as follows.

Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: twilio-runtime: Creating Service Serverless: twilio-runtime: Configuring "dev" environment Serverless: twilio-runtime: Creating 1 Functions Serverless: twilio-runtime: Uploading 1 Functions Serverless: twilio-runtime: Creating 1 Assets Serverless: twilio-runtime: Uploading 1 Assets Serverless: twilio-runtime: Waiting for deployment. Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Setting environment variables Serverless: twilio-runtime: Activating deployment Serverless: twilio-runtime: Function available at: my-twilio-project-7724-dev.twil.io/hello/world Serverless: twilio-runtime: Asset available at: my-twilio-project-7724-dev.twil.io/assets/foo/example.jpg
Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: twilio-runtime: Creating Service Serverless: twilio-runtime: Configuring "dev" environment Serverless: twilio-runtime: Creating 1 Functions Serverless: twilio-runtime: Uploading 1 Functions Serverless: twilio-runtime: Creating 1 Assets Serverless: twilio-runtime: Uploading 1 Assets Serverless: twilio-runtime: Waiting for deployment. Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Waiting for deployment. Current status: building Serverless: twilio-runtime: Setting environment variables Serverless: twilio-runtime: Activating deployment Serverless: twilio-runtime: Function available at: my-twilio-project-7724-dev.twil.io/hello/world Serverless: twilio-runtime: Asset available at: my-twilio-project-7724-dev.twil.io/assets/foo/example.jpg

Congratulations! 🎉 You just deployed your first Twilio Runtime service that is able to serve functions and assets using the Serverless CLI. The URLs displayed in the deploy log are now publicly available.

Twilio runtime page

How does that work? And how can you tweak Twilio functions to your needs? Let’s find out!

The file structure of a Serverless Twilio project

The create command created all the files you need – ready-to-deploy. It includes the following:

.rw-r--r-- .gitignore .rw-r--r-- example.jpg .rw-r--r-- handler.js drwxr-xr-x node_modules .rw-r--r-- package-lock.json .rw-r--r-- package.json .rw-r--r-- serverless.yml
.rw-r--r-- .gitignore .rw-r--r-- example.jpg .rw-r--r-- handler.js drwxr-xr-x node_modules .rw-r--r-- package-lock.json .rw-r--r-- package.json .rw-r--r-- serverless.yml

You see the familiar serverless.yml configuration file, a package.json and package-lock.json, and a function (handler.js) and asset file (example.jpg).

The project has only one npm dependency defined inside of the package.json: @twilio-labs/serverless-twilio-runtime. You installed this dependency already, and if you now have a look at the serverless.yml, you’ll see that it defines @twilio-labs/serverless-twilio-runtime as a plugin.

# serverless.yml plugins: - '@twilio-labs/serverless-twilio-runtime'
# serverless.yml plugins: - '@twilio-labs/serverless-twilio-runtime'

This plugin definition makes it possible to deploy to the Twilio Runtime.

The serverless.yml holds more configuration than the plugin definition, though. It also configures the runtime and provides a quick way to define and deploy serverless functions and assets.

General configuration

The main configuration for your serverless service happens inside of the provider property. Twilio is the provider in this case.

You have the following configuration options:

  • Authentication (required)
  • Dependencies
  • Name of the deployed environment
  • Environment variables accessible inside of your functions

Let’s go over these options one-by-one.

Authenticate your serverless services with Twilio

To deploy functions to the Twilio Runtime, you have to define your accountSid and an authToken. You could potentially hardcode these values in your serverless.yml but it is recommended to pass these values via environment variables (${env:TWILIO_ACCOUNT_SID} and ${env:TWILIO_AUTH_TOKEN}). This way, you don’t risk to push your sensible credentials to GitHub.

# serverless.yml provider: name: twilio # Auth credentials which you'll find at twilio.com/console config: accountSid: ${env:TWILIO_ACCOUNT_SID} authToken: ${env:TWILIO_AUTH_TOKEN}
# serverless.yml provider: name: twilio # Auth credentials which you'll find at twilio.com/console config: accountSid: ${env:TWILIO_ACCOUNT_SID} authToken: ${env:TWILIO_AUTH_TOKEN}

The above configuration is the reason why the command TWILIO_ACCOUNT_SID=AC… TWILIO_AUTH_TOKEN=a8… serverless deploy works. The serverless command picks up the environment variables and passes them to the Twilio Runtime plugin.

Define needed npm dependencies

If you have experience using the Serverless Framework, you may be used to the process of packaging dependencies into a single bundle. The Twilio Runtime handles dependencies differently.

The deploy command does not need to pack all your local dependencies into a single package to upload. The Twilio Runtime allows you to define your dependencies, and you only need to upload your function and asset files. The rest just works!

Having a look at the serverless.yml, you’ll see that the bootstrap project has one dependency – asciiart-logo. The definition of dependencies is similar to package.json dependency definitions.

# serverless.yml provider: # Twilio runtime as your preferred provider name: twilio # Dependency definitions similar # to dependencies in a package.json # -> these dependencies will be available in the # Twilio Node.js runtime dependencies: asciiart-logo: '*'
# serverless.yml provider: # Twilio runtime as your preferred provider name: twilio # Dependency definitions similar # to dependencies in a package.json # -> these dependencies will be available in the # Twilio Node.js runtime dependencies: asciiart-logo: '*'

You can now include const logo = require('asciiart-logo'); in your function files and the dependency will be available.

Define your environment

If you’re heavily relying on serverless functions, you’ll find yourself facing increased complexity quickly. To tackle this complexity, you can safely deploy QA and staging environments to test your functions before they go into production.

The environment property lets you deploy different environments to the Twilio Runtime.

provider: # Twilio runtime as your preferred provider name: twilio # Twilio runtime supports several domains # your functions and assets will be available under # -> defaulting to 'dev' environment: ${env:TWILIO_RUNTIME_ENV, 'dev'}
provider: # Twilio runtime as your preferred provider name: twilio # Twilio runtime supports several domains # your functions and assets will be available under # -> defaulting to 'dev' environment: ${env:TWILIO_RUNTIME_ENV, 'dev'}

The environment property is also defaulting to dev and you can change it via the environment variable TWILIO_RUNTIME_ENV.

A deployed Twilio function URL consists of the service name, a random hash, and the defined environment. The endpoint URL my-twilio-project-7724-dev.twil.io/hello/world tells you that you’re looking at a function in the dev environment included in the my-twilio-project service.

Define accessible environment variables

When deploying functions to the Twilio Runtime, you may also need a way to define variables that will be available in the function context. These environment variables can be handy to store authentication tokens for other services, to define used endpoints or any dynamic values.

The environmentVars property lets you define these values. They will be available in the context property when your functions are executed.

# serverless.yml provider: # Twilio runtime as your preferred provider name: twilio # Environment variables passed to your functions # available via process.env environmentVars: MY_MESSAGE: 'This is cool stuff'
# serverless.yml provider: # Twilio runtime as your preferred provider name: twilio # Environment variables passed to your functions # available via process.env environmentVars: MY_MESSAGE: 'This is cool stuff'

The above provider properties are all the configurations you need to tailor your functions deployment to your use case! But where are the function definitions and how come there was an asset deployed?

Define your functions

You can define and configure functions by editing the functions property in your serverless.yml. When you look at it, there is already one function defined.

Every function definition has to export a handler as follows:

exports.handler = function(context, event, callback) { /* … */ };
exports.handler = function(context, event, callback) { /* … */ };

The function property tells Serverless to use handlers.js, make it available at /hello/world and make it publicly accessible.

# serverless.yml functions: hello-world: # Path to the JS handler function in the project (without file extension '.js') handler: handler # URL path of the function after deployment path: /hello/world # visibility of the function (can be "public" or "protected") access: public
# serverless.yml functions: hello-world: # Path to the JS handler function in the project (without file extension '.js') handler: handler # URL path of the function after deployment path: /hello/world # visibility of the function (can be "public" or "protected") access: public

You can read more about Twilio functions in the docs.

Define your assets

The Twilio Runtime allows you to upload assets via the assets property. You can access these assets then inside of your deployed functions. The combination of assets and functions can become handy if you want to play an audio file or want to respond to a message with a specific image.

resources: assets: # Asset name example-image: # path to the asset in the project filePath: example.jpg # URL path to the asset after deployment path: /assets/foo/example.jpg # visibility of the asset access: public
resources: assets: # Asset name example-image: # path to the asset in the project filePath: example.jpg # URL path to the asset after deployment path: /assets/foo/example.jpg # visibility of the asset access: public

To learn more about how to use assets in your functions have a look at the docs.

With these function and asset definitions, you are able to deploy a new service in just a few minutes. 🎉

Other included Serverless commands

The Serverless Twilio integration supports two other commands – invoke and info.

Invoke

invoke is a command that you can use to call a deployed function to see if the response is what you expect.

TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless invoke -f hello-world ,--------------------------------------------------------------------------. | | | _____ _ _ _ ____ _ _ | | |_ _|_ _(_) (_) ___ | _ \ _ _ _ __ | |_(_)_ __ ___ ___ | | | | \ \ /\ / / | | |/ _ \ | |_) | | | | '_ \| __| | '_ ` _ \ / _ \ | | | | \ V V /| | | | (_) | | _ <| |_| | | | | |_| | | | | | | __/ | | |_| \_/\_/ |_|_|_|\___/ |_| \_\\__,_|_| |_|\__|_|_| |_| |_|\___| | | | | | | version 1.0.0 | | | | | `--------------------------------------------------------------------------'
TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless invoke -f hello-world ,--------------------------------------------------------------------------. | | | _____ _ _ _ ____ _ _ | | |_ _|_ _(_) (_) ___ | _ \ _ _ _ __ | |_(_)_ __ ___ ___ | | | | \ \ /\ / / | | |/ _ \ | |_) | | | | '_ \| __| | '_ ` _ \ / _ \ | | | | \ V V /| | | | (_) | | _ <| |_| | | | | |_| | | | | | | __/ | | |_| \_/\_/ |_|_|_|\___/ |_| \_\\__,_|_| |_|\__|_|_| |_| |_|\___| | | | | | | version 1.0.0 | | | | | `--------------------------------------------------------------------------'
Info

The info command gives you information about your deployed service.

TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless info Service information ******* Service: tutorial-try-out Service Sid: ZSd5030028ddd5b0714f3981865c80b90d Environment: dev Environment unique name: dev-environment Environment Sid: ZEf282491f28a513bb310f4f1c167a55d3 Environment domain name: tutorial-try-out-7724-dev.twil.io Environment variables: MY_MESSAGE: THIS IS cool stuff Assets: access: public creation date: 2019-09-03T09:51:08Z path: /assets/foo/example.jpg sid: ZN60c9751befba796c9190cd789f400568 url: https://tutorial-try-out-7724-dev.twil.io/assets/foo/example.jpg Functions: access: public creation date: 2019-09-03T09:51:07Z path: /hello/world sid: ZN06e8bd0c672045dbc1134de22ebe7fda url: https://tutorial-try-out-7724-dev.twil.io/hello/world
TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless info Service information ******* Service: tutorial-try-out Service Sid: ZSd5030028ddd5b0714f3981865c80b90d Environment: dev Environment unique name: dev-environment Environment Sid: ZEf282491f28a513bb310f4f1c167a55d3 Environment domain name: tutorial-try-out-7724-dev.twil.io Environment variables: MY_MESSAGE: THIS IS cool stuff Assets: access: public creation date: 2019-09-03T09:51:08Z path: /assets/foo/example.jpg sid: ZN60c9751befba796c9190cd789f400568 url: https://tutorial-try-out-7724-dev.twil.io/assets/foo/example.jpg Functions: access: public creation date: 2019-09-03T09:51:07Z path: /hello/world sid: ZN06e8bd0c672045dbc1134de22ebe7fda url: https://tutorial-try-out-7724-dev.twil.io/hello/world

Using the info command, you can quickly get information about deployed functions, assets and service configuration.

Twilio serverless functions – the environment tailored to power your communication flows

After the first deployment, the new function endpoint only returns a string with ASCII art. How can you change it to respond to webhooks for incoming SMS the correct way?

A Twilio function provides you with a global Twilio variable that includes a twiml object to build the correct response. TwiML is Twilio’s configuration language to control communication flows. Go into the handler.js file, drop the ASCII art, and change it to respond to incoming HTTP calls with some TwiML.

If you define the function URL as a webhook for a phone number, sent-in messages would get the combination of “Hello!” and the defined environment variable as a response.

// handler.js exports.handler = function(context, event, callback) { let twiml = new Twilio.twiml.MessagingResponse(); twiml.message(`Hello! ${context.MY_MESSAGE}`); callback(null, twiml); };
// handler.js exports.handler = function(context, event, callback) { let twiml = new Twilio.twiml.MessagingResponse(); twiml.message(`Hello! ${context.MY_MESSAGE}`); callback(null, twiml); };

Rerun the deploy command, and you’re ready to handle incoming messages with Twilio.

TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless deploy
TWILIO_ACCOUNT_SID=AC... TWILIO_AUTH_TOKEN=a4... serverless deploy

Twilio functions are built to answer Twilio webhooks and glue services together quickly

Twilio XML

Additional resources

We’re very excited about the Serverless integration. It’s still pretty new, and we’re working on getting more docs onto serverless.com and adding more commands to the CLI. What functionality are you missing? We’d love to get some feedback on it!

If you want to read more about Twilio functions, you can have a look here:

Subscribe to our newsletter to get the latest product updates, tips, and best practices!

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.