Lect07 NodeJS 2
Lect07 NodeJS 2
Lect07 NodeJS 2
Lecture 7
Node.js (part 2)
Today’s Contents
● Handling POST requests in Express.js
● Express.js Middleware
● Express.js File Uploading
● Cookies in Express.js
● CommonJS Modules
● nodemon package
● File I/O in Node.js
○ Reading/writing text files
○ Saving data to text files in JSON format
● Fetch API Review
Handling POST ● Handling different POST requests with
Node.js
requests in Node.js ● Testing web services with Postman
POST Parameters
With GET endpoints, we've used req.params and req.query to get
endpoint parameters passed in the request URL.
But remember that POST requests carry data in the Request body!
Use Postman to send a post request of each type to the following URL and see
result:
https://hanustartup.org/wpr/PostTypes.php
Also inspect the request that Postman sends using Postman’s console.
Middleware & Request/Response Pipeline
The multer Module
A module for extracting POST parameters sent through multipart POST requests like
those sent with FormData.
Has a lot of functionality to support file uploading, but we will just use it to access the
body of a POST request sent through FormData, which we can't get with just
req.body.
To use, we'll need to set an option to ignore upload features with multer().none()
app.use(multer().none());
(*) Remember to run npm install multer in any project that uses it.
Supporting all POST requests
We often don't want to make assumptions about what method a client uses to POST
data. You are required to support all three with the appropriate middleware on all
assignments in this class.
// for application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true })); // built-in middleware
// for application/json
app.use(express.json()); // built-in middleware
// for multipart/form-data (required with FormData)
app.use(multer().none()); // requires the "multer" module
Postman demo #2
Use Node.js to handle three types of Post request (normal, form-data, json) at:
http://localhost/post_target
● The above example adds the requestTime property to the req object for every request
to this Express web server.
● A middleware function is any function which receives 3 parameters: req, res, next
● next is a Function which should be invoked at the end of the middleware's body
Express.js middleware
● An Express application is essentially a series of middleware function calls.
● Middleware functions are functions that have access to:
○ The request object (req)
○ The response object (res)
○ The next middleware function in the request-response cycle
● Middleware functions can perform the following tasks:
○ Execute any code
○ Make changes to the req and the res objects
○ End the request-response cycle
○ Call the next middleware function in the stack
● If the current middleware function does not end the request-response cycle, it
must call next() to pass control to the next middleware function. Otherwise,
the request will be left hanging.
Types of middleware
● Application-level middleware
● Router-level middleware
● Error-handling middleware
● Built-in middleware
● Third-party middleware
Third-party middleware
● This method still requires an HTML form, but will submit the form using
JavaScript instead of the default behavior of the form's submit button.
○ Note: you'll need to call the preventDefault() function to prevent the default
behavior.
How to handle file upload on server?
Example of using multer middleware on the form-handling endpoint to receive a
single uploaded file:
app.post(
'/upload',
multer({ dest: 'tmp/' }).single('avatar'),
async (req, res) => {
let ava = req.file;
await fs.rename(ava.path, 'public/images/' +
ava.originalname);
res.send("Upload complete!");
}
);
The uploaded file object
const multer = require('multer');
const upload = multer({ dest: 'tmp/' });
app.post('/upload', upload.single('avatar'), async (req, res) => {
res.json(req.file); // let's see what's inside this object
});
● The multiple attribute lets user select multiple files for this file input.
Uploading multiple files - Server
const upload = multer({ dest: 'tmp/' });
app.post(
'/photos/upload',
upload.array('photos', 12),
function (req, res) {
// req.files is an array of `photos` files
// req.body will contain other non-file fields, if any
}
);
● Cookies are data, stored in small text files on your browser's temporary
storage.
○ Temporary storage is the place where your browser caches html, css, js, images, etc.
○ Temporary storage is meant to speed-up page loads
● Cookies were originally invented to remember user information (to be used in
future visits). Today, cookies are used for authenticating users, storing user
preferences and tracking user's activities.
● Cookies are attached to every HTTP request from the client.
○ The more cookies, the bigger the request size → takes more time to send a request
○ In every request, the server-side application can read cookies from client
● Server-side application can send cookies to client.
Using Cookies on client-side
res.cookie('quandd', '123123', {
maxAge: 5000,
// expires works the same as the maxAge
expires: new Date('01 12 2023')
});
res.send('Cookie has been set!');
The CommonJS module
● In Node, each file is treated as a separate module. Consider the following code:
● On the first line, the module circle.js that is in the same directory is
imported (loaded). Here are the contents of circle.js:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
● If the exact filename is not found, Node.js will attempt to load the required file
with the added extensions: .js, .json, and finally .node. Therefore, the
above module can also be imported like the following:
const circle = require('./circle');
● If the module identifier passed to require() is not a core module, and does
not begin with '/', '../', or './', Node.js will attempt to load the module
from the ./node_modules directory.
The module wrapper
● Before a module's code is executed, Node.js will wrap it with a function
wrapper that looks like the following:
Usage
● nodemon wraps your application, so you can pass all the arguments you would
normally pass to your app:
nodemon [your node app]
● For CLI options, use the -h (or --help) argument:
nodemon -h
● Using nodemon is simple, if my application accepted a host and port as the
arguments, I would start it as so:
nodemon ./server.js localhost 8080
File I/O in Node.js Unlike the browser, we have access to
the file system when running Node.js.
fs.readFile(fileName, encodingType)
● fileName: (string) file name
● encodingType: file encoding (usually "utf8")
● Returns: Promise with a value holding the file's contents
fs.writeFile(fileName, contents)
● fileName: (string) file name
● contents: (string) contents to write to file
● Returns: Promise without any resolved value
Reading a file with error-first callbacks
As soon as fs.readFile finishes reading the file (or there's an error), it will
invoke the callback function that you provide.
Writing to files
const fs = require('fs').promises;
// ...
// ...
Error handling with async/await
You can read any file, including JSON. To parse JSON file contents and use as a JS
object, use JSON.parse().
● JSON files are text representations of JSON objects. When you read from
them, you will get a big string. When you write to them, you need to write a
string. Remember to use JSON.parse(jsonString) to turn a JSON string
into an object, and JSON.stringify(jsonObj)to turn a JSON object into a
string.
Title
Likes Shares
Comments
Could end up with JSON like:
{
posts: [
{
title: "This is my first blog post!",
body: "I don't have much to say, but here it is.",
time: "2021-05-17T23:37:22Z"
},
{
title: "This is my second blog post!",
body: "I still don't have a lot to say because I'm limited to 100 characters per line.",
time: "2021-05-18T23:37:22Z"
}
]
}
What about accessing the posts?
By ….
● All posts, most recent post, first, last, last week's, last months, that one post on
May 17th, all posts containing a word in a title, posts with a certain word in the
body, within a time range, having so many comments, have comments with
certain words, ....
Question: How might you write the code to access posts in different ways?
app.get('/blogs', async function(req, res) {
let title = req.query.title;
let post = req.query.post;
let date = req.query.date;
let sort = req.query.sort;
Like this, let posts = await getPosts();
let filteredPosts = [];
maybe? for (let i = 0; i < posts.length; i++) {
if (posts[i].title.includes(title)
&& posts[i].body.includes(post)
&& (new Date(posts[i].time)) >= (new Date(date))) {
filteredPosts.push(posts[i]);
}
}
if (sort === 'asc') {
// Sort filteredPosts w/ oldest first
} else if (sort === 'desc') {
// Sort filteredPosts w/ newest first
}
res.json(filteredPosts);
});
Discussion
The Fetch API provides a modern, flexible way to make network requests in
client-side JavaScript.
Replaces the older XMLHttpRequest API.
• Key Features:
Returns a Promise.
Supports various HTTP methods like GET, POST, PUT, DELETE.
Easy to use with async/await.
Basic Syntax
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
• Using asyn/await
Response Properties:
response.ok: Checks if the request was successful (status in the range 200-299).
response.status: HTTP status code of the response.
response.headers: Access response headers.
Fetch API
Error Handling
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch Error:', error);
}
}
Common Errors:
Network failures.
Invalid URLs.
Issues with CORS (Cross-Origin Resource Sharing).