Skip to content

Instantly share code, notes, and snippets.

@asktami
Created October 10, 2019 22:57
Show Gist options
  • Select an option

  • Save asktami/a5464c74246b0eb95f9b630753ddcd69 to your computer and use it in GitHub Desktop.

Select an option

Save asktami/a5464c74246b0eb95f9b630753ddcd69 to your computer and use it in GitHub Desktop.
Web APIs with Express
const express = require('express');
const morgan = require('morgan'); // Morgan logging tool
const app = express();
// This is middleware that requests pass through
// on their way to the final handler
// *** a request can pass through multiple functions before a response is sent ***
app.use(morgan('dev')); // Morgan logging tool
// Morgan documenation:
// https://www.npmjs.com/package/morgan
// This is the final request handler
// add a function that responds with some text to GET request to the root URL (/)
// app.get(PATH, HANDLER)
// Request documentation: https://expressjs.com/en/4x/api.html#req
app.get('/', (req, res) => {
res.send('Hello Express!');
});
// *************************
// SUM
// http://localhost:8000/sum?a=3&b=4
// Result: The sum of a and b is 7.
app.get('/sum', (req, res) => {
//1. get values from the request
// const a = req.query.a;
// const b = req.query.b;
const { a, b } = req.query;
//2. validate the values
if (!a) {
//3. a was not provided
return res.status(400).send('Please provide a');
}
if (!b) {
//3. b was not provided
return res.status(400).send('Please provide b');
}
//4. and 5. if everything is valid do the processing.
const numA = parseFloat(a);
const numB = parseFloat(b);
if (Number.isNaN(numA)) {
return res.status(400).send('a must be a number');
}
if (Number.isNaN(numB)) {
return res.status(400).send('b must be a number');
}
const numC = numA + numB;
const result = `The sum of ${numA} and ${numB} is ${numC}.`;
//6. send the response
res.send(result);
});
// CIPHER
// EX: http://localhost:8000/cipher?text=abc&shift=1
// Result: BCD
/*
Create an endpoint /cipher. The handler function should accept a query parameter named text and one named shift. Encrypt the text using a simple shift cipher also known as a Caesar Cipher. It is a simple substitution cipher where each letter is shifted a certain number of places down the alphabet. So if the shift was 1 then A would be replaced by B, and B would be replaced by C and C would be replaced by D and so on until finally Z would be replaced by A. using this scheme encrypt the text with the given shift and return the result to the client. Hint - String.fromCharCode(65) is an uppercase A and 'A'.charCodeAt(0) is the number 65. 65 is the integer value of uppercase A in UTF-16. See the documentation for details.
https://learncryptography.com/classical-encryption/caesar-cipher
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode
*/
app.get('/cipher', (req, res) => {
const { text, shift } = req.query;
// validation: both values are required, shift must be a number
if (!text) {
return res.status(400).send('text is required');
}
if (!shift) {
return res.status(400).send('shift is required');
}
const numShift = parseFloat(shift);
if (Number.isNaN(numShift)) {
return res.status(400).send('shift must be a number');
}
// all valid, perform the task
// Make the text uppercase for convenience
// the question did not say what to do with punctuation marks
// and numbers so we will ignore them and only convert letters.
// Also just the 26 letters of the alphabet in typical use in the US
// and UK today. To support an international audience we will have to
// do more
// Create a loop over the characters, for each letter, covert
// using the shift
const base = 'A'.charCodeAt(0); // get char code
const cipher = text
.toUpperCase()
.split('') // create an array of characters
.map(char => {
// map each original char to a converted char
const code = char.charCodeAt(0); //get the char code
// if it is not one of the 26 letters ignore it
if (code < base || code > base + 26) {
return char;
}
// otherwise convert it
// get the distance from A
let diff = code - base;
diff = diff + numShift;
// in case shift takes the value past Z, cycle back to the beginning
diff = diff % 26;
// convert back to a character
const shiftedChar = String.fromCharCode(base + diff);
return shiftedChar;
})
.join(''); // construct a String from the array
// Return the response
res.status(200).send(cipher);
});
// LOTTO (array)
// EX: http://localhost:8000/lotto?numbers=11&numbers=12&numbers=13&numbers=14&numbers=15&numbers=16
/*
Create a new endpoint /lotto that accepts an array of 6 distinct numbers between 1 and 20 named numbers. The function then randomly generates 6 numbers between 1 and 20. Compare the numbers sent in the query with the randomly generated numbers to determine how many match. If fewer than 4 numbers match respond with the string "Sorry, you lose". If 4 numbers match respond with the string "Congratulations, you win a free ticket", if 5 numbers match respond with "Congratulations! You win $100!". If all 6 numbers match respond with "Wow! Unbelievable! You could have won the mega millions!".
*/
app.get('/lotto', (req, res) => {
const { numbers } = req.query;
// validation:
// 1. the numbers array must exist
// 2. must be an array
// 3. must be 6 numbers
// 4. numbers must be between 1 and 20
if (!numbers) {
return res.status(200).send('numbers is required');
}
if (!Array.isArray(numbers)) {
return res.status(200).send('numbers must be an array');
}
const guesses = numbers
.map(n => parseInt(n))
.filter(n => !Number.isNaN(n) && (n >= 1 && n <= 20));
if (guesses.length != 6) {
return res
.status(400)
.send('numbers must contain 6 integers between 1 and 20');
}
// fully validated numbers
// here are the 20 numbers to choose from
const stockNumbers = Array(20)
.fill(1)
.map((_, i) => i + 1);
//randomly choose 6
const winningNumbers = [];
for (let i = 0; i < 6; i++) {
const ran = Math.floor(Math.random() * stockNumbers.length);
winningNumbers.push(stockNumbers[ran]);
stockNumbers.splice(ran, 1);
}
//compare the guesses to the winning number
let diff = winningNumbers.filter(n => !guesses.includes(n));
// construct a response
let responseText;
switch (diff.length) {
case 0:
responseText = 'Wow! Unbelievable! You could have won the mega millions!';
break;
case 1:
responseText = 'Congratulations! You win $100!';
break;
case 2:
responseText = 'Congratulations, you win a free ticket!';
break;
default:
responseText = 'Sorry, you lose';
}
// uncomment below to see how the results ran
// res.json({
// guesses,
// winningNumbers,
// diff,
// responseText
// });
res.send(responseText);
});
// *************************
// *************************
// HANDLE QUERYSTRING
// in browser: http://localhost:8000/greetings?name=Legolas&race=elf
app.get('/greetings', (req, res) => {
//1. get values from the request
const name = req.query.name;
const race = req.query.race;
//2. validate the values
if (!name) {
//3. name was not provided
return res.status(400).send('Please provide a name');
}
if (!race) {
//3. race was not provided
return res.status(400).send('Please provide a race');
}
//4. and 5. both name and race are valid so do the processing.
const greeting = `Greetings ${name} the ${race}, welcome to our kingdom.`;
//6. send the response
res.send(greeting);
});
// SEE QUERY OBJECT IN TERMINAL
app.get('/queryViewer', (req, res) => {
console.log(req.query);
res.end(); //do not send any data back to the client
});
// SEE REQUEST OBJECT
app.get('/echo/:name', (req, res) => {
const thisRoute = JSON.stringify(req.route);
const thisParams = JSON.stringify(req.params);
const thisQuery = JSON.stringify(req.query);
const responseText = `Here are some details of your request:
Base URL: ${req.baseUrl}
Host: ${req.hostname}
Path: ${req.path}
Route: ${req.route}
Params: ${req.params.name}
Query: ${req.query.id}
RouteObj: ${thisRoute}
ParamsObj: ${thisParams}
QueryObj: ${thisQuery}
Method: ${req.method}
Body: ${req.body}
App: ${req.app}
IP: ${req.ip}
Fresh: ${req.fresh}
Stale: ${req.stale}
`;
res.send(responseText);
});
/*
// RESULT
// in browser: http://localhost:8000/echo/tami?id=99
Here are some details of your request:
Base URL:
Host: localhost
Path: /echo/tami
Route: [object Object]
Params: tami
Query: 99
RouteObj: {"path":"/echo/:name","stack":[{"name":"<anonymous>
","keys":[],"regexp":{"fast_star":false,"fast_slash":false},"method":"get"}],"methods":{"get":true}}
ParamsObj: {"name":"tami"}
QueryObj: {"id":"99"}
Method: GET
Body: undefined
App: function(req, res, next) {
app.handle(req, res, next);
}
IP: ::1
Fresh: false
Stale: true
*/
// TEST ENDPOINTS
app.get('/burgers', (req, res) => {
res.send('We have juicy cheese burgers!');
});
app.get('/pizza/pepperoni', (req, res) => {
res.send('Your pizza is on the way!');
});
app.get('/pizza/pineapple', (req, res) => {
res.send("We don't serve that here. Never call again!");
});
// the console.log is only visible in Terminal
app.listen(8000, () => {
console.log('Express server is listening on port 8000!');
});
// run this app with:
// node app.js
// then view in browser at:
// http://localhost:8000/
const express = require('express');
const morgan = require('morgan'); // Morgan logging tool
const app = express();
// This is middleware that requests pass through
// on their way to the final handler
// *** a request can pass through multiple functions before a response is sent ***
app.use(morgan('dev')); // Morgan logging tool
// Morgan documenation:
// https://www.npmjs.com/package/morgan
// This is the final request handler
// add a function that responds with some text to GET request to the root URL (/)
// app.get(PATH, HANDLER)
// Request documentation: https://expressjs.com/en/4x/api.html#req
app.get('/', (req, res) => {
res.send('Hello Express!');
});
// *************************
// SUM
// http://localhost:8000/sum?a=3&b=4
// Result: The sum of a and b is 7.
app.get('/sum', (req, res) => {
//1. get values from the request
// const a = req.query.a;
// const b = req.query.b;
const { a, b } = req.query;
//2. validate the values
if (!a) {
//3. a was not provided
return res.status(400).send('Please provide a');
}
if (!b) {
//3. b was not provided
return res.status(400).send('Please provide b');
}
//4. and 5. if everything is valid do the processing.
const numA = parseFloat(a);
const numB = parseFloat(b);
if (Number.isNaN(numA)) {
return res.status(400).send('a must be a number');
}
if (Number.isNaN(numB)) {
return res.status(400).send('b must be a number');
}
const numC = numA + numB;
const result = `The sum of ${numA} and ${numB} is ${numC}.`;
//6. send the response
res.send(result);
});
// CIPHER
// EX: http://localhost:8000/cipher?text=abc&shift=1
// Result: BCD
/*
Create an endpoint /cipher. The handler function should accept a query parameter named text and one named shift. Encrypt the text using a simple shift cipher also known as a Caesar Cipher. It is a simple substitution cipher where each letter is shifted a certain number of places down the alphabet. So if the shift was 1 then A would be replaced by B, and B would be replaced by C and C would be replaced by D and so on until finally Z would be replaced by A. using this scheme encrypt the text with the given shift and return the result to the client. Hint - String.fromCharCode(65) is an uppercase A and 'A'.charCodeAt(0) is the number 65. 65 is the integer value of uppercase A in UTF-16. See the documentation for details.
https://learncryptography.com/classical-encryption/caesar-cipher
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode
*/
app.get('/cipher', (req, res) => {
const { text, shift } = req.query;
// validation: both values are required, shift must be a number
if (!text) {
return res.status(400).send('text is required');
}
if (!shift) {
return res.status(400).send('shift is required');
}
const numShift = parseFloat(shift);
if (Number.isNaN(numShift)) {
return res.status(400).send('shift must be a number');
}
// all valid, perform the task
// Make the text uppercase for convenience
// the question did not say what to do with punctuation marks
// and numbers so we will ignore them and only convert letters.
// Also just the 26 letters of the alphabet in typical use in the US
// and UK today. To support an international audience we will have to
// do more
// Create a loop over the characters, for each letter, covert
// using the shift
const base = 'A'.charCodeAt(0); // get char code
const cipher = text
.toUpperCase()
.split('') // create an array of characters
.map(char => {
// map each original char to a converted char
const code = char.charCodeAt(0); //get the char code
// if it is not one of the 26 letters ignore it
if (code < base || code > base + 26) {
return char;
}
// otherwise convert it
// get the distance from A
let diff = code - base;
diff = diff + numShift;
// in case shift takes the value past Z, cycle back to the beginning
diff = diff % 26;
// convert back to a character
const shiftedChar = String.fromCharCode(base + diff);
return shiftedChar;
})
.join(''); // construct a String from the array
// Return the response
res.status(200).send(cipher);
});
// LOTTO (array)
// EX: http://localhost:8000/lotto?numbers=11&numbers=12&numbers=13&numbers=14&numbers=15&numbers=16
/*
Create a new endpoint /lotto that accepts an array of 6 distinct numbers between 1 and 20 named numbers. The function then randomly generates 6 numbers between 1 and 20. Compare the numbers sent in the query with the randomly generated numbers to determine how many match. If fewer than 4 numbers match respond with the string "Sorry, you lose". If 4 numbers match respond with the string "Congratulations, you win a free ticket", if 5 numbers match respond with "Congratulations! You win $100!". If all 6 numbers match respond with "Wow! Unbelievable! You could have won the mega millions!".
*/
app.get('/lotto', (req, res) => {
const { numbers } = req.query;
// validation:
// 1. the numbers array must exist
// 2. must be an array
// 3. must be 6 numbers
// 4. numbers must be between 1 and 20
if (!numbers) {
return res.status(200).send('numbers is required');
}
if (!Array.isArray(numbers)) {
return res.status(200).send('numbers must be an array');
}
const guesses = numbers
.map(n => parseInt(n))
.filter(n => !Number.isNaN(n) && (n >= 1 && n <= 20));
if (guesses.length != 6) {
return res
.status(400)
.send('numbers must contain 6 integers between 1 and 20');
}
// fully validated numbers
// here are the 20 numbers to choose from
const stockNumbers = Array(20)
.fill(1)
.map((_, i) => i + 1);
//randomly choose 6
const winningNumbers = [];
for (let i = 0; i < 6; i++) {
const ran = Math.floor(Math.random() * stockNumbers.length);
winningNumbers.push(stockNumbers[ran]);
stockNumbers.splice(ran, 1);
}
//compare the guesses to the winning number
let diff = winningNumbers.filter(n => !guesses.includes(n));
// construct a response
let responseText;
switch (diff.length) {
case 0:
responseText = 'Wow! Unbelievable! You could have won the mega millions!';
break;
case 1:
responseText = 'Congratulations! You win $100!';
break;
case 2:
responseText = 'Congratulations, you win a free ticket!';
break;
default:
responseText = 'Sorry, you lose';
}
// uncomment below to see how the results ran
// res.json({
// guesses,
// winningNumbers,
// diff,
// responseText
// });
res.send(responseText);
});
// *************************
// *************************
// HANDLE QUERYSTRING
// in browser: http://localhost:8000/greetings?name=Legolas&race=elf
app.get('/greetings', (req, res) => {
//1. get values from the request
const name = req.query.name;
const race = req.query.race;
//2. validate the values
if (!name) {
//3. name was not provided
return res.status(400).send('Please provide a name');
}
if (!race) {
//3. race was not provided
return res.status(400).send('Please provide a race');
}
//4. and 5. both name and race are valid so do the processing.
const greeting = `Greetings ${name} the ${race}, welcome to our kingdom.`;
//6. send the response
res.send(greeting);
});
// SEE QUERY OBJECT IN TERMINAL
app.get('/queryViewer', (req, res) => {
console.log(req.query);
res.end(); //do not send any data back to the client
});
// SEE REQUEST OBJECT
app.get('/echo/:name', (req, res) => {
const thisRoute = JSON.stringify(req.route);
const thisParams = JSON.stringify(req.params);
const thisQuery = JSON.stringify(req.query);
const responseText = `Here are some details of your request:
Base URL: ${req.baseUrl}
Host: ${req.hostname}
Path: ${req.path}
Route: ${req.route}
Params: ${req.params.name}
Query: ${req.query.id}
RouteObj: ${thisRoute}
ParamsObj: ${thisParams}
QueryObj: ${thisQuery}
Method: ${req.method}
Body: ${req.body}
App: ${req.app}
IP: ${req.ip}
Fresh: ${req.fresh}
Stale: ${req.stale}
`;
res.send(responseText);
});
/*
// RESULT
// in browser: http://localhost:8000/echo/tami?id=99
Here are some details of your request:
Base URL:
Host: localhost
Path: /echo/tami
Route: [object Object]
Params: tami
Query: 99
RouteObj: {"path":"/echo/:name","stack":[{"name":"<anonymous>
","keys":[],"regexp":{"fast_star":false,"fast_slash":false},"method":"get"}],"methods":{"get":true}}
ParamsObj: {"name":"tami"}
QueryObj: {"id":"99"}
Method: GET
Body: undefined
App: function(req, res, next) {
app.handle(req, res, next);
}
IP: ::1
Fresh: false
Stale: true
*/
// TEST ENDPOINTS
app.get('/burgers', (req, res) => {
res.send('We have juicy cheese burgers!');
});
app.get('/pizza/pepperoni', (req, res) => {
res.send('Your pizza is on the way!');
});
app.get('/pizza/pineapple', (req, res) => {
res.send("We don't serve that here. Never call again!");
});
// the console.log is only visible in Terminal
app.listen(8000, () => {
console.log('Express server is listening on port 8000!');
});
// run this app with:
// node app.js
// then view in browser at:
// http://localhost:8000/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment