As a front-end development engineer, I often struggle with whether to learn back-end development to become a real full stack. Of course, node.js is the first choice for back-end development, because there is no need to learn a new development language, but Node.js seems to be difficult. Because the beginning of the back-end development of the most difficult side of the show, such as module and memory management, although for developers who have higher pursuit, these principles must be mastered more in-depth the better, but for ordinary back-end business development, in fact, only need to be able to achieve business can be.

Today we will start with the simplest todo List project, uncover the mystery of backend development, help you on the road to full stack development engineer. Why todo List? Because it is the most representative project and the most familiar, it is the least difficult to accept. Although it is small, it has all the five organs. It contains the necessary skills for front and back end development. This article is a complete can include the front and back end of the code can run directly, the effect is as follows:

This tutorial is suitable for developers with a basic front-end knowledge of Node.js (NPM installation and reference modules are enough)

We’re going to start from scratch and work our way through the code

  1. The first step is to implement a simplified, purely static version of Todolist
  2. The second step is to set up a background interface and mock the data so that the page will display properly first
  3. Third, design the database and change the test data to a real interface

In fact this is also the real project development process, present Taiwan before and after development is separation, Taiwan before and after the development in accordance with the first interface to define their respective development code, front-end using static data simulation (mock) can accomplish more than 99%, the functional development of the back-end is pure data, your unit tests, all development, Taiwan before and after docking deployed in test environment.

Step from head to toe, front to pure static version of Todolist

To keep the tutorial simple, the front-end code is implemented in jquery. First create a blank file named index.html, introduce jquery into the page, and add a UL element as a container. Add a text box and add button with the following HTML code:

<ul id="list"></ul>
<input type="text" id="title" placeholder="Enter your to-do list">
<button id="btn_add">add</button>
Copy the code

Now there is no background database, data can only be defined with a static array, each record has ID,title,status three attributes, respectively, indicating the item number,title, whether it has been completed. Define an array to store the test data, and then use forEach to loop the array, splicing the HTML string, and finally merge the HTML to generate DOM. This example uses ES6 template string, convenient writing and demonstration.

The code is as follows:

function render(){
        var html="";
        var result=[
        {id:1.title:"hello".status:0},
        {id:2.title:"hello".status:0}]; result.forEach(function(item){
            var checked=item.status>0?"checked":"";
            var li=`<li itemId="${item.id}">
                <input type="checkbox" ${checked}>
                <input type="textbox" value="${item.title}"> < a href =" javascript: "class =" delete "> delete < / a > < / li > `html+=li; $(})"#list").html(html);
       
    
}
$(function (){
    render();
})
Copy the code

Run index.html to display the page successfully.

In the second iteration, mock data was used to connect the real backend interface

Well, the static page rendering has been written, and it’s time to take the first step in the background development. Change the static data to call the Ajax interface, and modify the render function slightly

function render(){
    $.post("http://localhost/todo/list", {},function (result){
        var html="";
        result.forEach(function(item){
            var checked=item.status>0?"checked":"";
            var li=`<li itemId="${item.id}">
                <input type="checkbox" ${checked}>
                <input type="textbox" value="${item.title}"> < a href =" javascript: "class =" delete "> delete < / a > < / li > `html+=li; $(})"#list").html(html);
    });
}
Copy the code

However, the problem is that there is no background server, and the local HTML file will encounter cross-domain error when sending ajax request. Those of you who have done local development must know that ajax will generate cors exception, because the local file is opened with the file:// protocol. If you visit the page of http://protocol, Cross-domain errors may occur because of the same origin policy. The same origin policy requires:

  1. The agreement is the same
  2. Domain name is the same
  3. The same port

The console error message is as follows:

This problem is actually a way to solve, as long as the background interface to achieve cORS cross-domain, can be unimpeded call, so that your code does not have to deploy to the remote server, you can call the remote server interface, very convenient. So let’s build a HTTP service to implement CORS, boy! We don’t use Express today, and we don’t use KOA, because they don’t mock, and cross-domains require a lot of extra code. So I wrapped one of my own, webContext, github address: github.com/windyfancy/…

NPM can be installed, we create a directory on the hard disk, such as todo_SAMPLE, for storing background projects, switch to this folder, run NPM install installation

npm install --save webcontext
Copy the code

Create app.js in the root directory of your project to start the HTTP service. Write two lines of code:

app.js

const WebContext = require('webcontext');
const app = new WebContext();
Copy the code

Run node app.js to start the HTTP service, and then visit http://localhost/ to output hello message, indicating that the HTTP service has been set up successfully!

Next, create a todo subdirectory in the project root /service directory. A list.ejs blank file will be created in the todo directory with the following directory structure

|-- service                          
|     |--todo
|         |--list.ejs
Copy the code

Ejs, which is actually an EJS template file, can also store JSON

[
    {id:1,title:"hello",status:0},
    {id:2,title:"hello",status:0}
]
Copy the code

Now use the browser to access directly: http://localhost/todo/list, has lost the contents of the file can be directly, but with the local index. The HTML call this interface is still return cross-domain wrong, for the sake of safety, webcontext cross-domain configuration is off by default, We open the web.config.json(automatically generated when first run) in the root directory of the project and change the allowOrigin field value in the CORS property to * to enable cross-domain

"cors": {"allowOrigin":"*"
    }
Copy the code

Now access the local index.html and find that the request was successful. The HTTP response header correctly outputs the CORS cross-domain information.

Pre-war preparation work, design mysql database

The previous step completed the construction of the HTTP interface in the background, and used mock static data to verify that todolist loading function is normal. Finally, the exciting database development process has arrived. I am a little excited about being a CURD boy, stepping on the top of my life and serving as the CEO.

Just as the name implies, database is used to store data. At present, the mainstream database is relational database, such as mysql, Oracle, SQL Server, etc., which stores data of one table by row structure, just like an Excel table, and each table is an independent sheet. Let’s convert the static JSON data into a table like this:

id title status
1 hello 0
2 world 0

Mysql > alter table todo_list select * from todo_list;

  • Id: Indicates the number of the to-do item. The value type is represented as an int in the database. It is the unique identification of each record, namely the primary key
  • Title: Indicates the name of the to-do item. It is a string, represented in the database as a vARCHAR variable length string
  • Status: Indicates the status of the to-do item. It is a Boolean, and for extension we define it as a data type, also as an int

Let’s create a table, first must install mysql

On the official website https://www.mysql.com/downloads/ download and install the complete installation. Once installed, you can use your own Workbench to connect to your database. Connect with the password you initially set when installing:

Create a new table in todo_DB. Create a new table in todo_DB. Create a new table in todo_DB.

You can also create tables using SQL code:

CREATE TABLE `todo_list` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(45) DEFAULT NULL,
  `status` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
)
Copy the code

After creating the database table, we try to connect to the database using code. The WebContext framework already integrates the database connection, and just needs to be configured to automatically connect to the database when app.js starts, without writing any extra code

Modify web.config.json to set database connection parameters

"database": {"host":"127.0.0.1"."port":"3306"."user":"root"."password":"Your password"."database":"todo_db"
    } 
Copy the code

Mysql workbench is required to execute the following code to connect to the database using Node.js: ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘your password ‘;

Ok, now re-run Node app.js to automatically connect to the database

In the third iteration, write database query and save interface to achieve CURD

After the database connection is successful, we will test whether the query and write data can be normal, since the database is still empty, we will start by inserting data.

What is the CURD?

It represents the four basic operations of database creation (Create), Update (Update), read (Retrieve) and Delete (Delete). For relational database, can through SQL (Structured Query Language (Structured Query Language SQL) to write code to support the four basic operations, corresponding to the insert, update, select, delete four statements.

Webcontext has to insert, update, select, delete the encapsulation, so for the simple read and write operations, do not need to write the original SQL statement.

insert

In the project directory to create a js file, / service/todo/add js, webcontext realized the page address automatic routing, no manual routing code, when visiting http://localhost/service/todo/add, The onLoad method in the path (/service/todo/add.js) is automatically called, and the code for add.js is as follows:

module.exports= {
    async onLoad() {
        await this.database.insert("todo_list", {title:"hello".status:0});
        this.render({code:"OK"}}})Copy the code

There are only four lines of code, so let’s go through them line by line

  1. The module exports: Export this object so that the WebContext framework can reference it automatically. This object will automatically inherit from the Context class. You don’t have to write any extend or prototype code, The WebContext framework implements the functions of automatically adding routes and inheriting files in the /service directory. Node.js implements server page technology similar to.php,.jsp, automatic routing
  2. Async onLoad function: First, this is an asynchronous function, since line 3 is “await” the database, so async keyword must be added here, and onLoad is an event, or a callback function, which means that the function code is executed after receiving an HTTP request in the background.
  3. This.database. insert, since the current file automatically inherits from the Context class, you can use this to get request, response, and database, which encapsulates basic curd operations. The first parameter of the insert method is the name of the table to be inserted, and the second parameter is an object that represents the name and value of the field to be inserted.
  4. This.render outputs the incoming string or object to the HTTP response. Passing an object will automatically stringify.

Write, we come to visit http://localhost/service/todo/add, and then use the mysql workbench check the database, data has been successfully put in storage. This.request. Data [“title”] specifies the title field in the POST form.

await this.database.insert("todo_list", {title:this.request.data["title"].status:0});
Copy the code

select

Now that we have data in the database, we need to change the interface for the mock request to read real data. Delete list.ejs and create a new list.js file in /service/todo:

/service/todo/list.js

module.exports= {
    async onLoad() {
        var result=await this.database.select("todo_list")
        this.render(result); }}Copy the code

Without too much explanation, the same as the previous page, the only change you need to make is to change the insert method to select method, this example is relatively simple, just need to pass a table name. In fact, the SELECT method has been packaged very powerful, support a variety of WHERE conditions, sorting, database layer paging, multiple table join, etc., will not be expanded.

update

/service/todo/update.js

module.exports= {
    async onLoad() {
        await this.database.update("todo_list".this.request.data)
        this.render({code:"OK"}}})Copy the code

Update and insert are written almost the same. For a bit of freshness, use the this.request.data form as the second parameter. This saves a lot of code, but your code will return an error if someone else maliciously posts invalid data.

delete

/service/todo/delete.js

module.exports= {
    async onLoad() {
        await this.database.delete("todo_list", {id:this.request.data["id"]})
        this.render({code:"OK"}}})Copy the code

Delete with only the table name and ID parameters

Optimization: Rewrite the route

Ok, now that the CURD operation is complete, the business code is only 8 lines, but it could be less, because mysql supports replace into statements and inserts and updates can be combined into one. Even with some parameter validation, the amount of code is very small. Now after the separation of front and back, the background development of database business is really much simpler than the front end. In addition to some SQL statements with multiple table connections and statistics that are difficult to write, other data operation codes are highly repetitive. However, since the popularization of VUE and React in the front end, the development threshold has been reduced a lot.

This project is relatively simple, there are only a few lines of code in the background, but it needs to be split into 4 files. Can we simplify it a little bit? The answer is yes, webContext supports functions that can be directly mapped to JS files by expanding the name! Request to http://localhost/todo.list, for example, will direct call/service/todo list of js () method. Let’s delete the todo directory code we just wrote, re-implement it, and organize it into a file.

/service/todo.js

module.exports= {  
  onRequest() {},
  async list() {
    var result=await this.database.select("todo_list")
    this.render(result); 
  }, 
  async add() {
      await this.database.insert("todo_list", {title:this.request.data["title"].status:0});
      this.render({code:"OK"})},async update() {
      await this.database.update("todo_list".this.request.data)
      this.render({code:"OK"})},async delete() {
    await this.database.delete("todo_list", {id:this.request.data["id"]})
    this.render({code:"OK"}}})Copy the code

We in the address bar, visit http://localhost/todo.list test, success! Data can be returned normally.

In the fourth iteration, add add, save, and remove front-end code

Now the background interface has been completed, but the front-end code of adding, modifying and deleting has not been realized, we use the way of event delegation to achieve, the front-end code is very simple and will not be described in detail, the complete code is as follows.

function saveItem(target){
    var li=$(target).parents("li");
        var id=li.attr("itemId")
        var title=li.find("input[type=textbox]").val();
        var status=li.find("input[type=checkbox]").prop("checked")?1:0;
        $.post("http://localhost/todo.update", {id:id,title:title,status:status},function (res){
            if(res.code="OK"){ render(); }}); }function deleteItem(target){
    var id=$(target).parents("li").attr("itemId")
    $.post("http://localhost/todo.delete", {id:id},function (res){
        if(res.code="OK"){ render(); }}); } $("#list").on("change"."input[type=textbox]".function (e){ saveItem(e.target); $(})"#list").on("click"."input[type=checkbox]".function (e){
    saveItem(e.target)
})
$("#list").on("click".".delete".function (e){
    deleteItem(e.target)
})
$("#btn_add").click("click".function (e){
    $.post("http://localhost/todo.add", {title: $("#title").val()},function (res){
        if(res.code="OK"){ render(); }}); })Copy the code

Clean up the battlefield and migrate static files to deploy to HTTP servers

Now that all the code is complete, you can’t always store the HTML and JS files locally. Webcontext already has built-in static file service. Just store the local index.html and jquery.js files in the root /client directory of the site. To visit http://localhost/index.html, you can visit here. Node.js implements server page technology similar to.php and.jsp, automatic routing

Finally, since we are all in the same HTTP path, we can change the absolute path of $. Post to a relative path in the code.

conclusion

This article uses 8 lines of code to implement the Todolist background interface. The reason why so little code is due to the design philosophy of WebContext: convention is better than configuration, default configuration is better than manual configuration, configuration is better than coding, and the user experience of the developer is put first.

For example:

  1. It implements server page technology, no need to add routing code, because the page address itself already contains routing information, a file to handle a request, also easy to decouple, can also handle multiple requests in a file, Todo.list calls the list method in todo.js directly from the URL, without modifying the routing file every time you add a request path.
  2. The configuration of the database connection string, automatic connection to the database, a line of code to achieve CURD, also achieved the ORM database entity mapping function, the realization of the need not to write SQL can operate the database.
  3. The default configuration files are automatically generated, and in most cases you do not need to modify these configurations.
  4. For forms, uploads, json, everything is automatically parsed into objects, and you can even write the form data directly to the database, eliminating redundant code for intermediate conversion parameters

The structure design of WebContext refers to the elegant design of ASP.NET. Using a Context object as a container, it is very convenient to call request, Response,session and other HTTP request processing operations. Even in EJS templates, You can also use this to get the context for HTTP processing or database operations directly. And extended many functions, such as using the database object to operate the database, logger object to write logs and so on. The main structure is as follows:

Although provides so many functions, its code is very concise, only about one thousand lines, it is easy to understand, although it is not spring Bai Chun, strong, is not high, if you want to know is how to cast a web framework, might as well to read the source code, if you think useful, please don’t grudge your star, it is still a small tree, I need your support.

Github address: github.com/windyfancy/…

All the code of this article has also been uploaded to Github, in the WebContext_examples project, students who need to check.