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.
JSON (doesn't support comments):
{
"name": {
"first": "Luke",
"last": "Skywalker"
}
}
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
(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
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"
}
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
}
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);
}
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
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
You'll see an output message along the lines of:
Initialized empty Git repository in [..]/mailer/.git/
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
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'
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)