Node and node based Express framework + MongoDB

Keywords: node.js MongoDB express

1 Node

Node provides us with a JavaScript code running environment that can directly interact with the operating system without relying on the browser.

It can directly use JS to write background programs, so that foreground developers can also quickly develop background code, that is, HTML+JavaScript+MongoDB; Instead of Tomcat, the foreground (HTML+Javascript) + background (Java) + database (MySql).

Within n seconds of setTimeout waiting, the program does not block, but continues to execute downward. This is the asynchronous non blocking of Node.js.
In the actual application environment, there are often many I/O operations (such as network requests, database queries, etc.) that take a lot of time, and Node.js can continue to process new requests while waiting, which greatly improves the throughput of the system.

Node introduces three new global objects: 1) require; 2) exports and 3) module.

require is used to import other Node modules. Its parameters accept a string representing the name or path of the module, which is usually called the module identifier. There are three forms:

  • Write module names directly, usually core modules or third-party file modules, such as os, express, etc
  • The relative path of the module, pointing to other Node modules in the project, such as. / utils
  • The absolute path of the module, for example, / home/xxx/MyProject/utils
// Import built-in libraries or third-party modules
const os = require('os');
const express = require('express');

// Import other modules through relative paths
const utils = require('./utils');

// Import other modules through absolute path
const utils = require('/home/xxx/MyProject/utils');

Export export object

// myModule.js
function add(a, b) {
  return a + b;
}

// Export function add
exports.add = add;

By adding the add function to the exports object, external modules can use this function through the following code. Create a main.js next to myModule.js. The code is as follows:

// main.js
const myModule = require('./myModule');

// Call the add function in myModule.js
myModule.add(1, 2);

It is not required for actual project release or deployment. npm adds all development dependencies to the dev dependencies field.

Build a simple static server with node

Using node to build a server can be divided into three steps:

  1. Import the corresponding module with require
  2. Create server
  3. configure port

Create a new server.js in the new node server folder:

//1.require http module
var http = require('http')
//2. Create a server and pass in a callback function to process web page requests
var server = http.createServer(function (req, res) {
  res.setHeader('Content-Type', 'text/html;charset=utf-8')
  res.writeHead(200, 'OK')
  res.write(`<html><head></head><body><h1>hello world<h1></body></html>`)
  res.end()
})
console.log('open http://localhost:8080')
//3. Set the listening port
server.listen(8080)

Open the terminal, enter the node server folder, and enter node index.js to start the server

Node middle layer
On the premise of separating the front and back ends, we can set up an intermediate layer (node.js) between the server (java) and the browser (js)

Reasons for choosing node as the middle tier: using js, a language familiar to the front end, has low learning cost; Good execution speed.

When the front and rear ends are separated, the node middle layer can assume more responsibilities:

  • Agent: in the development environment, agents can be used to solve the most common cross domain problems; In the online environment, agents can be used to forward requests to multiple servers.

  • Caching: caching is actually a requirement closer to the front end. The node middle layer can directly handle some caching requirements.

  • Current limiting: the node middle layer can limit the current corresponding to the interface or route.

  • Routing: the front end needs to master the permissions and logic of page routing.

2 why use node

node is simple and powerful, lightweight and scalable.
Simple: node uses js for coding
Powerful: non blocking I/O (input / output), good at high concurrent access
Lightweight: the front and rear ends use the unified language js
Scalable: it can easily deal with multi instance, multi server architecture, and has a lot of third-party components

3 what are the benefits of using npm

Through npm, you can install and manage project dependencies, and specify the specific version number of dependencies. You can manage project information and configure scripts through package.json file.

4 how to determine whether the current script is running in the browser or node environment

By judging the global object, if it is window, the current script is running in the browser; If it is global, it is in the node environment.

5 difference between synchronous and asynchronous

Once the synchronous method call starts, the caller must wait until the method call returns before continuing the subsequent behavior;
Once the asynchronous method call starts, the method call will return immediately, and the caller can continue the subsequent operation. Asynchronous methods are usually in another thread, and the whole process will not hinder the caller's work.

6 Introduction to several common modular specifications

CommonJS specification is mainly used for server-side programming. The loading module is synchronous, which is not suitable for the browser environment, because synchronization means blocking loading, and browser resources are loaded asynchronously.

Amd specification loads modules asynchronously in the browser environment, and multiple modules can be loaded in parallel. However, the development cost of AMD specification is high, and it is difficult to read and write the code.

CMD specification is very similar to AMD specification. Both are used for browser programming and can be easily run in Node.js.

ES6 realizes the module function, and the implementation is quite simple. It can completely replace CommonJS and AMD specifications and become a general module solution for browsers and servers.

7. How does node establish connection with MongoDB

1) Introduce Mongoose (Mongoose encapsulates some common methods such as adding, deleting, modifying and querying MongoDB, making it easier for Node to operate MongoDB database)

2) Use the mongoose.connect() method to connect to the MongoDB database

3) Whether the listening connection is successful

4) After the connection is successful, add, delete, modify and query the database through the node and writing interface

8 Express

Express is a popular web development framework based on node.js running environment, which can quickly build a fully functional website.
Installation: npm install express
First, install the Express generator scaffold (used to initialize the Express project): NPM install - G Express generator
Initialize Express Project Command: Express Vue online shop backend (of course, you can also manually npm init initialize)
Start project: npm start

Some websites and apps we usually use will save our data. When we close these websites or apps, we can see some previous text and video records when we open them next time. We can implement the back-end service through the Express framework based on the Node.js platform and store the data in MongoDB. In this way, our website can record the addition, deletion, modification and other operations of users, and can obtain our previous records whenever it is opened in the future.

A server is implemented with the built-in http module of Node.js

const http = require('http'); // Import module

const hostname = 'localhost'; // Specify the host name
const port = 3000; // Specify the port number

// Create server
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end('Hello World\n');
});

// Turn on the server
server.listen(port, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Meaning of the above code:

  1. Import http module
  2. Specify the hostname and port number of the server
  3. Create an HTTP server with http.createServer. The parameter is a callback function, accept a request object req and a response object res, and write the response content in the callback function (status code 200, type HTML document, content Hello World)
  4. Turn on the server at the specified port

Finally, run server.js: node server.js
Open localhost:3000 with a browser and you can see Hello World

It can be found that directly using the built-in http module to develop the server has the following obvious disadvantages:

  • You need to write a lot of low-level code -- for example, manually specify the HTTP status code and header field, and finally return the content.
  • There is no special routing mechanism - routing is one of the most important functions of the server. Through routing, the corresponding content can be returned according to different request URL s and HTTP methods of the client.

This leads to two major encapsulation and improvement of built-in http by Express:

  • More powerful Request and Response objects, adding many practical methods
  • The definition and analysis of routing can easily split the code

(1) More powerful Request and Response objects

The Request object is usually represented by the req variable. Here are some important members of req:

  • req.body: the data of the client request body, which may be form or JSON data
  • req.params: path parameter in request URI
  • req.query: query parameters in the request URI
  • req.cookies: cookies of the client

The Response response object, usually represented by the res variable, can perform a series of Response operations, such as:

// Send a string of HTML code
res.send('HTML String');

// Send a file
res.sendFile('file.zip');

// Render a template engine and send
res.render('index');

The operations on the Response object are very rich, and can also be called in a chain:

// Set the status code to 404 and return the Page Not Found string
res.status(404).send('Page Not Found');

(2) Routing mechanism
When the client sends a request to the server, it includes two elements: path (URI) and HTTP request method (including GET, POST, etc.).
The combination of path and request method is generally called API Endpoint.
The mechanism that the server selects the corresponding processing logic according to the endpoint accessed by the client is called routing.

In Express, you only need to define a route in the following form:

app.METHOD(PATH, HANDLER)

Of which:

  • app is an express server object
  • METHOD can be any HTTP request METHOD, including get, post, put, delete, and so on
  • PATH is the URI accessed by the client, for example / or / about
  • HANDLER is the callback function when the route is triggered

Next, we will start to implement a web server with Express (different from the above server implemented with the built-in http module of Node.js)

1 create the express server folder and initialize the project

npm init

Then you can enter all the way down (of course, you can fill in carefully), and you will find that the package.json file has been created.

2 add Express project dependency

npm install express

3 install nodemon

nodemon accelerated development
Nodemon is a development server that can detect changes in workspace code and restart automatically. Install nodemon with the following command:

npm install nodemon --save-dev

Here, we install nodemon as dev dependencies for development, because it is only needed for development. At the same time, we add the start command in package.json:

{
  // ...
  "scripts": {
    "start": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  // ...
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}

4 code implementation

Create the server.js file

const express = require('express');

const hostname = 'localhost';
const port = 3000;

const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});

// Call the listen method to start the server
app.listen(port, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

In the above code, we first create an Express server object with the express() function, then define the route of the home page / with the route definition method app.get mentioned above, and finally call the listen method to start the server.

From this step, we can start the server by running the npm start command, and we can also see the contents of Hello World, but the code is much simpler and clearer.

Tip: after running npm start, you can keep the server open. After editing and saving the code, Nodemon will automatically restart the server and run the latest code.

middleware
Middleware is not a unique concept of Express. It is a widely used software engineering concept (even extended to other industries). It refers to the component that decouples the specific business logic from the underlying logic. In other words, middleware is code that can be applied to multiple application scenarios and has good reusability.

The simplified middleware process of Express is shown in the following figure:

First, the client sends a request to the server, then the server executes each Middleware in turn, finally reaches the route, and selects the corresponding logic to execute.

Two points need special attention:

  • The middleware is executed in order, so the order is very important when configuring the middleware
  • When the middleware executes the internal logic, it can choose to pass the request to the next middleware or directly return the user response

Definition of Express Middleware
In Express, middleware is a function:

function someMiddleware(req, res, next) {
  // Custom logic
  next();
}

Among the three parameters, req and res are the previously mentioned Request object and Response response object;
The next function is used to trigger the execution of the next middleware.

Note: if you forget to call the next function and do not directly return the response, the server will be directly stuck in the middleware and will not continue to execute!

There are two ways to use middleware in Express: Global middleware and routing middleware:

  • Global Middleware
    The middleware can be registered through the app.use function:
app.use(someMiddleware);
  • Routing Middleware
    By registering the middleware during route definition, the middleware will only be executed when the user accesses the URI corresponding to the route, for example:
app.get('/homepage', someMiddleware, (req, res) => {
  res.send('Hello World');
});

Then, the defined someMiddleware will be triggered only when the user accesses / home, and will not be triggered when accessing other paths.

Writing Middleware
Implement the first Express middleware: the function is very simple, that is, print the access time, HTTP request method and URI of the client at the terminal, called logging middleware. The code is as follows:

// ...

const app = express();

function loggingMiddleware(req, res, next) {
  const time = new Date();
  console.log(`[${time.toLocaleString()}] ${req.method} ${req.url}`);
  next();
}

app.use(loggingMiddleware);

app.get('/', (req, res) => {
  res.send('Hello World');
});

// ...

Note: writing the console.log statement in the middleware is a bad practice, because the console.log (including other synchronous codes) will block the asynchronous event loop of Node.js and reduce the server throughput. In actual production, it is recommended to use the third-party excellent logging middleware, such as morgan, winston and so on.

Run the server and try to access each path with your browser. Here, I visited the home page (localhost:3000) and / hello (localhost:3000/hello, the browser should see 404) to see the corresponding output of the terminal console:

[11/28/2019, 3:54:05 PM] GET /
[11/28/2019, 3:54:11 PM] GET /hello

We only implemented a very simple middleware. In fact, middleware can not only read various attributes on req objects, but also add new attributes or modify existing attributes, which can easily implement some complex business logic (such as user authentication).

Rendering a page with a template engine
Express provides good support for today's mainstream template engines (such as Pug, Handlebars, EJS, etc.).
(template engine: it only needs to be understood as an "upgraded HTML document")

Handlebars will be used as the template engine. First add the npm package:

npm install hbs

Create a views folder to place all templates. Then create the homepage template index.hbs, with the code as follows:

<h1>Index</h1>
<a href="/contact">Go</a>

contact.hbs:

<h1>contact</h1>

Configure and use templates in server.js:

// Specify the template storage directory
app.set('views', '/path/to/templates');

// Specify the template engine as Handlebars
app.set('view engine', 'hbs');

When using templates, you only need to call the res.render method in the routing function.

// Render a template named hello.hbs
res.render('hello');

The modified server.js code is as follows:

// ...

const app = express();

app.set('views', 'views');
app.set('view engine', 'hbs');

// Defining and using logging middleware

app.get('/', (req, res) => {
  res.render('index');
});

app.get('/contact', (req, res) => {   // The route definition of GET /contact is added here
  res.render('contact');
})

// ...

Split logic using sub routes
When our website is getting larger and larger, it is not a good idea to put all the code in server.js. "Split logic" (or "modularization") is the most common practice, and in Express, we can implement it through sub routing Router.

First, create a routes directory to store all sub routes. Create the routes/index.js file with the following code:

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

router.get('/', (req, res) => {
  res.render('index');
});

router.get('/contact', (req, res) => {
  res.render('contact');
});

module.exports = router;

Create routes/api.js with the following code:

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

router.get('/', (req, res) => {
  res.json({ name: 'Ann', age: 12 });
});

router.post('/new', (req, res) => {
  res.status(201).json({ msg: 'Hello' });
});

module.exports = router;

Finally, we delete all the old routing definitions in server.js and replace them with two routers just implemented. The code is as follows:

const express = require('express');
const path = require('path');

const indexRouter = require('./routes/index');
const apiRouter = require('./routes/api');

const hostname = 'localhost';
const port = 3000;

const app = express();

// ...
app.use(express.static('public'));

app.use('/', indexRouter);
app.use('/api', apiRouter);

app.use('*', (req, res) => {
  res.status(404).render('404', { url: req.originalUrl });
});

// ...

9 Express + MongoDB

Initialize Express Project

Here we choose to use the Express generator scaffold to initialize our Express project:

npm install -g express-generator

To initialize an Express project:

express e-server

Open project:

npm install
npm start

Open browser http://localhost:3000/ You can see the initial good project effect.

In the project code initialized by express generator, we only need to understand the following four files throughout the tutorial:

  • app.js: Express application master file
  • bin/www: script used to start the server
  • routes/index.js: route master file
  • views/index.ejs: the template file of the home page. Here, we only intend to implement the API data interface, so we don't care

Different from the previous Express tutorial, the scaffolding code does not put all routes in app.js, but splits them according to different sub applications (users and index).

In app.js:
Start with importing related dependencies, and then initialize the Express instance by calling express(); Then we set the template engine as ejs and the storage directory of the template engine, then load and use a series of middleware, finally export the Express instance, throw it to the bin/www script to call and start the server.

Routing part routes/index.js
Routing is the core of API server. We need to access specific routing interfaces when we add, delete, modify and query 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;

The above code first imports express, then uses its attribute method to generate a router instance, then defines the HTTP method get to handle how to handle when our server address is / accessed by the get method, and finally exports the index route.

⭐ Our API server actually accesses the routes defined by us through various HTTP methods (POST, DELETE, PUT, GET, etc.), and then adds, deletes, changes and queries the database to obtain the data we expect.

⭐ Tip: Although Express can also display the user interface through the template engine, because the front end of my project has been implemented with Vue, there is no need for the template engine.

Access MongoDB database

The most popular solution to data persistent storage is undoubtedly database, and MongoDB stands out from many database products with its excellent performance, scalability and flexible data mode.

After you start MongoDB, you can access it through localhost:27017.

Install Mongoose
Mongoose encapsulates some common methods such as adding, deleting, modifying and querying MongoDB, making it easier for nodes to operate MongoDB database.

npm install mongoose

Import mongoose into the app.js file and connect to the MongoDB database through the interface mongoose.connect provided by mongoose:

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

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

Then run the server through npm start, and we will connect to our MongoDB database in Express. Although we can't see any effect yet, we will write the origin operation database to test the effectiveness of the connection.

Designing Schemas and Models for databases
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 you need and what the corresponding data type is.

Create a model/index.js file to write a Schema

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

const textSchema = Schema({
  id: ObjectId,
  text: String,
  createTime: {
    type: Date,
    default: Date.now
  },
});
const Text = model("Text", textSchema);

module.exports = { Text };

Schema receives a JavaScript object to describe the data structure and corresponding data types we need. In addition to the well-known data types such as String and Number, ObjectId is a special data type. We use it to define the main key of our single MongoDB document to mark the uniqueness of stored data.
Then create the corresponding data Model through Model, and then export the created data Model. The Model here is the Model in the classic MVC design pattern.

Complete API routing
Routing is a key part of Express and the entrance for the client to interact with the server. In Express routing, two parameters are accepted: Request and Response, one is used to obtain the client's Request and the other is used to send the Response to the client server.

Create the api.js file under router. Open the app.js file and add the following code:

const api = require('./routes/api');

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

The api route is imported and the access path / api/v1 is defined. All accesses to / api/v1 and its sub paths, such as / api/v1/xxx, activate the api handler. In the classic MVC design pattern, the api is also called Controllers.

Create the Controllers folder, and create the texts.js file below:

Specific texts Controller

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

const textController = {
  all(req, res) {
    Text.find({}).sort({ _id: -1 })
      .exec((err, texts) => res.json(texts))
  },
  byId(req, res) {
    const idParams = req.params.id;

    Text
      .findOne({ _id: idParams })
      .exec((err, text) => res.json(text));
  },
  create(req, res) {
    const requestBody = req.body;
    const newText = new Text(requestBody);

    newText.save((err, saved) => {
      Text
        .findOne({ _id: newText._id })
        .exec((err, text) => res.json(text))
    })
  },
  update(req, res) {
    const idParams = req.params.id;
    let text = req.body;

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

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

module.exports = textController;

Then write api Controllers:

const express = require('express');
const router = express.Router();
const textController = require('../../controllers/text');

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

module.exports = router;

Now the API server is set up and can be tested through the API testing tool postman.

Test 1:

 <script src="./axios.min.js"></script> 
mounted() {
    axios
      .get('http://localhost:3000/api/v1/texts')
      .then(response => (this.info = response))
      .catch(function (error) { // Request failure processing
        console.log(error);
      });
}

Test 2 in vue:

import axios from 'axios'
Vue.prototype.$axios = axios    //Global registration: this.$axios
mounte() {
   this.$axios
      .get("http://localhost:3000/api/v1/texts")
      .then((response) => console.log(response));
}

10 MongoDB common commands

Enter database management mode mongo
Exit database management mode exit

Show all databases list show dbs
Create database: use dbName (if the database name does not exist, create it, and enter directly if it already exists) use test
View current database: db
Delete database: db.dropDatabase()

Display all collections in the current database: show collections
Create a collection: db.tableName.insert({}), which is usually created automatically when creating data
Delete collection: db.tableName.drop()

New data: dB. Collection name. insert({BSON data}), such as db.user.insert ({name ":" admin "," age ": 20})

Query data: db. Collection name. Find ({condition object}) db.foo.find() - query all
Modify data: db. Set name. Update (find object, modify result)
Delete data: db. Collection name. remove ({}) - deletes all data in the current collection,
db. Collection name. remove({"name": "Ann"}) - deletes the specified data

11 relational and non relational databases

Relational database

Relational database refers to a database that uses a relational model to organize data. Relational model refers to a two-dimensional table model.

advantage:

  • Easy to understand: the two-dimensional table structure is relatively easy to understand
  • Easy to use: the SQL language is easy to operate
  • Easy maintenance

Disadvantages:

  • High concurrency (tens of thousands of read and write requests per second) is difficult to handle
  • Low query efficiency
  • When the number of users and visits of an application system increases day by day, the database can not simply expand the performance and load capacity by adding hardware and service nodes like the web server. When the database system needs to be upgraded and expanded, it often needs shutdown maintenance and data migration.

Mainstream relational databases: Oracle, SQL Server, MySQL, etc

Non relational database

Non relational databases are stored as key value pairs.

advantage:

  • If you want to obtain different information, you only need to get the corresponding value according to the key, instead of performing an association query on multiple tables in a relational database.
  • It is applicable to social network services, such as microblog. The upgrading of the system and the increase of functions mean great changes in the data structure, which is difficult for relational databases to cope with.

Cons: not suitable for persistent storage

Mainstream non relational databases: Redis, MongoDB, etc

Redis is the key value database
The key value database is characterized by high concurrent read-write performance.
Key value database is a database that stores data in key value pairs. The whole database can be understood as a large map, and each key will correspond to a unique value.

MongoDB is a document database
Documents can be long, complex, or even unstructured. mongodb is a document database and a key value database.
MongoDB has powerful query function and can query quickly in massive data.

12 using node to solve cross domain problems

Set on the node server side:

//Solve cross domain problems
  app.use(async(ctx, next) => {           
      // Specify the source domain that the server side allows cross domain resource access* Indicates that JavaScript from any domain is allowed to access resources
      ctx.set("Access-Control-Allow-Origin", "*");

      // ...

      await next();
  });

Posted by DaiLaughing on Mon, 13 Sep 2021 19:10:20 -0700