Some websites and apps we use at ordinary times will save our data. When we close these websites or apps, we can still see our previous text and video records when we open them next time. In the second tutorial of the mini-full stack e-commerce application series, we will implement the back-end API data interface through the Express framework based on node.js platform and store the data in MongoDB. In this way, our website will be able to record the items that users add, and we will be able to retrieve our previous records whenever we open them later.

prompt

Reading this article requires some understanding of the Express framework. If you’re not familiar with it, read this tutorial to get started.

Welcome to the Series from Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express:

  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (PART 1)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Part 2)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Part 3)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (4)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (5)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (6)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (7)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Final)

If you think our tutorial is good, please give us a thumbs up! Encourage us to write the rest of the tutorial faster and better! You can also tell us what features you want in the comments section, and we’ll think about it carefully!

Quick start

code

You can view the final source code for this part of the tutorial on Github: source code address.

Initialize the project

First, let’s use express-Generator scaffolding to initialize our Express project. Run the following command on the terminal to install:

npm install -g express-generator
Copy the code

Open the terminal and run the following command to check whether the installation is successful:

express --version # 4.15.5
Copy the code

Then type the following command to initialize our Express project:

express vue-online-shop-backend
Copy the code

When the project is successfully initialized, start the project with the following command:

cd vue-online-shop-backend
npm install
npm start
Copy the code

Then open your browser and type http://localhost:3000/ to see what our initial project looks like.

A preliminary study of scaffolding code

In the project code initialized via Express-Generator, we only need to know about the following four files throughout the tutorial:

  • app.js: Express main application file
  • bin/www: script used to start the server
  • routes/index.js: Main route file
  • views/index.ejsThe template file for the home page, here we are only going to implement the API data interface, so don’t worry

Unlike the previous Express tutorial, the scaffolding code does not put all the routes in app.js, but rather breaks them up by subapplication (users, index), which is similar to the vue-Router nesting pattern in the first tutorial in this series.

Let’s take a quick look at app.js and routes/index.js and walk through the basics of Express. Here’s the code for app.js:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var index = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine'.'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') = = ='development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
Copy the code

It starts with importing dependencies, then initializing the Express instance by calling Express (), then setting the template engine to EJS and the directory to store the template engine, then a series of middleware loads and uses, and finally exporting the Express instance. Throw in the bin/ WWW script to make the call and start the server.

prompt

Here we can see that the two routes we imported, Index and Users, are also handled like any other middleware, so in Express “everything is middleware”.

Let’s take a look at our routing part routes/index.js again. Routing is the core of our API server. We need to access specific routing interfaces to add, delete, modify and check data.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/'.function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
Copy the code

The code above imports Express, then uses its property methods to generate a Router instance, then defines the HTTP method get to handle access to our server at /, and finally exports our Index route.

Our API server actually accesses our defined routes through VARIOUS HTTP methods (POST, DELETE, PUT, GET, etc.), and then adds, deletes, changes, and checks the database accordingly to GET the data we want.

summary

By briefly explaining the four files that express-Generator scaffolding generated for us, we learned the following:

  • In Express, Middlewares, we combine middleware to handle complex back-end logic.
  • Our API server actually does this by defining a series of routes and adding, deleting, modifying and querying data when accessing these routing interfaces using different HTTP methods.
  • While Express can also present the user interface through a template engine, since the front end of our mini-e-commerce application is already implemented with Vue, we don’t need a template engine.

Adding the MongoDB Database

The most popular solution to persistent storage is undoubtedly database, and MongoDB stands out from many database products by virtue of its excellent performance, scalability and flexible data mode. Moreover, the core functions of MongoDB are based on BSON (Binary JSON) and even provide JavaScript Shell, so it is more popular in the Node community. MongoDB can be downloaded from its website. Once downloaded and installed, open a new terminal (command console) and run the following command to open the database (Windows users can search mongo.exe and open it) :

$mongod 2019-12-22T18:10:25.285+0800 I CONTROL [initandListen] MongoDB starting: Pid =14475 port=27017 DBPath =/data/db 64-bit host=mRc 2019-12-22T18:10:25.285+0800 I CONTROL [initandlisten] db version pid=14475 port=27017 DBPath =/data/db 64-bit host=mRc 2019-12-22T18:10:25.285+0800 I CONTROL [initandlisten] DB version T18 v3.6.0 2019-12-22: the assembling. 285 + 0800 I CONTROL [initandlisten] git version: a57d8e71e6998a2d0afde7edc11bd23e5661c915...Copy the code

Many logs are generated when mongod is enabled and can be accessed via localhost:27017. Then go back to the terminal we opened earlier and install the Mongoose NPM package:

$ npm install mongoose
Copy the code

Mongoose is the most popular MongoDB ODM (Object Document Mapping), which is more convenient to use than the underlying MongoDB Node driver.

Then we import Mongoose into our app.js file and connect to our MongoDB database through the interface provided by Mongoose:

// ...
const mongoose = require('mongoose');

// ...

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine'.'ejs');

// Connect to the database
mongoose.connect(`mongodb://localhost:27017/test`);


// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
// ...
Copy the code

Then we run the server with NPM start and we connect to our MongoDB database in Express. Although we don’t see any results yet, we will soon write a route to manipulate the database to test the connection.

Allows resources to be accessed across domains

Then we need to do something extra, which may seem irrelevant to our project, but is a necessary part of it, which is to enable cross-domain access to CORS (Cross-Origin Resources Sharing). CORS is a solution to restrict access to resources under this domain name. When CORS is off, access to resources under this domain name by another domain name will be denied. If you want to know what CORS is in detail, here is a recommended article by Ruan Yifeng, which explains the principle of CORS in detail.

We open the app.js file and add the following code:

// ...

// Database connection here
mongoose.connect(`mongodb://localhost:27017/test`);

// CORS config here
app.all('/ *'.function(req, res, next) {
  // CORS headers
  res.header("Access-Control-Allow-Origin"."*"); // restrict it to the required domain
  res.header('Access-Control-Allow-Methods'.'GET,PUT,POST,DELETE,OPTIONS');
  // Set custom headers for CORS
  res.header('Access-Control-Allow-Headers'.'Content-type,Accept,X-Access-Token,X-Key');
  if (req.method == 'OPTIONS') {
    res.status(200).end();
  } else{ next(); }});Copy the code

In general, the above code has a lot of problems, we usually use the NPM package CORS to solve the problem, of course, we use a simple and crude way.

Design Schemas and Models for the database

In order to interact with MongoDB database through Mongoose in the server, we need to define Schema and Model. Define them to tell Mongoose what data structure and corresponding data type you want.

Let’s create a model/index.js file to write our Schema.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const model = mongoose.model.bind(mongoose);
const ObjectId = mongoose.Schema.Types.ObjectId;

const productSchema = Schema({
  id: ObjectId,
  name: String.image: String.price: Number.description: String.manufacturer: { type: ObjectId, ref: 'Manufacturer'}});const manufacturerSchema = Schema({
  id: ObjectId,
  name: String});const Product = model('Product', productSchema);
const Manufacturer = model('Manufacturer', manufacturerSchema);

module.exports = { Product, Manufacturer };
Copy the code

Schema receives a JavaScript object to describe the data structure we need and the corresponding data types. In addition to the familiar data types such as String and Number, the ObjectId is a special data type. We use it to define the primary key for our individual MongoDB documents, which is used to mark the uniqueness of the stored data.

We can also see that in our productSchema, manufacturer data structure we define a ref attribute, which is a relational database-like foreign key that MongoDB provides to us, allowing us to create one-to-many data documents. Therefore, the manufacturer attribute of productSchema corresponds to a manufacturer record.

We then use the model to create the correct data model, and then export the data model we created. The model here is the model in the classic MVC design pattern.

Complete API Routing

Routing is a key component of Express and an entry point through which the client interacts with the server. An Express route takes two parameters: Request and Response, one used to retrieve the client’s Request and one used to send the Response to the client server.

Open the app.js file and add the following code:

// ...

var index = require('./routes/index');
var users = require('./routes/users');
const api = require('./routes/api');

var app = express();

// ...

app.use('/', index);
app.use('/users', users);
app.use('/api/v1', api);

// ...
Copy the code

As you can see, we imported the API route and defined the access path/API /v1. All calls to/API /v1 and its subpaths such as/API /v1/ XXX activate API handlers, which are also called Controllers in the classic MVC design pattern.

Next we write API Controllers, which define the routing interfaces for handling goods and manufacturers. Here we’ll use classic RESTful apis to write our routing interfaces:

const express = require('express');
const router = express.Router();
const productController = require('.. /.. /controllers/product');
const manufacturerController = require('.. /.. /controllers/manufacturer');

router.get('/manufacturers', manufacturerController.all);
router.get('/manufacturers/:id', manufacturerController.byId);
router.post('/manufacturers', manufacturerController.create);
router.put('/manufacturers/:id', manufacturerController.update);
router.delete('/manufacturers/:id', manufacturerController.remove);

router.get('/products', productController.all);
router.get('/products/:id', productController.byId);
router.post('/products', productController.create);
router.put('/products/:id', productController.update);
router.delete('/products/:id', productController.remove);

module.exports = router;
Copy the code

As you can see, we imported our productController and manufacturerController into the Index Controller. A series of routes are then defined.

The functions of the first five operating Manufacturer routes are as follows:

  • GET /manufacturersObtain all manufacturers
  • GET /manufacturers/:idGet the individual manufacturer, here:idRepresents dynamic routing, used to match any string:/manufacturers/<any-string>.
  • POST /manufacturersThe user creates a single manufacturer
  • PUT /manufacturers/:idUsed to modify a single manufacturer
  • DELETE /manufacturers/:idUsed to delete a single manufacturer

The five routing functions of the corresponding product are as follows:

  • GET /productsGet all products
  • GET /products/:idGet a single item, here:idRepresents dynamic routing, used to match any string:/products/<any-string>.
  • POST /productsThe user creates a single item
  • PUT /products/:idUsed to modify a single item
  • DELETE /products/:idUsed to delete a single item

Finally we export our route.

Now let’s look at the manufacturer Controller.

const Model = require('.. /model');
const { Manufacturer } = Model;

const manufacturerController = {
  all(req, res) {
    Manufacturer.find({})
      .exec((err, manfacturers) = > res.json(manfacturers))
  },
  byId(req, res) {
    const idParams = req.params.id;

    Manufacturer
      .findOne({ _id: idParams })
      .exec((err, manufacturer) = > res.json(manufacturer));
  },
  create(req, res) {
    const requestBody = req.body;
    const newManufacturer = new Manufacturer(requestBody);

    newManufacturer.save((err, saved) = > {
      Manufacturer
        .findOne({ _id: newManufacturer._id })
        .exec((err, manfacturer) = > res.json(manfacturer))
    })
  },
  update(req, res) {
    const idParams = req.params.id;
    let manufacturer = req.body;

    Manufacturer.updateOne({ _id: idParams }, { ... manufacturer }, (err, updated) => { res.json(updated); }) }, remove(req, res) {const idParams = req.params.id;

    Manufacturer.findOne({ _id: idParams }).remove( (err, removed) = > res.json(idParams) )
  }
}

module.exports = manufacturerController;
Copy the code

As you can see, we defined a manufacturerController object that organizes a series of operations to add, delete, change, and review manufacturers.

We started by importing the ManufacturerModel we defined earlier, which is the interface Mongoose provided for us to manipulate the database, We add, delete, and modify data using a series of find, findOne, updateOne, and deleteOne defined on the Model.

Finally, there is our Product Controller, whose internal operations are basically the same as those of the Manufacturer Controller we discussed above.

const Model = require('.. /model');
const { Product } = Model;

const productController = {
  all(req, res) {
    Product.find({})
      .populate('manufacturer')
      .exec((err, products) = > res.json(products))
  },
  byId(req, res) {
    const idParams = req.params.id;

    Product
      .findOne({ _id: idParams })
      .populate('manufacturer')
      .exec((err, product) = > res.json(product));
  },
  create(req, res) {
    const requestBody = req.body;
    const newProduct = new Product(requestBody);

    newProduct.save((err, saved) = > {
      Product
        .findOne({ _id: newProduct._id })
        .populate('manufacturer')
        .exec((err, product) = > res.json(product))
    })
  },
  update(req, res) {
    const idParams = req.params.id;
    const product = req.body;

    console.log('idParams', idParams);
    console.log('product', product);

    Product.updateOne({ _id: idParams }, { ... product }, (err, updated) => { res.json(updated); }) }, remove(req, res) {const idParams = req.params.id;

    Product.findOne({ _id: idParams }).remove( (err, removed) = > res.json(idParams) )
  }
}

module.exports = productController;
Copy the code

After writing the above code and saving it, open the terminal and enter NPM start to start our server.

Since our server will connect to the MongoDB database when it is started, we need to ensure that the local MongoDB database is enabled. We can run the following command to enable it:

$ mongod
Copy the code

Ok, now that our API server is set up, let’s use the API testing tool POSTman to test whether our API is successful.

Test GET/API /v1/manufacturers:

Test POST/API /v1/manufacturers: We add “OnePlus”

Tests PUT/API/v1 / manufacturers / : id: here we change “One Plus” to “One Plus”

Test the DELETE/API/v1 / manufacturers / : id: we add just “one plus”

Product, POST/API /v1/products: Here, we add Manufacturer as the foreign key when defining the data attribute of product. Therefore, the corresponding Manufacturer attribute must be ObjectId of a Manufacturer. For example, here we add Xiaomi’s new product Mix Alpha:

You can see that we have added a new Mi phone called “Mix Alpha” with manufacturer 5DA72eAAC6CCEA32F823C247.

summary

Now that we have our API server set up, we have learned the following in this tutorial:

  • Know Express routing and how to connect to MongoDB with Mongoose
  • Write routes, models, and Controllers
  • Use POSTman to test the API we wrote

This tutorial gives you a basic understanding of how to write API back-end servers using Node and Express. Now that we know the basics of Vue and how to set up back-end servers, we’ll consider how to build large applications using Vue. See you in the next tutorial!

Want to learn more exciting practical skills tutorial? Come and visit the Tooquine community.