Creating a Web API using Deno and AWS

Overview

This tutorial walks through the steps needed to create a Web API on AWS using Deno. Our Deno Web API will be created using the Pogo framework, a Sinatra-like framework similar to Express used in Node, and will use the Nginx web server. Our API will be hosted on an AWS EC2 instance, will use both HTTP and HTTPS (SSL/TLS), and will have a domain name associated with it.

Creating an AWS EC2 Instance to Host Our Web API

To host our Web API, we will use an Amazon Linux 2 AMI EC2 instance. You can tailor most features of the machine for your particular usage, but for our Web API we will need to modify the Security Group setting to allow for SSH, HTTP, and HTTPS inbound access:

Picking the type of machine for our EC2 instance

Setting the security group for our EC2 instance

Once everything is set and the EC2 instance has started, connect to your EC2 instance using SSH. After connecting we can now start installing everything needed to run our Nginx server and our Deno Web API.

Installing Deno on the EC2 Instance

The first thing we should do is install Deno on the instance using the following command:

Command Line Shell

curl -fsSL https://deno.land/x/install/install.sh | sh

Once the command is finished and Deno is done installing, we will get output on the console like this:

Command Line Shell Output

Deno was installed successfully to /home/ec2-user/.deno/bin/deno Manually add the directory to your $HOME/.bash_profile (or similar) export DENO_INSTALL="/home/ec2-user/.deno" export PATH="$DENO_INSTALL/bin:$PATH" Run '/home/ec2-user/.deno/bin/deno --help' to get started

By default, the deno command won't be included on the PATH variable, so lets add it to the PATH by modifying the .bash_profile file using the nano editor:

Command Line Shell

nano $HOME/.bash_profile

The output from installing Deno will tell you what needs to be added to the .bash_profile file:

Finally, reload the bash profile by running the following command:

Command Line Shell

source ~/.bash_profile

Now we will be able to use the deno command on the command line.

Creating a Pogo Application for Our Web API

Pogo is a Sinatra-like web framework created by Seth Holladay, with a very similar syntax to Express for NodeJS. Pogo is written specifically for Deno, and has very good documentation, which can be found here.

For our Web API, we will create an API that echoes user input in the URL parameters, and provides a series of routes that perform basic mathematical functions, again taking inputs from the URL parameters. Here is the code for our Web API:

server.js

import pogo from "https://deno.land/x/pogo/main.js"; const server = pogo.server({ port : 55555 }); // Route for echoing text server.router.get("/echo/{text}", (request, response) => { return request.params.text }); // Route for adding 2 numbers server.router.get("/add/{num1}/{num2}", (request, response) => { return Number.parseFloat(request.params.num1) + Number.parseFloat(request.params.num2); }); // Route for subtracting 2 numbers server.router.get("/sub/{num1}/{num2}", (request, response) => { return Number.parseFloat(request.params.num1) - Number.parseFloat(request.params.num2); }); // Route for multiplying 2 numbers server.router.get("/mul/{num1}/{num2}", (request, response) => { return Number.parseFloat(request.params.num1) * Number.parseFloat(request.params.num2); }); // Route for dividing 2 numbers server.router.get("/div/{num1}/{num2}", (request, response) => { return Number.parseFloat(request.params.num1) / Number.parseFloat(request.params.num2); }); // Route for raising 2 numbers by a power server.router.get("/pow/{num1}/{num2}", (request, response) => { return Math.pow(Number.parseFloat(request.params.num1), Number.parseFloat(request.params.num2)); }); // Route for squaring a number server.router.get("/sqa/{num1}", (request, response) => { return Math.pow(Number.parseFloat(request.params.num1), 2); }); // Route for square rooting a number server.router.get("/sqr/{num1}", (request, response) => { return Math.sqrt(Number.parseFloat(request.params.num1), Number.parseFloat(request.params.num2)); }); // Route for getting a random number server.router.get("/ran", (request, response) => { return Math.random(); }); server.start();

We can create our server.js file using a command line text editor like vim or nano:

Command Line Shell

nano server.js

And that's it, we've created our Web API!

Setting up the Nginx Web Server

Before installing Nginx, let's update the existing yum packages on the EC2 instance:

Command Line Shell

sudo yum update -y

Once all the packages have finished updating let's install nginx:

Command Line Shell

sudo amazon-linux-extras install nginx1

Now let's configure nginx so that it proxies to our Pogo application. We'll configure the nginx.conf file to include the proxy_pass variable, which will point to our web application running in localhost on the same EC2 instance:

Command Line Shell

sudo nano /etc/nginx/nginx.conf

Now that the configuration is complete, let's start the nginx server:

Command Line Shell

sudo service nginx start

And let's also start the Pogo application:

Command Line Shell

nohup deno run --allow-net server.js &

If everything worked correctly, we should now be able to connect to our Web API using the browser at the Public IPv4 of our EC2 instance:

And there it is, we were able to connect to our Web API using the browser!

Giving our Deno Web API a Domain Name

To make our Deno Web API easier to use, we'll want to associate the IP Address with a domain name. Fortunately, AWS provides the Route 53 service, a DNS service where you can manage and register domain names. In my case, I purchased the domain name denoapi.com. For more information on how to use Route 53 see this guide.

Route 53 domain name services

Now that I have a domain name, I need to get an Elastic IP for my EC2 instance. EC2 instance IP's by default are dynamic, and may change when you restart the instance. As a result, we need a static IP that we can associate with our domain name. We can request a static IP through the Elastic IP section within the EC2 service:

The Elastic IP Page

Summary of my Elastic IP

Once we have our Elastic IP, we simply need to associate the Elastic IP with our running EC2 instance that is hosting our Deno Web API:

Associating the Elastic IP with the EC2 instance

Finally, now that our instance has a static IP, we simply need to associate our domain name with the IP, which can be done through Route 53. Simply click Hosted zones, click on your domain name, and create a record set for your domain with our static IP:

Creating a record set for my domain

It may take a little while for the record set to propagate throughout the DNS, but eventually, if everything worked correctly, you should be able connect to your Deno Web API in the browser using your domain name:

Setting up HTTPS (SSL/TLS) for our Deno Web API

Finally, one last thing we will want to do is to set up our Deno Web API to use HTTPS, as HTTP is not encrypted, so it is not suitable for any API where confidential data is being passed back and forth. To set up our Web API for HTTPS, we'll use Let's Encrypt, a service where we can easily request a certificate that can be used to encrypt our traffic and enable HTTPS for our Nginx server. Additionally, to generate the certificate we'll use certbot, a small program for easily generating certificates from Let's Encrypt.

Before installing certbot on our EC2 instance, we'll need to run the following commands one at a time to download all the dependencies for certbot:

Command Line Shell

sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/ sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm sudo yum-config-manager --enable epel* sudo yum repolist all

And now that all the dependencies are installed, lets install certbot (as well as the nginx plugin for certbot):

Command Line Shell

sudo yum install -y certbot python2-certbot-nginx

With certbot installed, we simply need to run this command to generate a certificate for our domain name. Additionally, this command will reconfigure our nginx configuration for us so we won't need to manually update the config files:

Command Line Shell

sudo certbot --nginx -d <your_domain_name>

In my case, since my domain name is denoapi.com the command I used looked like this:

Command Line Shell

sudo certbot --nginx -d denoapi.com

And generated this output. Note that certbot will prompt you for some info such as an email address and if you agree to the Terms of Service:

Command Line Shell Output

[ec2-user@ip ~]$ sudo certbot --nginx -d denoapi.com Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): denotutorials@protonmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server at https://acme-v02.api.letsencrypt.org/directory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (A)gree/(C)ancel: A

Certbot Output

Now let's reload the nginx server so that our new configurations will take effect:

Command Line Shell

sudo nginx -s reload

And if everything worked successfully, we should now be able to access our Deno Web API through HTTPS via the web browser:

Perfect! We now have created and released our Web API to the world using Deno on the backend. However, there is one last thing we should configure so that our certificate gets renewed automatically.

Setting Up a Recurring Cron Job to Renew Our Certificate

We can set up recurring certificate renewal by modifying the /etc/crontab file with the following command:

Command Line Shell

sudo nano /etc/crontab

We can add the following line to the bottom of the file to set up the cron job:

Command Line Shell

39 1,13 * * * root certbot renew --no-self-upgrade

Finally, restart the cron daemon with the following command:

Command Line Shell

sudo systemctl restart crond

And that's it! Now the certificate will auto renew twice a day at 1:39 A.M. and 1:39 P.M.

Summary

As our API grows, there are some other features we may want to add to make our Deno Web API more scalable and maintainable. Below are some suggestions to consider for the future of the API:

Adding a load balancer and spreading the backend Deno application across multiple EC2 instances.

Setting up health checks for the EC2 instances to monitor when an instance goes down.

Breaking up the Deno backend into controllers and routers to make the backend more scalable.

Forcing all HTTP requests to upgrade to HTTPS so that all traffic is encrypted by default.

If you have any comments or questions on this article, please feel free to reach out to me at denotutorials@protonmail.com