Author(s): Hayden Smith
Network
Internet
Web
If you want to learn more about networking, go and study COMP3331.
HTTP is an example of one of the protocols. It is the protocol of the web. The primary protocol you use to access URLs in your web browser.
HTTP: Hypertext Transfer Protocol
I.E. Protocol for sending and receiving HTML documents (nowadays much more)
Web browsers are applications to request and receive HTTP.
A very popular npm
library exists to allow you to run your own HTTP server with NodeJS. It's called Express Server.
import express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsimport express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsThis is us importing the express library
import express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsThis creates an instance of a server, and we define the network port to run on.
A port is essentially one of the roads in and out of a computer's network. There are often 65,000-ish.
import express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsThis line is a quirk of express
that is required in order for the data of many requests to be interpreted.
import express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsThis says that "when URL /
is accessed, call this function". The function sends some text to the person accessing that URL.
If we want our server to do more, we need to write lots more of these.
import express from 'express'; const app = express(); const port = 3000; app.use(express.text()); app.get('/potatotheory', (req, res) => { const name = req.query.name; res.json({ "message": `Hi ${name}`, }); }); app.post('/yuchao', (req, res) => { res.json({ name: 'Yuchao!', }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_express_basic.tsThis line actually starts the server (on a particular port). It essentially runs an infinite loop so the program runs forever constantly waiting for new people to "access" it via a certain URL.
Let's take a step back to learn about what servers are used for.
An API (Application Programming Interface) refers to an interface exposed by a particular piece of software.
The most common usage of "API" is for Web APIs, which refer to a "contract" that a particular service provides. The interface of the service acts as a black box and indicates that for particular endpoints, and given particular input, the client can expect to receive particular output.
A RESTful API is an application program interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. These 4 methods describe the "nature" of different API requests.
GET, PUT, POST, DELETE are HTTP Methods. They refer to CRUD operations.
Method | Operation |
---|---|
POST | Create |
GET | Read |
PUT | Update |
DELETE | Delete |
Different CRUD methods require different approaches for input. All output are the same.
import express from 'express'; const app = express(); const port = 3001; app.use(express.text()); app.get('/apple', (req, res) => { const name = req.query.name; res.send(JSON.stringify({ msg: `Hi ${name}, thanks for sending apple!`, })); }); // same for .put and .delete app.post('/orange', (req, res) => { const body = JSON.parse(req.body); const name = body.name; res.send(JSON.stringify({ msg: `Hi ${name}, thanks for sending orange!`, })); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_crud.tsSome methods of input work for all CRUD methods. Also, two routes can be the same name if they have different methods.
import express from 'express'; const app = express(); const port = 3001; app.use(express.text()); app.get('/apple/:name', (req, res) => { const name = req.params.name; res.send(JSON.stringify({ msg: `Hi ${name}, thanks for sending apple!`, })); }); app.post('/apple/:name', (req, res) => { const name = req.params.name; res.send(JSON.stringify({ msg: `Hi ${name}, thanks for sending apple!`, })); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_crud2.tsDifferent CRUD properties require different approaches for input.
req.query
(capture URL query string)req.params
(capture URL path variable)req.body
(capture body)req.params
(capture URL path variable)If we are embrace the use of JSON everywhere, we can make use of other library feature to clean up the code.
import express from 'express'; const app = express(); const port = 3001; app.use(express.json()); app.get('/apple', (req, res) => { const name = req.query.name; res.json({ msg: `Hi ${name}, thanks for sending apple!`, }); }); // same for .put and .delete app.post('/orange', (req, res) => { const name = req.body.name; res.json({ msg: `Hi ${name}, thanks for sending orange!`, }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_crud_json.tsIf we are embrace the use of JSON everywhere, we can make use of other library feature to clean up the code.
import express from 'express'; const app = express(); const port = 3001; app.use(express.json()); app.get('/apple', (req, res) => { const name = req.query.name; res.json({ msg: `Hi ${name}, thanks for sending apple!`, }); }); // same for .put and .delete app.post('/orange', (req, res) => { const name = req.body.name; res.json({ msg: `Hi ${name}, thanks for sending orange!`, }); }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_crud_json.tsSometimes we put version numbers in our routes to help us keep track, e.g.:
You will work with this principle in your major project.
Create a web server that uses CRUD to allow you to create, update, read, and delete a point via HTTP requests
Use a global variable to manage the state.
Restful APIs can also return errors:
import express from 'express'; const app = express(); const port = 3001; app.use(express.text()); app.get('/apple/:name', (req, res) => { const name = req.params.name; if (name === 'Hayden') { res.status(400).json({ error: 'Bad name', }); } else { res.json({ msg: `Hi ${name}, thanks for sending apple!`, }); } }); app.listen(port, () => { console.log(`Listening on port ${port}`); });
4.2_error.tsHow can we talk to a web server as a client?
sync-requests
How to download/install ARC:
You may need to use a tool like this in the final exam.
We can use an npm
package sync-requests
to allow us to programatically send RESTful API requests. npm install sync-requests
.
We can send them to our previous server.
import request from 'sync-request'; const res = request( 'GET', 'http://localhost:3001/apple', { qs: { name: 'Hayden', } } ); console.log(JSON.parse(String(res.getBody())));
4.2_requests.tsLet's look at the sync-request library to see if we can remove name=Hayden
from URL.
jest
import request from 'sync-request'; function get(route: string, qs: any) { const res = request( 'GET', `http://localhost:3001${route}`, { qs: qs, } ); return JSON.parse(String(res.getBody())); } describe('Test Apple', () => { test('If it returns a name string successfully', () => { const bodyObj = get('/apple', { name: 'Hayden', }); expect(bodyObj.msg).toBe('Hi Hayden, thanks for sending apple!'); }); }); describe('Test Orange', () => { test('If it returns a name string successfully', () => { const res = request( 'POST', 'http://localhost:3001/orange', { json: { name: 'Hayden' }, } ); const bodyObj = JSON.parse(String(res.getBody())); expect(bodyObj.msg).toBe('Hi Hayden, thanks for sending orange!'); }); });
4.2_requests.test.tsIn general, iteration 2 requires that you implement an HTTP server. However! Many of the routes that exist in iteration 2 are just wrappers of your iteration 1 functions.
Therefore it should be easy to "wrap" your iteration 1 functions with an HTTP server. I.E. Most of the "server" stuff you'll do is just routing, gathering bodies, and returning responses, while treating your iteration 1 functions like blackboxes.
Did you know we can make node
auto restart if new files are compiled?
If we:
npm install --save-dev nodemon
node
with nodemon
in package.tson
Then run npm run start
in a separate terminal.
Did you know we can make tsc
auto run if source files are changed?
If we:
--watch
flag to tsc
commandThen run npm run tsc
in a separate terminal.