Created
October 10, 2019 22:57
-
-
Save asktami/a5464c74246b0eb95f9b630753ddcd69 to your computer and use it in GitHub Desktop.
Web APIs with Express
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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