父子节点 构建
In this article we will be building an eCommerce platform with Node.js as backend and for frontend, we will have 3 different technologies (Angular, React and Vue.js). I just have completed frontend part with Vue Vite, you can read. Angular and React will be soon launched.
在本文中,我们将使用Node.js作为后端构建一个电子商务平台,对于前端,我们将拥有3种不同的技术(Angular,React和Vue.js)。 我刚刚完成了Vue Vite的前端部分,您可以阅读 。 Angular和React将很快发布。
We will break down this article into two parts, the backend and the frontend. Our application will have basic features like adding of product and adding product to cart.
我们将把本文分为两个部分,后端和前端。 我们的应用程序将具有基本功能,例如添加产品和将产品添加到购物车。
Prerequisites
先决条件
- Familiarity with HTML, CSS, and JavaScript (ES6+). 熟悉HTML,CSS和JavaScript(ES6 +)。
- Vs code or any code editor installed on your development machine. vs代码或开发计算机上安装的任何代码编辑器。
- Postman installed on your development machine. 在您的开发机器上安装了Postman。
- Basic knowledge of React and Express.js. React和Express.js的基础知识。
We will start by setting up the backend for our application. Let’s create a new directory for our application and initialize a new Node.js application. Open up your terminal and type the following:
我们将从为应用程序设置后端开始。 让我们为应用程序创建一个新目录,并初始化一个新的Node.js应用程序。 打开您的终端并输入以下内容:
cd desktop
mkdir reactcart && cd reactcart
npm init -y
code .
安装必要的软件包 (Installing the necessary packages)
We will have to install some packages for our application:
我们将必须为我们的应用程序安装一些软件包:
body-parser
: is a piece of express middleware that reads a form’s input and stores it as a javascript object accessible through req.body.body-parser
:是一种表达中间件,它读取表单的输入并将其存储为可通过req.body访问的javascript对象。nodemon
: will watch our files for any changes and then restarts the server when any change occurs.nodemon
:将监视我们的文件是否有任何更改,然后在发生任何更改时重新启动服务器。express
will be used to build our Node.js server.express
将用于构建我们的Node.js服务器。cors
: is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin.cors
:是一种机制,它使用附加的HTTP标cors
告诉浏览器,使运行在一个来源的Web应用程序可以访问来自另一个来源的选定资源。dotenv
: will store all of our environment variables. This is where we will store our email variables.dotenv
:将存储我们所有的环境变量。 我们将在这里存储我们的电子邮件变量。morgan
: is a package that will log all our application routes.morgan
:是一个软件包,它将记录我们的所有应用程序路由。mongoose
: an object modeling tool used to asynchronous query MongoDB.mongoose
:用于异步查询MongoDB的对象建模工具。multer
: is a Node.js middleware for handlingmultipart/form-data
, which is primarily used for uploading files.multer
:是用于处理multipart/form-data
的Node.js中间件,主要用于上传文件。
To install this packages open your terminal and type:
要安装此软件包,请打开终端并输入:
npm i express mongoose morgan dotenv multer body-parser cors nodemon --save
Running this command will create a node_modules
folder. You have to create a .gitignore
file and add the node_modules
file inside it.
运行此命令将创建一个node_modules
文件夹。 您必须创建一个.gitignore
文件,并在其中添加node_modules
文件。
设置服务器 (Setting up the server)
We will continue by creating an src/index.js
file and add the following lines of code:
我们将继续创建一个src/index.js
文件,并添加以下代码行:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const app = express();
app.use(morgan('dev'));
app.use(cors());
app.use(bodyParser.json())
app.get('/', (req, res) => {
res.json({
message: 'Arise MERN developers'
});
});
const port = process.env.PORT || 4000;
app.listen(port, () => {
console.log(`Application is running on ${port}`);
});
After adding this, We can run our application using nodemon by typing nodemon src
in our terminal. Running this will output Application is running on 4000
.
添加完之后,我们可以通过在终端中键入nodemon src
来使用nodemon运行应用程序。 运行此命令将输出Application is running on 4000
。
Now that our server is running, We have to setup our mongoDB server. To do this create a new directory src/config
and create a mongoose.js
file and add the following codes:
现在我们的服务器正在运行,我们必须设置我们的mongoDB服务器。 为此,请创建一个新目录src/config
并创建一个mongoose.js
文件并添加以下代码:
const mongoose = require("mongoose");
module.exports = app => {
mongoose.connect('mongodb://localhost:27017/cart', {
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: false
}).then(res => console.log("connected")).catch(err => console.log(err))
mongoose.Promise = global.Promise;
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
process.on("SIGHUP", cleanup);
if (app) {
app.set("mongoose", mongoose);
}
};
function cleanup() {
mongoose.connection.close(function () {
process.exit(0);
});
}
Now we need to register this config in our index.js
file:
现在我们需要在我们的index.js
文件中注册此配置:
require("./config/mongoose.js")(app);
Adding this will connect to our database when ever our Node.js server is running.
每当我们的Node.js服务器运行时,添加该文件将连接到我们的数据库。
Note that you have to declare this after you have declared the instance of express.
请注意,在声明了express实例之后,必须声明它。
We have to now create our mongoDB models and routes for out products and cart.
现在,我们必须为产品和购物车创建mongoDB模型和路线。
Create an src/app
directory, This is where we will be creating our modules. Inside this directory, Create a product directory and add the following file:
创建一个src/app
目录,这是我们将创建模块的位置。 在此目录中,创建产品目录并添加以下文件:
- model.js model.js
- controller.js controller.js
- repository.js repository.js
- route.js route.js
It’s also a good idea to take all DB communications to the repository file.
将所有数据库通信都移至存储库文件也是一个好主意。
Lets define our product model by adding this to our model.js file:
让我们通过将其添加到我们的model.js文件中来定义我们的产品模型:
const mongoose = require("mongoose");
const productSchema = mongoose.Schema({
name: {
type: String,
required: [true, "Please include the product name"],
},
price: {
type: String,
required: [true, "Please include the product price"],
},
image: {
type: String,
required: true,
},
});
const Product = mongoose.model("Product", productSchema);
module.exports = Product;
Our product model will be basic as possible as it holds the product name, price and image.
我们的产品型号将尽可能基本,因为它包含产品名称,价格和图像。
We now need to define our DB requests in our repository.js file:
现在,我们需要在repository.js文件中定义数据库请求:
const Product = require("./model");
exports.products = async () => {
const products = await Product.find();
return products;
};
exports.productById = async id => {
const product = await Product.findById(id);
return product;
}
exports.createProduct = async payload => {
const newProduct = await Product.create(payload);
return newProduct
}
exports.removeProduct = async id => {
const product = await Product.findByIdAndRemove(id);
return product
}
We need to define our basic routes to get all products, get single product details, remove product and create product. The logic is the routes will be talking to our controllers and the controller talks to the repository and the repository talks to our model.
我们需要定义基本路线以获取所有产品,获取单个产品详细信息,删除产品并创建产品。 逻辑是,路由将与我们的控制器进行通信,而控制器将与存储库进行通信,存储库与我们的模型进行通信。
Before we define our routes we need to configure multer for our image upload. Create a multer.js file and add the following code:
在定义路线之前,我们需要为图像上传配置multer。 创建一个multer.js文件并添加以下代码:
const multer = require("multer");
const path = require("path");
//image upload
const storage = multer.diskStorage({
destination: (req, res, cb) => {
cb(null, path.join("./files/"));
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + file.originalname);
}
});
// checking file type
const fileFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cb(new Error('Not an image! Please upload an image.', 400), false);
}
};
exports.upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 6
},
fileFilter: fileFilter
});
create a files
directory in the root of your application. This is where all uploaded images will be stored.
在应用程序的根目录中创建一个files
目录。 这是所有上传图像的存储位置。
Since all images goes to the files directory, We have to make that files
folder. To do this head over to the index.js file and add this:
由于所有图像都进入文件目录,因此我们必须将其放置在files
夹中。 为此,请转到index.js文件并添加以下内容:
app.use('/files', express.static("files"));
With this done, we can now serve images store in the files directory.
完成此操作后,我们现在可以将图像存储在files目录中。
Add this to the routes.js file:
将此添加到routes.js文件:
const router = require("express").Router();
const productController = require("./controller");
const multerInstance = require('../../config/multer')
router.post("/", multerInstance.upload.single('image'), productController.createProduct);
router.get("/", productController.getProducts);
router.get("/:id", productController.getProductById);
router.delete("/:id", productController.removeProduct);
module.exports = router;
We now have to define the methods for this routes. To do that create add this to the controller.js file:
现在,我们必须为此路线定义方法。 为此,将其添加到controller.js文件:
const productRepository = require('./repository')
exports.createProduct = async (req, res) => {
try {
let payload = {
name: req.body.name,
price: req.body.price,
image: req.file.path
}
let product = await productRepository.createProduct({
...payload
});
res.status(200).json({
status: true,
data: product,
})
} catch (err) {
console.log(err)
res.status(500).json({
error: err,
status: false,
})
}
}
exports.getProducts = async (req, res) => {
try {
let products = await productRepository.products();
res.status(200).json({
status: true,
data: products,
})
} catch (err) {
console.log(err)
res.status(500).json({
error: err,
status: false,
})
}
}exports.getProductById = async (req, res) => {
try {
let id = req.params.id
let productDetails = await productRepository.productById(id);
res.status(200).json({
status: true,
data: productDetails,
})
} catch (err) {
res.status(500).json({
status: false,
error: err
})
}
}
exports.removeProduct = async (req, res) => {
try {
let id = req.params.id
let productDetails = await productRepository.removeProduct(id)
res.status(200).json({
status: true,
data: productDetails,
})
} catch (err) {
res.status(500).json({
status: false,
error: err
})
}
}
Create a routerHandler.js file inside the src
directory, This will be our global routes handler:
在src
目录中创建一个routerHandler.js文件,这将是我们的全局路由处理程序:
const productRoutes = require("./Product/routes")
module.exports = app => {
app.use("/product", productRoutes);
}
Then register it in the index.js
file. Make sure to register this file after the mongoose instance.
然后将其注册到index.js
文件中。 确保在猫鼬实例之后注册该文件。
require('./app/routeHandler')(app)
测试我们的路线 (Testing our routes)
Getting all products
获取所有产品
Creating a post
建立讯息
Get product by id
按编号获取产品
Remove product
移除产品
We can now start working on our cart features. Create a new directory Cart
inside the src/app
directory. Just like we did for the Products module we will define the model, routes, repostory and controller files.
现在,我们可以开始使用购物车功能。 在src/app
目录中创建一个新目录Cart
。 就像我们对“产品”模块所做的一样,我们将定义模型,路线,重新发布和控制器文件。
Lets start by defining our cart models:
让我们从定义购物车模型开始:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ItemSchema = new Schema({
productId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
},
quantity: {
type: Number,
required: true,
min: [1, 'Quantity can not be less then 1.']
},
price: {
type: Number,
required: true
},
total: {
type: Number,
required: true,
}
}, {
timestamps: true
})
const CartSchema = new Schema({
items: [ItemSchema],
subTotal: {
default: 0,
type: Number
}
}, {
timestamps: true
})
module.exports = mongoose.model('cart', CartSchema);
Here we create our first schema to hold the instance of our current product and the create the second file which will hold the array of items in our cart.
在这里,我们创建第一个架构来保存我们当前产品的实例,并创建第二个文件来保存购物车中的商品数组。
Now we have to define our repository.js file:
现在,我们必须定义我们的repository.js文件:
const Cart = require("./model");
exports.cart = async () => {
const carts = await Cart.find().populate({
path: "items.productId",
select: "name price total"
});;
return carts[0];
};
exports.addItem = async payload => {
const newItem = await Cart.create(payload);
return newItem
}
Basically we write two methods that will get all cart items in our database and add an item to the cart model.
基本上,我们编写了两种方法来获取数据库中所有购物车商品并将商品添加到购物车模型。
We can now create our controllers for our cart, We will have 3 controllers:
现在我们可以为购物车创建控制器,我们将有3个控制器:
- Get all cart items 获取所有购物车物品
- Add product items to cart 将产品添加到购物车
- Empty cart 空购物车
const cartRepository = require('./repository')
const productRepository = require('../Product/repository'); exports.addItemToCart = async (req, res) => {
const {
productId
} = req.body;
const quantity = Number.parseInt(req.body.quantity);
try {
let cart = await cartRepository.cart();
let productDetails = await productRepository.productById(productId);
if (!productDetails) {
return res.status(500).json({
type: "Not Found",
msg: "Invalid request"
})
}
//--If cart exists ----
if (cart) {
//---- Check if index exists ----
const indexFound = cart.items.findIndex(item => item.productId.id == productId);
//------This removes an item from the the cart if the quantity is set to zero, We can use this method to remove an item from the list -------
if (indexFound !== -1 && quantity <= 0) {
cart.items.splice(indexFound, 1);
if (cart.items.length == 0) {
cart.subTotal = 0;
} else {
cart.subTotal = cart.items.map(item => item.total).reduce((acc, next) => acc + next);
}
}
//----------Check if product exist, just add the previous quantity with the new quantity and update the total price-------
else if (indexFound !== -1) {
cart.items[indexFound].quantity = cart.items[indexFound].quantity + quantity;
cart.items[indexFound].total = cart.items[indexFound].quantity * productDetails.price;
cart.items[indexFound].price = productDetails.price
cart.subTotal = cart.items.map(item => item.total).reduce((acc, next) => acc + next);
}
//----Check if quantity is greater than 0 then add item to items array ----
else if (quantity > 0) {
cart.items.push({
productId: productId,
quantity: quantity,
price: productDetails.price,
total: parseInt(productDetails.price * quantity)
})
cart.subTotal = cart.items.map(item => item.total).reduce((acc, next) => acc + next);
}
//----If quantity of price is 0 throw the error -------
else {
return res.status(400).json({
type: "Invalid",
msg: "Invalid request"
})
}
let data = await cart.save();
res.status(200).json({
type: "success",
mgs: "Process Successful",
data: data
})
}
//------------ This creates a new cart and then adds the item to the cart that has been created------------
else {
const cartData = {
items: [{
productId: productId,
quantity: quantity,
total: parseInt(productDetails.price * quantity),
price: productDetails.price
}],
subTotal: parseInt(productDetails.price * quantity)
}
cart = await cartRepository.addItem(cartData)
// let data = await cart.save();
res.json(cart);
}
} catch (err) {
console.log(err)
res.status(400).json({
type: "Invalid",
msg: "Something Went Wrong",
err: err
})
}
}
exports.getCart = async (req, res) => {
try {
let cart = await cartRepository.cart()
if (!cart) {
return res.status(400).json({
type: "Invalid",
msg: "Cart not found",
})
}
res.status(200).json({
status: true,
data: cart
})
} catch (err) {
console.log(err)
res.status(400).json({
type: "Invalid",
msg: "Something went wrong",
err: err
})
}
} exports.emptyCart = async (req, res) => {
try {
let cart = await cartRepository.cart();
cart.items = [];
cart.subTotal = 0
let data = await cart.save();
res.status(200).json({
type: "Success",
mgs: "Cart has been emptied",
data: data
})
} catch (err) {
console.log(err)
res.status(400).json({
type: "Invalid",
msg: "Something went wrong",
err: err
})
}
}
The code snippet has been commented for ease and better understanding.
已对代码段进行了注释,以简化和更好地理解。
We can now define our module routes and then define the global routes. Add this to the routes.js file:
现在,我们可以定义模块路由,然后定义全局路由。 将此添加到routes.js文件:
const router = require("express").Router();
const cartController = require("./controller");
router.post("/", cartController.addItemToCart);
router.get("/", cartController.getCart);
router.delete("/empty-cart", cartController.emptyCart);
module.exports = router;
And then update the routeHandler.js file to this:
然后将routeHandler.js文件更新为此:
const productRoutes = require("./Product/routes");
const cartRoutes = require('./Cart/routes')
module.exports = app => {
app.use("/product", productRoutes);
app.use("/cart", cartRoutes);
}
测试购物车功能 (Testing the cart features)
Adding item to cart:
将商品添加到购物车:
Get cart items:
获取购物车物品:
空购物车 (Empty cart)
For testing purposes, create some products using POSTMAN. This is what we will be using in our frontend application for testing purposes.
为了进行测试,请使用POSTMAN创建一些产品。 这就是我们将在前端应用程序中用于测试目的的内容。
行使 (Exercise)
- Add subtract product quantity from cart 从购物车中添加减去产品数量
- Remove single product from cart 从购物车中移除单个产品
After implementing this, Push your work to git and add the link in the comment section. Let’s have some fun😁
完成此操作后,将您的工作推送到git并将链接添加到注释部分。 让我们玩得开心😁
Now that our backend is ready we can now move on to our frontend. For frontend, I am going write that in 3 different frontend technologies, Vuejs, Angular and React, will post links here soon.
现在我们的后端已经准备好了,我们现在可以继续进行前端了。 对于前端,我要写的是,在3种不同的前端技术中, Vuejs ,Angular和React将很快在此处发布链接。
普通英语JavaScript (JavaScript In Plain English)
Did you know that we have three publications and a YouTube channel? Find links to everything at plainenglish.io!
您知道我们有三个出版物和一个YouTube频道吗? 在plainenglish.io上找到所有内容的链接!
翻译自: https://medium.com/javascript-in-plain-english/building-a-shopping-cart-in-node-js-bdbe02614eb9
父子节点 构建