User
Client (Browser)
DNS Server 1
"You requested cityexplorer.com. I don't know where to find that."
"I know that URL!
The IP Address of cityexplorer.com is 255.255.255.255."
"I will now make an
HTTP request to cityexplorer.com/"
City Explorer's Server
DNS Server 2
"I've never heard of cityexplorer.com. Help!"
"Cool, I'm going to save that in my memory for next time"
"I found the path you requested. Here are the files (HTML, CSS, JS, Json and images) you need."
City Explorer's Database
"I will now make an
HTTP request to cityexplorer.com/location?city=Chicago"
"Let me check my database to see if I know Chicago's latitude and longitude"
"Nope"
Location IQ API
Ok, I will ask my friends at locationiq.com
"41.8837736,
-87.622191"
Open Weather API
"Cloudy, with a chance of rain"
[After storing this data] What's the weather forecast at that location?
Here are the HTML, CSS, JS, Json, and Images you need.
"I better take my umbrella"
"What's the weather today?"
[types "Chicago" in the search bar]
Our web server needs to start with a package.json file to store project configurations. Create one with this command:
npm init -y
npm install dotenv express
{
"name": "deployment",
"version": "1.0.0",
"description": "",
"main": "server.js",
"dependencies": {
"dotenv": "^8.2.0",
"express": "^4.17.1"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
A list of dependencies in package.json, package-lock.json, and node_modules folder will be added automatically
Dependencies are used to specify any other modules that a given module (represented by the package.json) requires to work.
If you clone a project that comes with a server.js file, it will likely have dependencies in it. When you run "npm install", it will install any modules in the package.json dependencies list.
A package-lock.json file ensures that future installations of the project use the same versions of the dependencies.
Our server requires 5 sections:
Application dependencies (Line 3)
Import the modules needed for our application to run
Application setup (Line 5)
Make an express app that is configured to whatever environment we are using
Route definitions (Line 7)
Define what path(s) will respond to the user upon request
Route handlers (Line 9)
Callback functions that run after the route is requested
App listener (Line 11)
Make sure the server is listening for requests
'use strict';
// Application Dependencies
// Application Setup
// Route Definitions
// Route Handlers
// App listener
First we need to require express and assign the returned function object to a variable (Line 4).
This code tells node to go into the node_modules folder and make express available in our project.
The next thing we do is make an app from express (Line 7).
Next we need a port to listen to network requests (Line 8).
Lastly, we need to tell the server to listen on that port and return a message letting us know when the server is on. (Line 15).
This line will always be the last line of the server code.
Run this command to process the start script from package.json
npm start
'use strict';
// Application Dependencies
const express = require('express');
// Application Setup
const app = express();
const PORT = 3000;
// Route Definitions
// Route Handlers
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));I use the computer’s terminal to start the server and I use VS Code’s terminal to do ACPs. Having multiple terminal instances open is useful.
In a web browser navigate to localhost:3000 or 127.0.0.1:3000
If you know your computer’s local IP address (something like 192.168.NNN.NNN), you can open another device connected to your network and type 192.168.NNN.NNN:3000 to view the server.
On Windows open up the Command Prompt via your Windows Start menu. Type in “ipconfig” and hit Enter. Look for the line that reads “IPv4 Address.” The number across from that text is your local IP address.
On Mac, click on the Apple icon in the top-left corner of your desktop and open System Preferences. Then, click on Network. Find your connection type—wifi or Ethernet—and click on it. (It should have a green dot if it’s active.). Look to the right, and you’ll see a sentence that looks like “...is connected to ..... and has the IP address...” The number that follows is your computer’s IP address.
Dotenv configures how our app runs on different environments. (Line 5)
For instance, we may want to run on port 3000 on our local computer, but Heroku may want to run on a different port, like 3001 (Line 9 and 1).
Square, Stripe, and other payment processors use separate environment domains for testing and production.
The call to require() returns an object that has a config() method in it.
Because we're instructing the server to run on a different port here, we need to shut down the server and restart it.
In the terminal you will have to press Ctrl + C to shut it down, then run "npm start" again.
Whenever the server.js file changes, the server will need to restart. It won't do that automatically.
If you install nodemon ("npm install -g nodemon") you can use "nodemon ." instead of "npm start" and it will automatically restart on changes.
The app will be installed globally for all projects. You do not need a nodemon dependency in your package.json file in case if it gets added.
If you have a syntax error your app will
crash automatically and will need to be
fixed before automatically restarting
'use strict';
// Application Dependencies
const express = require('express');
require('dotenv').config();
// Application Setup
const app = express();
const PORT = process.env.PORT || 3000;
// Route Definitions
// Route Handlers
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
PORT=3001
We don't have a homepage yet so the browser displays "Cannot GET /". Let's run a .get() function on our app to respond with a simple greeting. (Lines 12, 16-18)
This code says when someone requests the "/greet" path of our website we want to execute a callback function that responds with a 200 (OK) status code and send them a simple message.
If you add app.use('*', ...), any other requests will be served a 404 (Not found) message. (Line 13, 21-22)
'use strict';
// Application Dependencies
const express = require('express');
require('dotenv').config();
// Application Setup
const app = express();
const PORT = process.env.PORT || 3000;
// Route Definitions
app.get('/greet', greetHandler);
app.use('*', notFoundHandler);
// Route Handlers
function greetHandler(request, response) {
response.status(200).send('Hello World!');
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));If you console.log(request) inside a route handler you will see all of the data that is getting sent to our server.
request.method will return "GET"
request.path will return "/greet"
Press Ctrl + L to clear the terminal console.
Please note that response.send() will display the message in the browser’s body, while console.log() will display the message in the terminal, not in the browser’s console. Use console.log() to troubleshoot errors and debug.
It's common (not required) to direct users to static HTML, CSS, and JavaScript files when they navigate to the root of your website. Those files can be placed inside of a folder called "public". Running a .use() function on our app will respond with those files. (Line 12)
'use strict';
// Application Dependencies
const express = require('express');
require('dotenv').config();
// Application Setup
const app = express();
const PORT = process.env.PORT || 3000;
// Route Definitions
app.use(express.static('./public'));
app.get('/greet', greetHandler);
app.use('*', notFoundHandler);
// Route Handlers
function greetHandler(request, response) {
response.status(200).send('Hello World!');
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));Databases and APIs contain vast amounts of data for use on our public website. Servers are the connection between the two.
When the server receives the data it will respond to the client with Json.
First I assign a new path for data retrieval (Line 14)
The response the server receives from the external source will likely be in the form of an array of objects. (Lines 23-34)
It will respond to the client with a status code and the data formatted as Json. (Line 35)
Then in our public folder we can write regular JavaScript files with jQuery that processes ajax requests.
'use strict';
// Application Dependencies
const express = require('express');
require('dotenv').config();
// Application Setup
const app = express();
const PORT = process.env.PORT || 3000;
// Route Definitions
app.use(express.static('./public'));
app.get('/greet', greetHandler);
app.get('/data', dataHandler);
app.use('*', notFoundHandler);
// Route Handlers
function greetHandler(request, response) {
response.status(200).send('Hello World!');
}
function dataHandler(request, response) {
const airplanes = [
{
departure: new Date(2020, 7, 24),
canFly: true,
pilot: 'Well Trained'
},
{
departure: new Date(2020, 7, 25),
canFly: false,
pilot: 'NA'
}
];
response.status(200).json(airplanes);
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));'use strict';
// Application Dependencies
const express = require('express');
require('dotenv').config();
// Application Setup
const app = express();
const PORT = process.env.PORT || 3000;
// Route Definitions
app.use(express.static('./public'));
app.get('/greet', greetHandler);
app.get('/data', dataHandler);
app.get('/bad', (request, response) => { throw new Error('Sorry the the inconvenience.'); });
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function greetHandler(request, response) {
response.status(200).send('Hello World!');
}
function dataHandler(request, response) {
const airplanes = [
{
departure: new Date(2020, 7, 24),
canFly: true,
pilot: 'Well Trained'
},
{
departure: new Date(2020, 7, 25),
canFly: false,
pilot: 'NA'
}
];
response.status(200).json(airplanes);
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express'); //
// Application Setup
const app = express(); //
const PORT = process.env.PORT || 3000; //
// Route Definitions
app.get('/', rootHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function notFoundHandler(request, response) {
response.status(404).send('404 - Not Found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`)); //
Today we will create a front-end app (hosted on GitHub pages) that can talk to our back-end app (hosted on Heroku).
We will also set up a location and restaurant API endpoint that uses dummy data. Soon we'll fill that in with real data.
Cors (Cross Origin Resource Sharing) creates middleware that adds useful functionality to our application by allowing connections to be made with outside sources.
Add Cors by running "npm install cors"
Notice that this adds a new dependency to package.json
We will reference cors in the application dependencies (Line 6)
After our app is setup, we will use cors. (Line 11)
It’s common to forget inner parenthesis
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Follow the steps to create a Review App. Then move the changes to Staging and Production as discussed earlier.
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const locationData = require('./data/location.json');
const location = new Location(city, locationData);
response.status(200).send(location);
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.businesses;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).json({ notFound: true });
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, locationData) {
this.search_query = city;
this.formatted_query = locationData[0].display_name;
this.latitude = parseFloat(locationData[0].lat);
this.longitude = parseFloat(locationData[0].lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));