DEV Community

Al Madireddy
Al Madireddy

Posted on • Edited on

The "Serverless" Backend MVP

Welcome to part 3! If you've made it this far, you must be itching to write some code already. Not to worry, because by the end of this post, you will have written and deployed the Node.js backend for our app.

Routes and REST API's, whom?

Before we get into the code itself, lets take a minute to deconstruct exactly what we are writing.

Our backend application will do one task: send our form contents in an email to (y)our inbox.

This entails a couple of steps: take input over the network, validate it, and then trigger an email send. Simple enough, but what exactly does it mean to take input over the network and how is it accomplished? The answer to that will become apparent through the code we are going to write; that's why I am writing this tutorial!

Communicating over the network: JSON

The one thing that we do need to know is the concept of using JSON to encode useful information.

JSON is a key-value pair system that can be used to store data in a very human-readable and organized way. JSON stands for "Java*Script **Object **N*otation because the syntax is very similar to how objects are defined in Javascript:

// This is Javascript so we have comments

let name = {
    first: "Luke",
    last: "Skywalker"
};
// name is now a variable pointing to an "object"

let firstName = name.first // firstName == "Luke" 
let lastName = name["last"] // subscript notation also works. Useful if you have spaces in the key.
Enter fullscreen mode Exit fullscreen mode

JSON (doesn't support comments):

{
    "name": {
        "first": "Luke",
        "last": "Skywalker"
    }
}
Enter fullscreen mode Exit fullscreen mode

JSON is a string representation of a JavaScript object (or python's dictionary or Java's HashMap, etc).

One important note is every key in the JSON is wrapped in double quotes, and in Javascript, it doesn't need to be. An object is denoted with a set of braces { ... } so in this case, the key name maps to a value of type object which itself contains two keys, first and last, within it. Notice how the example in it's entirety is contained within an object.

JSON supports numbers, strings, arrays, booleans, null, and other objects as values, but only strings as keys.

So what?

Remember in Part 1 how our code sent the text "hello there" over the network to the browser? Now, instead of plain text, we are going to be sending JSON.

If we wanted to encode some data from an app into the text we were sending, such as certain variables' values, we could easily have made up a data to text encoding specific to our app.

For example, if our program had a firstname and lastname variable in memory, we can make up an encoding for a "name" which looks like this: name - [first: <Luke> last: <Skywalker>];

Everytime we wanted to encode those two variables in order to write to a text file or send over the network from our program, we can output it like that. Your app which reads from the file or recieves that input over the network can decode it accordingly and place the values into its own variables.

This solves the problem just fine, but what if you wanted to open up your app for other people to use for their names? What if you were working with other developers on your team and all your apps needed to work together? Now, you would have to explain how to decode the messages from the text encoding into a first and last name that their program could use.

To alleviate this, the industry decided to use JSON. It's a standardized specification with specific rules that apps use to encode data. JSON encoding and decoding tools are built into virtually every useful language and therefore is fairly easy to work with. It also helps that is is a good compromise between machine and human readablility: you can just look at the key-value pairs to see the information and understand it.

So, in order to communicate across the internet, apps and services can just send JSON-encoded data back and forth.

Here is official website you can learn more about the JSON format.

So what's an API?

API stands for "Application Programming Interface." Compare this to a GUI, or "graphical user interface." The same semantics apply: An API is a way for your application or program to interface with other applications or programs. But what does that mean?

Well, think about Google Maps. They have a lot of code, science, math, and geospacial algorithms that make up the magical functionality of Google Maps. Google naturally wants to sell this functionality and make money from developers wanting to get routing in their applications.

However, they can't just give developers access to the code, because now other developers know the code and secrets. Doing this also means there is no way for Google to limit what they can do.

So, they run the Google Maps "app" on a server, and then expose an API to the outside world. Other developers' applications can interface with the Google Maps API.

Developers can make a request from their code that goes to a specific Google Maps URL (like our browser request to our hello world app's URL, could be maps.google.com/get-route or something). They'll encode the starting location and ending locations into that request and the Google Maps API will recieve this request. It will run its magic and send back a response with the route encoded as a list of coordinates in JSON format.

This way, developers' applications can interface with the Google Maps application programmatically, sending data back and forth. This is exactly how we are going to be using the SendGrid API to send emails: our Node.js app will request an email send, and SendGrid will send it and respond with a success.

This JSON API pattern is also used internally within an application. The best example is what we are writing here:

Our front-end will be a React application that will send a request to our back-end that contains the contents of the form encoded in JSON format. Our back-end sees this, validates, sends email, and responds with a success if SendGrid worked.

JSON API's are organized into specific routes. Taking whatever URL we get generated from Now's system as the "base," we would define a route to send emails maybe at /sendMail. Sending a request to that route would run the email sending code.

Let's get started

To begin, set up a folder for your project on your computer. Mine will be called mailer and will live in the same folder as most of my other software projects.

Open that folder up in your favorite code editor (again, VS Code or bust).

Create a file called now.json in that folder, and also a new folder called api. Create a file inside the api folder called index.js. This should be very much the same as what you did in the first part of the tutorial.

Your folder structure should look the same as before, like this:

tutorial/
  |- hello-world/
  |- mailer/
     |- now.json
     |- api/
        |- index.js
Enter fullscreen mode Exit fullscreen mode

(If you haven't done the first part of the tutorial, you won't have the hello-world folder.

Now in your terminal, cd into the mailer folder, and run:

$ npm init -y
Enter fullscreen mode Exit fullscreen mode

This will generate a file called package.json with the following contents:

{
  "name": "mailer",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

This is more JSON (see, its everywhere).

This file contains metadata about your project, and is useful for portability, since the list of your project's libraries also gets stored in this file when something is installed. You can open it up in VS Code and fill in the author and keyword tags if you'd like.

Copy in the following into now.json as well, similar to before:

{
    "version": 2
}
Enter fullscreen mode Exit fullscreen mode

MVP - Minimum Viable Product

It's time to define the MVP and get it deployed. For us, the MVP for the backend will be a deployed application that responds with a JSON "hello world" to a request. All other features can be added on one-by-one iteratively on top of that.

Look in Part 2 for the definition of an MVP.

So let's get that MVP deployed. Create our function similar to Part 1 that responds to a request with "Hello World":

module.exports = (req, res) => {
  let response = {
    message: "Hello World" 
  };

  res.json(response);
}
Enter fullscreen mode Exit fullscreen mode

Here, you can see some small differences from the first part of the tutorial: instead of setting the status and passing in a string to send(), we are calling res's json() function and passing in an object to it.

To get a small explanation of what the module.exports means as well as the fat-arrow => function syntax, checkout Part 1.

The next step to finish our MVP is to deploy this:

$ now
Enter fullscreen mode Exit fullscreen mode

This should run, produce some output, part of which is the URL at which this function is deployed. My URL as per the logs is tutorial-mailer.almadireddy.now.sh. Your's would probably be "mailer.[username].now.sh" but mine starts with tutorial-mailer because I wanted it set up that way for organizational purposes. You can look up how to modify your ZEIT project name.

Like before, if you access that URL with /api in your browser, you should be able to see the JSON that you sent from the app.

Congrats! The MVP is complete!

Setting up Continuous Deployment

One cool thing about ZEIT Now is that it allows us to easily set up Continuous Deployment with Github. You can attach a Github repository to a ZEIT project, and ZEIT will automatically deploy new versions as the selected branch on Github is updated.

This isn't intended to be a tutorial on git and github, so look that up on your own if you haven't used it before. But, because I'm nice, I'll show the commands I use to get the project up on my own github.

Let's set that up now. First, we need to initialize git in our project folder. Again in the mailer folder, run the following command:

$ git init
Enter fullscreen mode Exit fullscreen mode

You'll see an output message along the lines of:

Initialized empty Git repository in [..]/mailer/.git/
Enter fullscreen mode Exit fullscreen mode

Like the message says, this will have created a hidden .git folder inside your project. This folder contains all the information that git needs to work properly. Don't mess with it.

Create a new file called .gitignore. This file is a listing of file and folder names that should be ignored by git.

Add the following to that file:

node_modules/
.env
Enter fullscreen mode Exit fullscreen mode

The node_modules folder and .env file don't exist yet, but they will in later parts of the tutorial.

Now, create another file called README.md. This is a Markdown file, and you can look up how to use Markdown on your own. Add some information to this file saying what the project is, or whatever you want.

Save both those files, and commit:

$ git add .
$ git commit -m 'initial commit'
Enter fullscreen mode Exit fullscreen mode

Now, we need to set up a repository on our Github account. Go into Github, click the button for a new repository, name it, and mark as private if you want. Make sure the the checkbox for Initialize this repository with a README is unchecked, since we already have one. Make sure the Add .gitignore and Add a license dropdowns are the default None selection. Hit the green Create repository button.

You'll be brought to an empty repository page with some instructions. We are interested in the section that says …or push an existing repository from the command line. Run those two commands in the project folder to get your code uploaded.

If everything went well, you'll see output with the last line being something along the lines of Branch 'master' set up to track remote branch 'master' from 'origin'. Reload the github page, and you should see your files as well as the contents of the README.

Now, we can set up the connection between our Github and ZEIT on ZEIT's website.

Head to zeit.co/dashboard and click on the mailer project. Use the "GIT INTEGRATION" section to select and connect your Github repo. Depending on how you signed into ZEIT, it may take you into a authorization flow to connect your Github account so ZEIT can see your repositories.

An Important Aside: HTTP Verbs

HTTP verbs are a concept that will become pretty important in the next part. The most common/important ones are:

  • POST
  • GET
  • PUT
  • PATCH
  • DELETE

These are also called "HTTP methods" sometimes, especially in code. These verbs classify the type of request made. For example there can be a GET request to an endpoint which does one thing, or a POST request to an endpoint which does another. Every time we've been accessing a URL through the browser, we've been making a GET request.

To see information about our network requests, open up the Developer Tools in your browser and go to the Network tab. Do this on your app's ZEIT URL and hit Refresh. You'll be able to see information for your /api request if you click on it from the list of requests.

When designing an API, each of the HTTP verbs should correspond to the proper action from Create, Read, Update, Delete (CRUD). This is a common practice, and makes it easier to understand. There's nothing stopping you from doing something else, except convention.

This will be important for us, because we will make our our app trigger email sends only with a POST request. This is semantically more correct, since we are "creating" an email message or email-send action.

Here is a great summarization with a little more detail about HTTP verbs. Note the distinction for the two Update verbs, PATCH and PUT.

Coming up Next

With that, I think it's a good place to end Part 3. Feel free to mess around with the code and do cool things before I finish the next part, where we will finish up the backend with all the features we need to recieve input and send email.

Also, download Postman and look into using it to send requests to your deployed application. You can change the type of request being made using Postman, and you can look into how to detect the request method being used in Node.js.

Top comments (0)