Uploaded by dth1617tranthientrong12a6

NodeJS

advertisement
Biên soạn: Trần Thiên Trọng – tranthientrong.it@gmail.com
SETUP NODEJS
https://wpbeaches.com/installing-node-js-on-macos-big-sur-and-earlier-macos-versions/
Setup NodeJS project
❯ npm init
package name: (crud_mysql) crud_mysql
version: (1.0.0) 1.0.0
description: This is project to demonstrate how to CRUD in NodeJS using MySQL
git repository:
author: Tran, Trong thien
license: (ISC) ISC
About to write to /Users/trong/Documents/Documents-Cloud/DEV/NodeJS/crud_mysql/package.json:
{
"name": "crud",
"version": "1.0.0",
"description": "This is project to demonstrate how to CRUD in NodeJS using MySQL",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Tran, Trong thien",
"license": "ISC"
}
Is this OK? (yes) yes
Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file.
NodeJS project on new machine
Bất cứ khi nào mở Node Project trên 1 máy mới, ta đều phải dùng
npm install
Nó sẽ nhìn vào file package.json để install các dependencies
NODE MODULE SYSTEM
Module giống như Library, đây là cách ta import một Module
import * as fs from 'fs';
Lưu ý thêm cái này vào package.js để sử dụng ES6, mới import được
{
}
"type":"module",
export
Để sử dụng các field từ file khác, ta phải import và export
import * as service from './service.js';
export const name = 'Trong'
console.log(service.name)
export function myFunction(name) {
return name;
console.log(service.myFunction('Trong'))
}
❯ node app.js
Trong
Trong
FILE SYSTEM
Current Directory
import path from 'path';
const dirname = path.resolve();
console.log(dirname)); -> /Users/trong/Documents/Documents-Cloud/DEV/NodeJS/practice/web-server
console.log(path.join(dirname, '../public')); -> /Users/trong/Documents/DocumentsCloud/DEV/NodeJS/practice/public
Input from command line
1 điều buồn cười là nodeJS không có cách nào đơn giản để nhận input từ user cả. Mà phải phụ thuộc vào cái module.
readline là module có sẵn trong NodeJS
import * as readline from 'readline';
Who are you? trong
Hey there trong!
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Who are you?', name => {
console.log(`Hey there ${name}!`);
rl.close();
});
Write File
import * as fs from 'fs';
fs.writeFileSync('notes.txt', 'This is note');
fs.appendFileSync('notes.txt', ', my name is Trong');
ASYNCHRONOUS
Đơn giản là nó sẽ chạy ở Thread mới không đồng bộ với flow chính, thread này ở C++, đó là lý do vì sao người ta vẫn gọi là
single-thread.
*Note: Tại sao bên dưới setTimeout là 0 giây nhưng vẫn print sau Stopping -> Bởi vì các function trong asynchronous được
gọi là callback, và callback chỉ được gọi khi flow chính hoàn tất.
console.log(chalk.green('Starting'));
setTimeout(() => {
console.log('1s passed')
}, 1000);
setTimeout(() => {
console.log('0s passed')
}, 0);
console.log(chalk.green('Stopping'));
Callback Function
Là 1 function BÌNH THƯỜNG ( gọi là B ), nhưng được truyền dưới dạng Agurment và Parameter của Function khác ( gọi là
A ), mỗi lần ta gọi B, B sẽ chạy ngày lập tức nhưng chưa chắc chạy A, A sẽ được Gọi lại để chạy vào một thời điểm nào đó
mà ta xác định trong B
Promises
Hiểu đơn giản thì Promises là một loại function mà kết quả trả về của nó là Asynchronous. Nghĩa là nó không trả về ngay
khi bạn gọi mà nó “HỨA” trong tương lai sẽ trả về, vì thể nên tất cả function Promises đều có hàm .then( )
Bên trong Promise cho ta 2 parameter, đây là 2 callback để xác định khi nào then( ), khi nào cacth( )
à Nếu bạn gọi callback tên là resolve, thì kết quả của nó sẽ được run ở then( )
à Nếu bạn gọi callback tên là reject, thì kết quả của nó sẽ được run ở catch( )
const functionWillReturnInFuture = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("I'm from 1s later");
}, 1000);
});
functionWillReturnInFuture.then((result) => {
console.log(`Result: ${result}`);
}).catch((error)=>{
console.log(error);
});
à Result: I'm from 1s later
const functionWillReturnInFuture = new Promise((resolve, reject) => {
setTimeout(() => {
reject("I'm from 1s later");
}, 1000);
});
functionWillReturnInFuture.then((result) => {
console.log(`Result: ${result}`);
}).catch((error)=>{
console.log(`Error: ${error}`);
});
à Error: I'm from 1s later
Kết hợp resolve và reject:
const functionWillReturnInFuture = (someValue) =>{
return new Promise((resolve, reject) => {
setTimeout(() => {
if (someValue > 0){
resolve("I'm Positive");
} else {
reject("ERROR Negative");
}
}, 1000);
});
}
Promises Chaining
Nếu ta gọi Promise trong 1 Promise khác thì thế lào? -> dùng then( ).then( ) thôi
const functionWillReturnInFuture = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("I'm from 1s later");
}, 1000);
});
functionWillReturnInFuture.then((result) => {
console.log(`Result 1: ${result}`);
return functionWillReturnInFuture;
}
).then((result) => {
console.log(`Result 2: ${result}`);
}
).catch((error) => {
console.log(`Error: ${error}`);
});
à
Result 1: I'm from 1s later
Result 2: I'm from 1s later
Async / Await
async function read() {
await client.connect();
const database = client.db(databaseName);
}
await read();
Convert from then( ).catch( ) à try-catch
findOne({_id: new ObjectId(_idParam)})
.then((result) => {
if (result == null) {
response.status(404).send(`TASK NOT FOUND`);
} else {
response.status(200).send(`FOUND ${result}`);
}
})
.catch((error) => {
response.status(500).send(`${error}`);
});
try {
const foundTasks = await Task.find({});
if (foundTasks == null) {
response.status(404).send(`TASKS NOT FOUND`);
} else {
response.status(200).send(`FOUND ${foundTasks}`);
}
} catch (error) {
response.status(500).send(`${error}`);
}
HTTP REQUEST
Có nhiều Module để có thể gửi request trong NodeJS, tuy nhiên theo document thì ta nên dùng
https://www.npmjs.com/package/axios
import axios, * as others from 'axios';
axios
.get('https://google.com')
.then(response => {
console.log(`Status Code: ${response.status}`)
console.log(`Data: ${response.data}`)
})
.catch(error => {
console.error(error)
});
Status Code: 200
Data: ...bla bla bla.................
GET
import axios, * as others from 'axios';
axios.get(WEATHER_URL+`/current?access_key=${API_KEY}
&query=Vietnam`)
.then(response => {
// handle success
console.log(`StatusCode: ${response.status}`);
console.log(response.data);
})
.catch(error => {
console.log(error);
})
.then(() => {
// always executed
});
StatusCode: 200
{
location: {
name: 'Hanoi',
country: 'Vietnam',
region: '',
lat: '21.033',
lon: '105.850',
timezone_id: 'Asia/Ho_Chi_Minh',
localtime: '2022-02-15 19:21',
localtime_epoch: 1644952860,
utc_offset: '7.0'
},
current: {
observation_time: '12:21 PM',
temperature: 16,
weather_code: 122,
weather_descriptions: [ 'Overcast' ],
wind_speed: 7,
wind_degree: 60,
wind_dir: 'ENE',
pressure: 1015,
precip: 0,
humidity: 88,
cloudcover: 100,
feelslike: 16,
uv_index: 1,
visibility: 9,
is_day: 'no'
}
}
axios.get(`${WEATHER_URL}/current?access_key=${API_KEY}&query=Vietnam`)
.then(response => {
// handle success
StatusCode: 200
console.log(`StatusCode: ${response.status}`);
Current
console.log(`Current temperature: ${response.data.current.temperature}`);
temperature: 16
console.log(typeof response.data);
object
})
WEBSERVER – EXPRESSJS
-> Hiểu đơn giản thì ExpressJS giúp ích rất nhiều trong việc tạo WebServer ( REST API )
ExpressJS is a light-weight web application framework.
You can then use a database like MongoDB with Mongoose (for modeling) to provide a backend for your Node.js application.
Express.js basically helps you manage everything, from routes, to handling requests and views.
import * as express from 'express';
const app = express();
Start Listen Server
import express, * as others from 'express';
import chalk from 'chalk';
const app = express();
app.get('', (request, response) => {
response.send('This is response');
})
app.get('/about', (request, response) => {
response.send('About');
})
app.listen(3000,()=>{
console.log(chalk.green(`listening on port
3000`));
})
Đây là cách nó hoạt động:
Send GET Request to localhost:3000
Send Response with body ‘This is response’
Client
http://localhost:3000
this is response
http://localhost:3000/about
About
Return Type
JSON
Server
app.get('/trong', (request, response) => {
response.send([
{
name: "Thien Trong",
age: 22
}
]);
})
http://localhost:3000/trong
[
{
"name": "Thien Trong",
"age": 22
}
]
Directory
Ví dụ ta muốn return public directory
import express, * as others from 'express';
import path from 'path';
import chalk from 'chalk';
const app = express();
const dirname = path.resolve();
const webpageDir = path.join(dirname, '/public')
console.log(webpageDir);
app.use(express.static(webpageDir));
app.listen(3000, () => {
console.log(chalk.green(`listening on port 3000`));
})
Vì index.html có trong directory nên nó sẽ tự loaded
Query String
Nhắc lại, Query String là đoạn phía sau dấu ? trong URL
Ta thường nhớ đến payload trong POST request để gửi đến Server mà quên mất Query String chính là thông tin trong form
mà ta có thể gửi đến Server luôn, vì thể Query String rất quan trọng nhé.
Ta có thể gọi response.query để lấy giá trị này
app.get('/product', (request, response) => {
console.log(request.query);
console.log(request.query.name);
console.log(request.query.age);
})
http://localhost:3000/product?name=’Trong’&age=22
Ta có thể set điều kiện như vầy:
app.get('/product', (request, response) => {
if(!request.query.name){
response.send("No name provided");
} else {
response.send(request.query);
}
})
Create Endpoint
Ở ví dụ dưới đây, ta sẽ tạo một endpoint với URL là:
http:localhost.com:3000/weather
Ta sẽ lấy dữ liệu từ một API khác nữa là weatherstack.com ( mục đích có data để làm mẫu thôi )
const API_KEY = 'ed5c69199afcb0bdddcf0bb4efb3b676';
const WEATHER_URL = 'http://api.weatherstack.com';
export const getWeather = (country, callbackFunc)=>{
axios
.get(`${WEATHER_URL}/current?access_key=${API_KEY}&query=${country}`)
.then(response => {
callbackFunc(response.data.current)
})
.catch(error => {
});
}
import express, * as others from 'express';
import {getWeather} from './weather-service.js';
app.get('/weather', (request, response) => {
if(!request.query.country){
response.send("No country provided");
} else {
getWeather(request.query.country, (weatherData)=>{
response.send(weatherData);
});
}
})
Send Request from front end
Nếu ở NodeJS có các method của axios thì ở Client ( cụ thể là Javasrcript ) sẽ có method gọi là fetch gửi request đến URL
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="script.js"></script>
</head>
<body>
<h1> Weather </h1>
</body>
</html>
console.log('Client side javascript file is loaded!')
const API_KEY = 'ed5c69199afcb0bdddcf0bb4efb3b676';
const WEATHER_URL = 'http://api.weatherstack.com';
fetch(`${WEATHER_URL}/current?access_key=${API_KEY}&query=Vietnam`).then((response)=>{
response.json().then((data)=>{
console.log(data);
});
});
Lưu ý bởi vì tất cả đoạn code trên đều xảy ra ở Client, bạn sẽ không nhìn thấy gì ở terminal
Thay vào đó bạn sẽ nhìn thấy console.log ở browser
Send Response back to Front End
Front End
Gửi 1 Request đến http:localhost:3000/weather?country=Vietnam
fetch(`/weather?country=Vietnam`,{
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}).then((response)=>{
response.json().then((data)=>{
console.log(data);
});
});
Backend lắng nghe trên endpoint http:localhost:3000/weather và return JSON
app.get('/weather', (request, response) => {
if(!request.query.country){
response.send("No country provided");
} else {
getWeather(request.query.country, (weatherData)=>{
response.send(weatherData);
});
}
})
app.listen(3000, () => {
console.log(chalk.green(`listening on port 3000`));
})
MONGODB
Setup
Download: https://www.mongodb.com/try/download/community
Cái chúng ta tải về và sử dụng được gọi là MongoDB Server( Để tránh nhầm lẫn với Atlas )
Sau khi tải về và giải nén, để thuận tiện:
1. Ta bỏ thư mục mongodb vào User/trong
2. Tạo thư mục mới tên mongodb-data
3. Gõ lệnh này để kết nối thư mục mongodb-data mới tạo
❯ /Users/trong/mongodb/bin/mongod --dbpath=/Users/trong/mongodb-data
Nếu bạn nhìn vào terminal, sẽ thấy 2 dòng này:
{"t":{"$date":"2022-02-17T08:51:44.249+07:00"},"s":"I", "c":"NETWORK", "id":23015,
"ctx":"listener","msg":"Listening on","attr":{"address":"127.0.0.1"}}
{"t":{"$date":"2022-02-17T08:51:44.249+07:00"},"s":"I", "c":"NETWORK", "id":23016,
"ctx":"listener","msg":"Waiting for connections","attr":{"port":27017,"ssl":"off"}}
à Nghĩa là mongoDB đang lắng nghe ở 127.0.0.1:27017
Connect to IDE
1. Download Robo 3T
2. Setting Connection
3. Để kiểm tra, ta thử gõ db.version()
Giống như JBDC Driver, bộ kit sử dụng để lập trình MongoDB được gọi là MongoDB Driver.
Document
https://docs.mongodb.com/drivers/
Offical Driver
https://www.npmjs.com/package/mongodb
Connect to MongoDB
Có 2 cách kết nối tới MongoDB (nhớ khởi động Mongo Server trước thông qua lệnh )
❯ /Users/trong/mongodb/bin/mongod --dbpath=/Users/trong/mongodb-data
Cách 1
import { MongoClient } from 'mongodb'
const connectionURL = 'mongodb://127.0.0.1:27017';
const databaseName = 'task-app';
const client = new MongoClient(connectionURL);
client.connect().then((client) => {
console.log(chalk.bgGreen.black(` SUCCESS CONNECTED TO MONGODB !!! `));
const database = client.db(databaseName);
}).catch((error)=>{
console.log(chalk.red(`Cannot connect to MongoDB: ${error}`));
});
import { MongoClient } from 'mongodb'
const connectionURL = 'mongodb://127.0.0.1:27017';
const databaseName = 'task-app';
MongoClient.connect(connectionURL, {useNewUrlParser: true}, (error, client) => {
Cách 2
if(error){
console.log(chalk.red(`Cannot connect to MongoDB: ${error}`));
} else {
console.log(chalk.bgGreen.black(' SUCCESS CONNECTED TO MONGODB '));
const database = client.db(databaseName);
}
});
MongoDB basic
ObjectID
Giống như document id, nó được tạo ra tự động bởi MongoDB cho mỗi document bạn tạo ra. Tuy nhiên nên lưu ý là khi
bạn xem document đó trên một IDE bất kỳ, chúng sẽ luôn hiện
điều này
nghĩa là giá trị thật của _id là kết quả trả về của ObjectId trả về, nó sẽ không hiện kiểu String để ta nhìn thấy hết, và giá
trị 620e45705f9eff8e218c92b7 cũng không phải giá trị thực sự của _id.
Chúng ta có thể tạo ObjectID theo ý mình:
client.connect().then((client) => {
console.log(chalk.bgGreen.black(` SUCCESS CONNECTED TO MONGODB !!! `));
const database = client.db(databaseName);
database.collection('tasks').insertMany([{
_id: 1,
description: 'Clean the house',
complete: true,
},
{
_id: 2,
name: 'Fix light',
complete: false,
},
{
_id: 3,
name: 'Debug',
complete: false,
}]
});
CRUD
C
insertOne
Ở ví dụ bên dưới, ta sẽ insert một document mới vào collection users
import {MongoClient} from 'mongodb'
const connectionURL = 'mongodb://127.0.0.1:27017';
const databaseName = 'task-app';
const client = new MongoClient(connectionURL);
client.connect().then((client) => {
console.log(chalk.bgGreen.black(` SUCCESS CONNECTED TO MONGODB !!! `));
const database = client.db(databaseName);
database.collection('users').insertOne({
name: 'Trong',
age: 22,
}
);
}).catch((error) => {
console.log(chalk.red(`Cannot connect to MongoDB: ${error}`));
});
db.getCollection('users').find({});
insertMany
client.connect().then((client) => {
console.log(chalk.bgGreen.black(` SUCCESS CONNECTED TO MONGODB !!! `));
const database = client.db(databaseName);
database.collection('users').insertMany([
{
name: 'Trong',
age: 22,
},
{
name: 'Cute',
age: 22,
}
], (error, result) => {
if (error) {
console.log(chalk.red(`Cannot Insert`));
} else {
console.log(chalk.bgGreen.black(' SUCCESS INSERT '));
console.log(JSON.stringify(result));
}
});
}).catch((error) => {
console.log(chalk.red(`Cannot connect to MongoDB: ${error}`));
});
R
findOne
database.collection('users').findOne({
name: 'Trong'
}, (error, result) => {
if (error) {
console.log(chalk.red(`Unable To Find`));
} else {
if(result == null){
console.log(chalk.bgGreen.black(' FOUND NOTHING !!! '));
} else {
console.log(chalk.bgGreen.black(` FOUND ${JSON.stringify(result)}`));
}
}
});
database.collection('users').findOne({
name: 'Trong',
age: 30
}, (error, result) => {
if (error) {
console.log(chalk.red(`Unable To Find`));
} else {
if(result == null){
console.log(chalk.bgGreen.black(' FOUND NOTHING !!! '));
} else {
console.log(chalk.bgGreen.black(` FOUND ${JSON.stringify(result)}`));
}
}
});
Lưu ý, để tìm theo _id, bạn cần tìm ObjectId( _id )
database.collection('users').findOne({
_id: new ObjectId("620e45705f9eff8e218c92b7"),
}, (error, result) => {
if (error) {
console.log(chalk.red(`Unable To Find`));
} else {
if(result == null){
console.log(chalk.bgGreen.black(' FOUND NOTHING !!! '));
} else {
console.log(chalk.bgGreen.black(` FOUND ${JSON.stringify(result)}`));
}
}
});
find
Như mọi method để query khác, khi kết quả trả về có nhiều kết quả, nó sẽ trả về Cursor
Bạn có thể dùng toArray( ) để danh sách kết quả
const resultList = database.collection('users').find({
name: 'Trong'
});
resultList.toArray((error, results)=>{
if (error) {
console.log(chalk.red(`Unable To Find`));
} else {
if (results == null) {
console.log(chalk.bgGreen.black(' FOUND NOTHING !!! '));
} else {
console.log(chalk.bgGreen.black(` FOUND ${JSON.stringify(results)}`));
console.log(chalk.bgGreen.black(` FOUND ${JSON.stringify(results[0])}`));
}
}
});
FOUND [
{"_id":"620e45705f9eff8e218c92b7","name":"Trong","age":22},
{"_id":"620e498b1379e27bbe251b81","name":"Trong","age":22},
{"_id":"620e498b1379e27bbe251b82","name":"Trong","age":23},
{"_id":"620e498b1379e27bbe251b83","name":"Trong","age":24}
]
FOUND {"_id":"620e45705f9eff8e218c92b7","name":"Trong","age":22}
U
Ở phần Update trong MongoDB, nó dùng 1 ký tự rất kỳ cmn lạ là $set, xem thêm các cách update khác tại đây:
https://docs.mongodb.com/manual/reference/operator/update/
updateOne
const updateResult = database.collection('tasks').updateOne(
{ _id: 1 },
{ $set: { complete: false }
});
updateResult.then((result)=>{
console.log(chalk.green('UPDATE SUCCESS documents =>'), result);
}).catch((error)=>{
console.log(chalk.red('UPDATE FAIL =>'), error);
});
à
$inc
à
database.collection('users').updateOne(
{_id: new ObjectId('620e498b1379e27bbe251b83')},
{
$inc: {age: 1}
}
);
updateMany
Update Many sẽ khác với Insert Many, nếu với Insert Many ta tạo từng Document, thì Update Many nghĩa là update
một lần cho tất cả Documents thảo mãn filter
à
database.collection('tasks').updateMany(
{complete: false},
{
$set: {complete: true}
}
);
D
deleteOne
Nếu chúng ta có > 2 Document cùng thoả mãn filter, nó sẽ delete cái đầu tiên
database.collection('tasks').deleteOne(
{name: 'Fix light'}).then((result) => {
console.log(chalk.green('DELETE SUCCESS documents =>'), result);
}).catch((error) => {
console.log(chalk.red('DELETE FAIL =>'), error);
});
deleteMany
à
database.collection('users').deleteMany(
{age: 25}).then((result) => {
console.log(chalk.green('DELETE SUCCESS documents =>'), result);
}).catch((error) => {
console.log(chalk.red('DELETE FAIL =>'), error);
});
MONGOOSE
https://www.npmjs.com/package/mongoose
Mongoose provides a straight-forward, schema-based solution to model your application data. It includes built-in type
casting, validation, query building, business logic hooks and more, out of the box.
If your app uses only one database, use mongoose.connect
await mongoose.connect(`${connectionURL}/${databaseName}`);
If you need to create additional connections, use mongoose.createConnection
const conn = mongoose.createConnection('your connection string');
ODM
Create Model
Nếu ORM là Object Relation Mapping thì ODM là Object Document Mapping
await mongoose.connect(mongodb:127.0.0.1:27017/task-app)
const MyModel = mongoose.model('ModelName', schema);
Một điều hay là ModelName bạn có thể đặt số ít hoặc số nhiều, chữ hoa hay không hoa, Mongoose cũng sẽ tự hiểu.
Ví dụ:
mongoose.model('Task', schema);
mongoose.model('Tasks', schema);
mongoose.model('tasks', schema);
mongoose.model('task', schema);
mongoose.model('tAsK', schema);
import mongoose from 'mongoose';
import validator, * as otherValidator from 'validator';
import chalk from 'chalk';
const connectionURL = 'mongodb://127.0.0.1:27017';
const databaseName = 'task-app';
await mongoose.connect(`${connectionURL}/${databaseName}`, {useNewUrlParser: true});
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
trim: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Not a right email address');
}
}
},
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Cannot set password to "password"');
}
}
},
age: {
type: Number,
default: 0,
validate(value) {
if (value < 0) {
throw new Error('Người âm phủ ???');
}
}
},
});
export const User = mongoose.model('users', UserSchema);
save( )
Sau khi bạn tạo ra được Object từ Document, thì bạn có thể save( ) instance của Object đó, lúc này document mới cũng
được thêm vào.
const UserSchema = new mongoose.Schema({
name: String,
age: Number,
});
const User = mongoose.model('User', UserSchema);
const userInstance = new User({name: 'Trong', age: 22});
userInstance
.save()
.then(() => {console.log(chalk.green(`Document Added: ${userInstance}`)); })
.catch((err) => {console.log(chalk.red(err)); });
Lưu ý 1: Nếu instance được tạo ra bị sai Type, nó sẽ báo lỗi
const userInstance = new User({name: 'Trong', age: 'old'});
userInstance
.save()
.then(() => {console.log(chalk.green(`Document Added: ${userInstance}`)); })
.catch((err) => {console.log(chalk.red(err)); });
à ValidationError: age: Cast to Number failed for value "old" (type string) at path "age"
Lưu ý 2: Một điều hay của NoSQL là nó không có 1 fixed-structure
Ví dụ: Ta tạo thêm 1 field tên là somethingElse và add vào Collection vẫn không sao cả, không nhất thiết tất cả
documents trong 1 collection phải có chung 1 strucuture như Relational
const TaskSchema = new mongoose.Schema({
name: String,
complete: Boolean,
somethingElse: String,
});
const Task = mongoose.model('task', TaskSchema);
const myTask = new Task({name: 'Learn NodeJS', complete: false, somethingElse: 'hehe'});
SchemaType Options
https://mongoosejs.com/docs/schematypes.html
Với mỗi SchemaType bạn lại có thêm nhiều options để áp dụng lên type đó
Ví dụ:
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
trim: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Not a right email address');
}
}
},
age: {
type: Number,
default: 0,
validate(value) {
if (value < 0) {
throw new Error('Người âm phủ ???');
}
}
},
});
String
•
lowercase: boolean, whether to always call .toLowerCase() on the value
•
uppercase: boolean, whether to always call .toUpperCase() on the value
•
trim: boolean, whether to always call .trim() on the value
•
match: RegExp, creates a validator that checks if the value matches the given regular expression
•
enum: Array, creates a validator that checks if the value is in the given array.
•
minLength: Number, creates a validator that checks if the value length is not less than the given number
•
maxLength: Number, creates a validator that checks if the value length is not greater than the given number
•
populate: Object, sets default populate options
Number
•
min: Number, creates a validator that checks if the value is greater than or equal to the given minimum.
•
max: Number, creates a validator that checks if the value is less than or equal to the given maximum.
•
enum: Array, creates a validator that checks if the value is strictly equal to one of the values in the given array.
•
populate: Object, sets default populate options
Date
•
min: Date
•
max: Date
ObjectId
•
populate: Object, sets default populate options
All Schema Types
•
required: boolean or function, if true adds a required validator for this property
•
default: Any or function, sets a default value for the path. If the value is a function, the return value of the function is
used as the default.
•
select: boolean, specifies default projections for queries
•
validate: function, adds a validator function for this property
•
get: function, defines a custom getter for this property using Object.defineProperty().
•
set: function, defines a custom setter for this property using Object.defineProperty().
•
alias: string, mongoose >= 4.10.0 only. Defines a virtual with the given name that gets/sets this path.
•
immutable: boolean, defines path as immutable. Mongoose prevents you from changing immutable paths unless the
parent document has isNew: true.
•
transform: function, Mongoose calls this function when you call Document#toJSON() function, including when
you JSON.stringify() a document.
const numberSchema = new Schema({
integerOnly: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
alias: 'i'
}
});
const Number = mongoose.model('Number', numberSchema);
const doc = new Number();
doc.integerOnly = 2.001;
doc.integerOnly;
à 2
doc.i;
à 2
doc.i = 3.001;
doc.integerOnly;
à 3
doc.i;
à 3
Indexes
You can also define MongoDB indexes using schema type options.
•
index: boolean, whether to define an index on this property.
•
unique: boolean, whether to define a unique index on this property.
•
sparse: boolean, whether to define a sparse index on this property.
const schema2 = new Schema({
test: {
type: String,
index: true,
unique: true // Unique index. If you specify `unique: true`
// specifying `index: true` is optional if you do `unique: true`
}
});
Data Validation
required
Ví dụ: Có thể thiếu complete nhưng không thể thiếu name vì đã required
const TaskSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
complete: Boolean,
});
const myTask = new Task({ complete: false});
à ValidationError: name: Path `name` is required.
validate( )
const UserSchema = new mongoose.Schema({
name: String,
age: {
type: Number,
validate(value){
if(value < 0){
throw new Error('Người âm phủ ???');
}
}
},
});
const userInstance = new User({name: 'Trong', age: -1});
à ValidationError: age: Người âm phủ ???
Ví dụ:
Ta muốn set field password với các điều kiện sau đây:
• Không được để trống
•
>= 6 ký tự
•
Không được bao gồm ‘password’
const UserSchema = new mongoose.Schema({
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value){
if (value.toLowerCase().includes('password')) {
throw new Error('Cannot set password to "password"');
}
}
},
});
Kêt hợp với validator.js
const UserSchema = new mongoose.Schema({
name: String,
email: {
type: String,
required: true,
validate(value){
if (!validator.isEmail(value)){
throw new Error('Not a right email address');
}
}
},
});
const User = mongoose.model('User', UserSchema);
const userInstance = new User({name: 'Trong', email: 'trong@'});
à ValidationError: email: Not a right email address
Middleware
Middleware (also called pre and post hooks) are functions which are passed control during execution of
asynchronous functions.
Define Middleware Before Compiling Models
Pre
Calling pre() trước khi compiling a model. The below script will print out "Hello from pre save":
next trong ví dụ dưới đây nhằm báo hiệu rằng bạn đã xong, tiếp theo là save, nếu không gọi next( ) thì chương trình sẽ
bị treo.
UserSchema.pre('save', async (next)=>{
console.log('hello from pre save');
next();
})
Connect 2 collections
Đây là 2 field riêng lẻ, liệu ta sẽ tạo kết nối với chúng ntn?
ref
Tạo references đến Model khác
const UserSchema = new mongoose.Schema({
...
});
mongoose.model('users', UserSchema);
const TaskSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'users'
}
});
Ta có thể dùng populate( ) để lấy references object
const task = await Task.findById('62124a1e637f0ea64b432583');
await task.populate('owner');
console.log(task.owner);
à
{
_id: new ObjectId("621238f48095808e9ddb6450"),
name: 'Trong Cute 123',
email: 'trongcute@gmail.com',
password: '$2b$08$Dq/BB5COJEl2UPi3j1jU7O4JZXBGpE4wwOLBvstXoeCwsgZeJ1th.',
age: 22,
}
Một cách nữa để liên kết với model khác nhưng không cần phải tạo field của Model đó trong Schema
virtual
virtual có nghĩa là ta đang muốn tạo 1 field có ý nghĩa logic nhưng không muốn nó xuất hiện trong mongoDB, virtual
này do Mongoose nghĩ ra, không phải của MongoDB
const TaskSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'users'
}
});
const UserSchema = new mongoose.Schema({
});
UserSchema.virtual('tasksOfUser', {
ref: 'tasks',
localField: '_id',
foreignField: 'owner'
});
Ở ví dụ trên ta đang muốn nói rằng ta muốn tạo 1 attribute virutal tên là tasksOfUser, field này kết nối localField của
UserSchema ( là User._id ) đến foreignField của tasks ( là Task.owner )
Cách này có tác dụng lên cả One-Many, nó có thể kết nối 1 User – Many Task
const user = await User.findById('621238f48095808e9ddb6450');
await user.populate('tasksOfUser');
console.log(user.tasksOfUser);
à
[
{
_id: new ObjectId("62124a1e637f0ea64b432583"),
name: 'Fix Bug',
complete: false,
owner: new ObjectId("621238f48095808e9ddb6450"),
__v: 0
},
{
_id: new ObjectId("6212ab51b73f24f0a1e7df2a"),
name: 'Finish backend',
complete: false,
owner: new ObjectId("621238f48095808e9ddb6450"),
__v: 0
}
]
Cascade Delete
UserSchema.pre('remove', async function (next) {
const user = this;
await Task.deleteMany({owner: user._id});
next();
});
router.delete('/users/:id', authMiddleware, async (request, response) => {
const _idParam = request.params.id;
try {
const deletedUser = await User.findOne({_id: new ObjectId(_idParam)});
if (deletedUser == null) {
response.status(404).send("User not found");
} else {
await deletedUser.remove();
response.status(200).send(`DELETED: ${deletedUser}`);
}
} catch (error) {
response.status(400).send(`${error}`);
}
});
REST API
Về cơ bản,
-
Client sẽ gửi các Request đến Server,
-
Dựa vào nội dung bên trong Request, Server sẽ gửi các Response tương ứng
Đối với từng lệnh CRUD sẽ tương ứng với các Url và Request Method khác nhau:
Vào trang này để xem ý nghĩa của các status code:
https://httpstatuses.com/
C
app.use( express.json( ) ) sẽ giúp request.body trở thành một Object, nếu không sẽ ra undefined.
import {MongoClient, ObjectId} from 'mongodb';
import {User} from './models/user.js';
import express from 'express';
const app = express();
const port = process.env.PORT || 3000;
/* _______________________________________________ CREATE ____________________________________ */
app.use(express.json());
app.post('/users',(request, response)=>{
const user = new User(request.body);
user
.save()
.then(result => {
response.status(201).send(`CREATE ${task} into MongoDB`);
})
.catch(error =>{
console.log(chalk.red(error));
});
});
app.listen(port, () => {
console.log(chalk.green(`Listening on port ${port}`));
})
à
Theo mặc định, response chúng ta send sẽ là
Bạn sẽ thấy Status 201 này trong response
nhờ vào dòng:
response.status(201).send(`CREATE ${task} into MongoDB`);
R
Read All
User.find( { } ) do mongoose cung cấp có chức năng y chang như
database.collection('users').find({})
import {MongoClient, ObjectId} from 'mongodb';
import {User} from './models/user.js';
import express from 'express';
import {Task} from "./models/task.js";
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.get('/users',(request, response)=>{
User.find({}).then((result)=>{
response.status(200).send(`FOUND ${result}`);
}).catch((error) => {
response.status(500).send(`${error}`);
});
});
FOUND
{
_id: new ObjectId("620e45705f9eff8e218c92b7"),
name: 'Trong',
age: 22
},
{
_id: new ObjectId("620e498b1379e27bbe251b81"),
name: 'Trong',
age: 22
},
{
_id: new ObjectId("620e498b1379e27bbe251b82"),
name: 'Trong',
age: 23
},
{
_id: new ObjectId("620f0f04adec7d06bf24d8bd"),
name: 'Trong',
age: 22,
__v: 0
},
{
_id: new ObjectId("620f50115ad943a58163ca60"),
name: 'Trong',
email: 'trong@gmail.com',
age: 22,
__v: 0
},
{
_id: new ObjectId("620fa37cda96293e589c7c64"),
name: 'Trong',
email: 'trong@gmail.com',
password: 'trongvip',
age: 22,
__v: 0
}
Read One
request.params
app.get('/users/:id',(request, response)=>{
console.log(request.params);
});
{
“id” : 3
}
Khi ta đặt :id trong url nghĩa là id là 1 param của url này
Tương tự:
request.params
{
/users/:id/:a/:b'
id: '1',
à
a: '2',
b: '3'
}
app.get('/users/:id', (request, response) => {
const _idParam = request.params.id;
User.findOne({_id : new ObjectId(_idParam)}).then((result) => {
if (result == null) {
response.status(404).send(`USER NOT FOUND`);
} else {
response.status(200).send(`FOUND ${result}`);
}
}).catch((error) => {
response.status(500).send(`${error}`);
});
});
FOUND {
_id: new ObjectId("620fa37cda96293e589c7c64"),
name: 'Trong',
email: 'trong@gmail.com',
password: 'trongvip',
age: 22,
__v: 0
}
U
Update One
findOneAndUpdate( filter, new object, options )
new Object sẽ có dạng:
{
attribute: new value,
attibute 2: new value
}
Nếu attribute nào không có trong Document cũ thì nó sẽ KHÔNG được auto thêm vào
app.patch('/users/:id', async (request, response) => {
const _idParam = request.params.id;
try {
const updatedUser = await User.findOneAndUpdate(
{_id: new ObjectId(_idParam)},
request.body,
{new: true, runVariables: true}
);
if (updatedUser == null) {
response.status(404).send("User not found");
} else {
response.send(`UPDATED: ${updatedUser}`);
}
} catch (error) {
response.status(400).send(`${error}`);
}
});
à
Exactly Fields
Bạn có thể bắt chỉ được update số field bạn mong muốn
app.patch('/users/:id', async (request, response) => {
const fieldsUpdated = Object.key(request.body);
const fieldAllowUpdate = ['name', 'email', 'password', 'age'];
const isValidField = fieldsUpdated.every(field => fieldAllowUpdate.includes(field));
if(!isValidField){
response.status(500).send("You must update exactly fields");
return;
}
const _idParam = request.params.id;
try {
const updatedUser = await User.findOneAndUpdate(
{_id: new ObjectId(_idParam)},
request.body,
{new: true, runVariables: true}
);
if (updatedUser == null) {
response.status(404).send("User not found");
} else {
response.send(`UPDATED: ${updatedUser}`);
}
} catch (error) {
response.status(400).send(`${error}`);
}
});
D
app.delete('/users/:id', async (request,response) => {
const _idParam = request.params.id;
try {
const deletedUser = await User.findOneAndDelete({_id: new ObjectId(_idParam)});
if (deletedUser == null) {
response.status(404).send("User not found");
} else {
response.status(200).send(`DELETED: ${deletedUser}`);
}
} catch (error) {
response.status(400).send(`${error}`);
}
});
Express Router
Nó không khác gì hết, nếu:
-
app = express( )
-
thì router = express.Router( )
Express Router là 1 built-in class để xây dụng các endpoints (URIs). The express router class helps in the creation of route
handlers.
const app = express();
app.use(express.json());
app.post('/tasks', async (request, response) => {
});
const app = express();
const router = new express.Router();
app.use(express.json());
app.use(router);
router.post('/tasks', async (request, response) => {
});
REST with Middleware
Ở đây ta dùng Middelware của Express, không phải của Mongooes
without Middleware:
New request à Run route handler
with Middleware:
New request à Do something à Run route handler
Bằng cách dùng Middleware, ta có thể ALLOW / DENIDED các request ta muốn, hoặc kiểm tra trong request có JWT hay
không, hoặc chặn tất cả request khi trang web đang bảo trì...
const app = express();
app.use((request, response, next)=>{
console.log(`REQUEST PATH: ${request.path}`);
console.log(`REQUEST METHOD: ${request.method}`);
next();
});
REQUEST PATH: /users/login
{
"email":"trongcute@gmail.com",
"password":"trongcute"
REQUEST METHOD: POST
}
Hoặc chặn tất cả request khi trang web đang bảo trì...
app.use((request, response, next)=>{
response.status(503).send('Website is under maintenance');
});
Put middleware between Request
Thông thường ta sẽ đặt middleware để kiểm tra Authentication trước khi một request được thực hiện
export const authMiddleware = async (request, response, next) => {
console.log('Auth middleware called');
next();
}
import {authMiddleware} from "../middleware/auth-middleware.js";
Run First
Run Second
router.get('/users', authMiddleware, async (request, response) => {
try {
const foundUsers = await User.find({});
response.status(200).send(`FOUND ${foundUsers}`);
} catch (error) {
response.status(500).send(`${error}`);
}
});
AUTHETICATION
Encrypting Password
https://www.npmjs.com/package/bcryptjs
const password = 'Trongvip123!';
const hashedPassword = await bcrypt.hash(password, 8);
console.log(password);
à Trongvip123!
console.log(hashedPassword);
à $2b$08$j05WdKlNgJ16OjTadJnC2u1M/fYKQccQ3EopuNdtDC8MbUv1pz7la
const isMatch = await bcrypt.compare('Trongvip123!', hashedPassword);
console.log(isMatch);
à true
Lưu ý: Phải viết syntax theo kiểu function, chứ không phải arrow function, thì this mới có giá trị là User
const UserSchema = new mongoose.Schema({
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Cannot set password to "password"');
}
}
}
});
UserSchema.pre('save', async function (next){
const user = this;
/*
* isModified means CREATE and UPDATE too
* */
if (user.isModified('password')){
user.password = await bcrypt.hash(user.password, 8);
}
next();
})
router.post('/users', async (request, response) => {
const user = new User(request.body);
try {
await user.save();
console.log(chalk.green(`CREATE ${user} into MongoDB`));
response.status(201).send(`CREATE ${user} into MongoDB`);
} catch (error) {
console.log(chalk.red(error));
}
});
à
Login
import
import
import
import
mongoose from 'mongoose';
bcrypt from 'bcrypt';
validator, * as otherValidator from 'validator';
chalk from 'chalk';
const connectionURL = 'mongodb://127.0.0.1:27017';
const databaseName = 'task-app';
await mongoose.connect(`${connectionURL}/${databaseName}`, {useNewUrlParser: true});
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
trim: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Not a right email address');
}
}
},
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Cannot set password to "password"');
}
}
},
});
UserSchema.pre('save', async function (next){
const user = this;
/*
* isModified means CREATE and UPDATE too
* */
if (user.isModified('password')){
user.password = await bcrypt.hash(user.password, 8);
}
next();
})
UserSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({email: email});
if(user == null){
throw new Error('Email or password not found');
}
const isMatch = await bcrypt.compare(password,user.password);
if(isMatch==false){
throw new Error('Email or password not found');
}
return user;
}
export const User = mongoose.model('users', UserSchema);
export const router = new express.Router();
/* ========================================== LOGIN ===========================================*/
router.post('/users/login', async (request, response) => {
try {
const foundUser = await User.findByCredentials(request.body.email, request.body.password);
response.send(foundUser);
} catch (error) {
response.status(400).send(`Something went wrong ${error}`);
}
})
à SUCCESSED
{
"email":"trongcute@gmail.com",
"password":"trongcute"
}
Create JWT for REST API
JWT = JSON WEBSERVER TOKEN
https://www.npmjs.com/package/jsonwebtoken
Basic
Như bạn thấy bên dưới, JWT mang thông tin được gọi là dataStoreInToken, nó được mã hoá bằng randomCharacters, từ đó
khi client cung cấp JWT, ta có thể kiểm tra dataStoreInToken là gì để đăng nhập, xử lý,...
•
jwt.sign( ) để tạo JWT
•
jwt.verify( ) để lấy data từ JWT
import jwt from "jsonwebtoken";
const dataStoreInToken = {
_id: '1234'
};
const randomCharacters = 'abcxyz';
const generatedToken = jwt.sign(dataStoreInToken, randomCharacters, {expiresIn: '7 days'});
console.log(generatedToken);
à eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJfaWQiOiIxMjM0IiwiaWF0IjoxNjQ1MzM3MTMyfQ
.PWqm_2RVgnEu4CWkJ-LChC4WzmCWG86BOWNnwxil70o
const resolvedData = jwt.verify(generatedToken, randomCharacters);
console.log(resolvedData);
Use JWT for Login
à { _id: '1234', iat: 1645337473, exp: 1645942628 }
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
trim: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Not a right email address');
}
}
},
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Cannot set password to "password"');
}
}
},
tokens: [{
token: {
type: String
}
}]
});
UserSchema.methods.generateAuthToken = async function () {
const user = this;
const dataStoreInToken = {
_id: user._id
};
const randomCharacters = 'web-enterprise';
const generatedAuthToken = jwt.sign(dataStoreInToken, randomCharacters, {expiresIn: '7 days'});
user.tokens = user.tokens.concat({token: generatedAuthToken});
await user.save();
return generatedAuthToken;
}
Ở ví dụ bên dưới, ta dùng request.user = user
để thêm user Object vào Request
export const authMiddleware = async (request, response, next) => {
console.log('Auth middleware called');
try {
const authTokenInHeader = request.header('Authorization').replace('Bearer ','');
const dataInAuthToken = jwt.verify(authTokenInHeader, 'web-enterprise');
const user = await User.findOne(
{
_id: dataInAuthToken._id,
'tokens.token': authTokenInHeader
});
if(user==null){
throw new Error();
}
request.user = user;
next();
} catch (error) {
response.status(401).send({error: "Authentication error"});
}
}
Lúc này request bạn gửi tới sẽ có thêm attribute là request.user mà Middleware đã thêm vào
router.get('/users/me', authMiddleware,async (request, response) => {
try {
response.status(200).send(`YOU ${request.user}`);
} catch (error) {
response.status(500).send(`${error}`);
}
});
router.post('/users', async (request, response) => {
const user = new User(request.body);
try {
await user.save();
const authToken = await user.generateAuthToken();
response.status(201).send({"user": user, "authToken": authToken});
} catch (error) {
console.log(chalk.red(error));
}
});
SORTING, PAGINATION, FILTERING DATA
Add timestamp
Trong Mongoose có hổ trợ thêm options khi define Schema, 1 trong những option đó là Timestamp
const UserSchema = new mongoose.Schema({
name: {
...
},
email: {
...
},
password: {
...
},
age: {
...
},
}, {
timestamps:true
});
Như bạn thấy, có 2 field được thêm vào là createdAt và updatedAt
{
"name": "Trong",
"email":"trong@gmail.com",
"password":"trongcute",
"age": 22
}
{
"user": {
"name": "Trong",
"email": "trong@gmail.com",
"password": "$2b$08$LuuWd3MhD4n17NJ.0ULe6uS1IapLAjERygGAo5rQ0SoQI8qvwv81m",
"age": 22,
"_id": "6212c7ac2d8ca91170a8f43c",
"sessionTokens": [],
"createdAt": "2022-02-20T22:58:52.852Z",
"updatedAt": "2022-02-20T22:58:52.852Z",
"__v": 0
}
}
Filtering Data
Ta muốn Client filtering Data mà họ muốn lấy thông qua URL, vì thế ta dùng request.query để phân tích data
router.get('/tasks', async (request, response) => {
const filter = {};
if (request.query.complete) {
filter.complete = (request.query.complete === 'true');
}
if (request.query.name) {
filter.name = request.query.name;
}
try {
const foundTasks = await Task.find(filter);
if (foundTasks == null) {
response.status(404).send(`TASKS NOT FOUND`);
} else {
response.status(200).send(`FOUND ${foundTasks}`);
}
} catch (error) {
response.status(500).send(`${error}`);
}
});
{
_id: new ObjectId("6212e34f0382897c385cee57"),
name: 'first',
complete: false,
owner: new ObjectId("6212c7ac2d8ca91170a8f43c"),
createdAt: 2022-02-21T00: 56: 47.702Z,
updatedAt: 2022-02-21T00: 56: 47.702Z,
__v: 0
}
Pagination
URL pattern để pagination là:
/tasks?limit=<số item trong 1 trang> & skip=<số trang đang đứng hiện tại, bắt đầu từ 0>
router.get('/tasks', async (request, response) => {
const filter = {}
let skip;
let limit;
if (request.query.complete) {
filter.complete = (request.query.complete === 'true');
}
if (request.query.name) {
filter.name = request.query.name;
}
if (request.query.skip) {
skip = parseInt(request.query.skip);
}
if (request.query.limit) {
limit = parseInt(request.query.limit);
}
try {
const foundTasks = await Task.find(filter)
.skip(skip)
.limit(limit);
if (foundTasks == null) {
response.status(404).send(`TASKS NOT FOUND`);
} else {
response.status(200).send(`FOUND ${foundTasks}`);
}
} catch (error) {
response.status(500).send(`${error}`);
}
});
{
_id: new ObjectId("6212c98973b55cac6ff3daff"),
name: 'Finish BE',
complete: false,
owner: new ObjectId("6212c7ac2d8ca91170a8f43c"),
createdAt: 2022-02-20T23: 06: 49.509Z,
updatedAt: 2022-02-20T23: 06: 49.509Z,
__v: 0
},
{
_id: new ObjectId("6212e34f0382897c385cee57"),
name: 'first',
complete: false,
owner: new ObjectId("6212c7ac2d8ca91170a8f43c"),
createdAt: 2022-02-21T00: 56: 47.702Z,
updatedAt: 2022-02-21T00: 56: 47.702Z,
__v: 0
}
Sorting
Trong hàm sort:
• -1 là DESC
• 1 là ASC
router.get('/tasks', async (request, response) => {
try {
const foundTasks = await Task.find(filter)
.sort({createdAt: -1});
if (foundTasks == null) {
response.status(404).send(`TASKS NOT FOUND`);
} else {
response.status(200).send(`FOUND ${foundTasks}`);
}
} catch (error) {
response.status(500).send(`${error}`);
}
});
Từ đó, Client khi muốn sort phải gửi URL có cả phần muốn Sort và sort theo DESC hay ASC, nên ta có:
/tasks?sortBy=complete_desc à { complete: -1 }
/tasks?sortBy=complete_asc à { complete: 1 }
router.get('/tasks', async (request, response) => {
const sort = {}
if (request.query.sortBy) {
const parts = request.query.sortBy.split('_');
if(parts[1] === 'desc' ){
sort[parts[0]] = -1;
} else {
sort[parts[0]] = 1;
}
}
try {
const foundTasks = await Task.find(filter)
.sort(sort);
if (foundTasks == null) {
response.status(404).send(`TASKS NOT FOUND`);
} else {
response.status(200).send(`FOUND ${foundTasks}`);
}
} catch (error) {
response.status(500).send(`${error}`);
}
});
FILE UPLOAD
VALIDATOR.JS
https://www.npmjs.com/package/validator
import * as validator from 'validator';
isEmail( )
console.log(validator.isEmail('foo@bar.com'));
true
console.log(validator.isEmail('foo@.com'));
false
USEFUL LIBRARIES
chalk.js
https://www.npmjs.com/package/chalk
import chalk from 'chalk';
console.log(chalk.blue('Hello') + ' World' + chalk.red('!'));
console.log(chalk.yellow.bgRed('Warning'));
console.log(chalk.whiteBright.bgGreen('Success'));
nodemon.js
https://www.npmjs.com/package/nodemon
Thư viện giúp NodeJS tự restarting khi có file thay đổi.
Như bạn thấy, Node Application luôn running và bất kỳ thay đổi gì nó cũng hiện trên terminal
DEPLOY APPLICATION
COMMON ERROR
problem
process.dlopen(module, path.toNamespacedPath(filename))
solve
npm rebuild bcrypt --build-from-source
Download