This article has participated in the third “topic writing” track of the Denver Creators Training Camp. For details, check out: Digg Project | Creators Training Camp third is ongoing, “write” to make a personal impact

In this paper, we will develop a complete project with front-end pages and back-end storage, and data communication through API at the front and back ends, centering on Node.js.

The back-end service

First, the HTTP module of Node.js supports the creation of a server, which we use to initialize the server as follows

const http = require('http');

const host = '127.0.0.1';
const port = '8090';

http.createServer((req, res) = > {
  console.log(req.url, 'requested at'.new Date());
  res.end('hello')
}).listen(port, host, () = > {
  console.log(`server running at http://${host}:${port}`);
})
Copy the code

After executing the file using Node.js, we can see the output of the callback function in the Listen method in control as follows

Server running at http://127.0.0.1:8090Copy the code

Clicking on the service address, or manually accessing it in a browser, returns hello from the server we just created

In addition, the server receives two requests from the client (browser), namely “/” and “/favicon.icon”, which is the default behavior of the browser, and it can be seen that after receiving the above two requests, the server output two request records as expected

/ Requested at 2021-08-25T01:16:33.143z /favicon. Ico Requested at 2021-08-25T01:16:33.808zCopy the code

That was the prototype of our back-end service, and we refined it to do two things:

  1. A request to the service root path ‘/’ returns the page index.html
  2. The requests received by the server are recorded in the server log file log.json

The best way to do both is to develop using a Node.js framework, such as Express or KOA, which we use here.

Install Express

First, we add the NPM configuration to the current project and install the Express module with the following command

npm init -y
npm install express
Copy the code

Refactoring services

Next, we retrofit the original Node.js service to Express implementation

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

const host = '127.0.0.1';
const port = '8090';

app.get('/'.(req, res) = > {
  res.end('hello')
})

app.listen(port, host, () = > {
  console.log(`server running at http://${host}:${port}`);
})
Copy the code

The Express module we introduced is itself a function that returns a server instance, which you can simply think of as a wrapper around the HTTP module in Node.js.

As you can see, it also has the listen method, and can also pass in the host address and port that the service listens to, and has the same listener callback function. The instance also has a GET method to receive requests from clients and give corresponding feedback.

Back page

This is where the advantage of using Express comes in, because node.js HTTP creates servers that can only return data directly, whereas Express implements server instance objects that can return files directly. We use this capability to return the page file on the root path request as follows

app.get('/'.(req, res) = > {
  res.sendFile('./index.html')})Copy the code

But then the console will report the following error

Server running at http://127.0.0.1:8090 TypeError: path must be absolute or specify root to res.sendfileCopy the code

This tells us that to use the sendFile method, either specify the absolute path to the file or declare the base path to the file that the sendFile method returns. So we can do file return in either of the following ways

const path = require('path');
const htmlRoot = path.resolve(__dirname, 'index.html')
res.sendFile(htmlRoot)
Copy the code
res.sendFile('index.html', {
    root: '. '
})
Copy the code

We use Vue. Js of CDN version to build the page quickly, as follows

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue"></script>
</head>
<body>
  <div id="app">
    {{message}}
  </div>
  <script>
    var app = new Vue({
      el: '#app'.data: {
        message: 'Hello Vue! '}})</script>
</body>
</html>
Copy the code

At this point, we have implemented the capability of the back-end service to return to the front page, with the following effect

log

Currently we can just output each request to the server console, but once our service terminates or the console empties, the history of the request becomes inaccessible. So we can take advantage of node.js’s file read and write capabilities (see # Node.js Development Essentials) to store each request record in a server file.

The core implementation logic is as follows

function log(msg) {
  const logMsg = msg + '\n';
  const logPath = `. /${logName}`;
  const existLog = fs.existsSync(logName);
  if (existLog) {
    fs.appendFileSync(logPath, logMsg);
  } else{ fs.createWriteStream(logPath).write(logMsg); }}Copy the code

We will determine whether the server already has a log file. If so, we will simply append records to the log file. If not, we will create a write data flow and write records to the log file.

This operation creates a log file so that the next time the server starts, if it reads an existing log file, it writes directly to the file.

To use the “logging” function we only need to call it where needed

app.get('/'.(req, res) = > {
  const logMsg = `${req.url} requested at, The ${new Date()}`
  console.log(logMsg);
  
  log(logMsg);

  res.sendFile('index.html', {
    root: '. '
  });
})
Copy the code

In this way, we have achieved the logging function.

The front page

Page submission data

Then, we implemented some basic common functions in the front page, such as adding notes. The core logic is as follows

<div id="app">
<div class="history">
  <div class="history-item" v-for="(item, index) of history" :key="index">{{item}}</div>
</div>
<div clas="item-add-wrap">
  <input v-model="newItem" type="text">
  <button @click="add">add</button>
</div>
</div>
<script>
var app = new Vue({
  el: '#app'.data: {
    history: [].newItem: ' '
  },
  methods: {
    add() {
      const api = 'http://127.0.0.1:8090/add';
      const params = {
        content: this.newItem
      }
      const option = {
        method: 'POST'.body: JSON.stringify(params),
        headers: new Headers({
          'Content-Type': 'application/json'})}console.log('option', option)
      fetch(api, option).then(res= > {
        if (res && res.status === 200) {
          console.log('res', res)
        }
      })
    }
  }
})
</script>
Copy the code

The fetch method of the browser is used for network data request, where the first parameter is the address of the interface to be requested and the second parameter is the basic configuration of the network request. For example, we declare the request form as JSON and send the POST request with the parameter as a JSON string. The page appears as follows

The back end receives data

We then need to add the processing of the Add method to the back-end service as follows

app.use(express.json());

function storeData(content, callback) {
  const data = content + '\n';
  const existed = fs.existsSync(dataName);
  const dataPath = `. /${dataName}`;
  if (existed) {
    fs.appendFileSync(dataPath, data);
  } else {
    fs.createWriteStream(dataPath).write(data);
  }
  callback();
}

app.post('/add'.(req, res) = > {
  const params = req.body;
  const logMsg = `POST ${req.url} | The ${JSON.stringify(params)} | The ${new Date()}`
  console.log(logMsg);

  log(logMsg);

  if (params) {
    const { content = ' ' } = params;
    if (content) {
      storeData(content, function() {
        res.json({
          errno: 0.data: null})})}else {
      res.json({
        errno: 400.errmsg: 'Content is empty'}}}})Copy the code

In the first line, we use the built-in middleware function of Express to jsonize the parameters in the network request, so that we can successfully parse out the JSON parameters passed from the page in the request body of post method.

In addition, we have defined the storeData function to store the data submitted by the page to a file on the server side. This process is to persist the data.

Finally, we use the server instance to listen for the POST method, and with the specified API name add, we receive requests from the page and can process and respond to the incoming data.

Page query data

Now that we can store data to the server through the page, we need to add the ability to read existing data from the server within the page, as follows

var app = new Vue({
  el: '#app'.data: {
    history: [].newItem: ' '
  },
  created() {
    this.getData();
  },
  methods: {
    add() {
      const api = 'http://127.0.0.1:8090/add';
      const params = {
        content: this.newItem
      }
      const option = {
        method: 'POST'.body: JSON.stringify(params),
        headers: new Headers({
          'Content-Type': 'application/json'
        })
      }
      fetch(api, option).then(res= > {
        if (res && res.status === 200) {
          res.json().then(jsonRes= > {
            if (jsonRes.errno === 0) {
              this.getData();
              this.newItem = ' '; }})}})},getData() {
      fetch('http://127.0.0.1:8090/list').then(res= > {
        if (res && res.status === 200) {
          res.json().then(jsonRes= > {
            if (jsonRes.errno === 0 && jsonRes.data) {
              this.history = jsonRes.data; }})}})Copy the code

The server responds to the data query

function readData(callback) {
  const dataPath = `. /${dataName}`;
  const existed = fs.existsSync(dataPath);
  let result;
  if (existed) {
    const content = fs.readFileSync(dataPath);
    result = content.toString();
  }
  callback(result);
}

app.get('/list'.(req, res) = > {
  const logMsg = `GET ${req.url} | The ${new Date()}`
  console.log(logMsg);
  
  log(logMsg);

  readData(data= > {
    if (data) {
      const jsonData = data.split('\n').filter(str= > str.length);
      res.json({
        errno: 0.data: jsonData
      })
    } else {
      res.json({
        errno: 400.errmsg: 'No content'})}})})Copy the code

At this point, we have completed the page’s ability to read data from the server and optimized the interaction with the newly added content by reading the latest content and clearing the input field. The overall effect is as follows:

Client display

Server display

conclusion

In this way, we have developed a complete application that can query server data and add data to the server based on Node.js as the core, which makes use of Node.js file reading and writing ability and mature Node.js framework — Express. A real Web application might also need to do the following:

  • Add routing middleware on the server
  • Server-side data persistence method encapsulation
  • Server-side logging middleware encapsulation
  • Front-end network request method encapsulation
  • Add routing middleware to front-end page
  • .

This is all based on the basic prototype we implemented in this example, so if you want to start your Node.js project, follow this example step by step and continue to improve.