Prerequires

  • familiar with HTML CSS js promise node.js npm
  • if you don’t familiar with Vue site or egg site you can see the official docs then can come back to read this article later
  • the most important thing is: try it by yourself !

What you will lean in this article :

  1. build a frontend ui with vue-cli vue vuex vue-router
  2. build a backend server with egg.js and cors plugin template engine plugin (like egg-view-nunjucks) and sequelize(MySQL ORM) plugin
    • handle CURD with different categories like(users articles videos etc)
    • add egg router.resoure to simplify the router path
    • serve a static file in egg
    • deploy a backend server(todo later)
  3. handle cors
  4. send axios request in frontend
  5. build a backend cms with elementUI : admin can see update modify some resources in this system.
  6. build a pc/web page to show a demo website,can show articles videos user can also login logout etc

What is egg.js

Egg is born for building Enterprise Application and Framework, we hope Egg will give birth to more application framework to help developers and developing teams reduce development and maintenance costs.

The fetaures of egg.js

  • Provide capability to customizd framework base on Egg
  • Highly extensible plugin mechanism
  • Built-in cluster
  • Based on Koa with high performance
  • Stable core framework with high test coverage
  • Progressive development

How to init?

npm init egg --type='simple'
Copy the code

if you run this command in a not empty dir . egg will tell you to select a new folder . then you can create a empty folder by yourself.

after install the dependiences. the project structor may like this below :

then cd server && npm run dev to start the server.after doing this . you can start a local server.

When you visit this url http://127.0.0.1:7001/ the egg can return a message for you, the message content is defined in the homeController like this :

//app/controller/home.js
"use strict";

const Controller = require("egg").Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    // change the return value 
    ctx.body = "hello alex from egg"; }}Copy the code

serve a static file

you can also create a demo.html file in /app/public folder. then you can see the file content when you enter this url in the browser http://127.0.0.1:7001/public/demo.html

router

- MVC (View --- Model --- Controller)
- View : show data 
- Model: handle datas 
- Controller : handle user's input 
Copy the code

handle api service

add a new Controller then add the controller to router with a path name like/student. then visit this url http://127.0.0.1:7001/student in the browser, will see the response is student the code is show in below:



//app/controller/student.js
"use strict";

 const Controller = require("egg").Controller;

 class StudentController extends Controller {
   async index() {
     const { ctx } = this;
     ctx.body = "student"; }}module.exports = StudentController;


//app/router.js
"use strict";
 
 / * * *@param {Egg.Application} app - egg application
  */
 module.exports = app= > {
   const { router, controller } = app;
   router.get("/", controller.home.index);
   
   //add this line below
   router.get("/student", controller.student.index);
 };
Copy the code

we can konw that controller can also used in router.js to handle different path user visited in the frontend.

get query params and post datas in backend

  • get GET method query params use this.ctx.request.query
class StudentController extends Controller {
  async index() {
    const { ctx } = this;
    console.log(ctx.request.query); ctx.body = ctx.request.query; }}/ / in a browser, you can visit this url to test at http://127.0.0.1:7001/student? age=18&sex=man
//the response is 


/ / http://127.0.0.1:7001/student? age=18&sex=man
{
  "age": "18"."sex": "man"
}
Copy the code
  • get GET method id use this.ctx.params
//add new path in router.js
router.get("/student/:id", controller.student.getId);

//add a new method in controller student.js
async getId() {
    const { ctx } = this;
    let id = ctx.params.id;
    ctx.body = 'The id of the current requested path is${id}`;
}



/ / http://127.0.0.1:7001/student/345
// Response is The id of the current request path is 345

Copy the code
  • get POST method use this.ctx.request.body

"use strict";

const Controller = require("egg").Controller;

let students = ["alex"."tom"."jony"];
class StudentController extends Controller {
  async index() {
    const { ctx } = this;
    const query = ctx.request.query;
    ctx.body = query;
  }

  async getId() {
    const { ctx } = this;
    let id = ctx.params.id;
    ctx.body = 'The id of the current requested path is${id}`;
  }

// add this two methods 
  async createStudentPage() {
    const { ctx } = this;
    const formHtml = '
      
; ctx.body = formHtml; } async addStudent() { let student = this.ctx.request.body; this.ctx.body = student; }}module.exports = StudentController; //router.js "use strict"; / * * *@param {Egg.Application} app - egg application */ module.exports = app= > { const { router, controller } = app; router.get("/", controller.home.index); router.get("/student", controller.student.index); router.get("/student/:id", controller.student.getId); //add this two routes router.get("/createStduent", controller.student.createStudentPage); router.post("/createStduent", controller.student.addStudent); }; Copy the code

after we sumit the form we will encounter a error : ForbiddenError in /createStduent invalid csrf token because egg has a csrf validation for the post mehthod . so we need to edit the config to omit the validation now.

Here we do the config temporarily in config/config.default.js for an example:

config.security = {
    csrf: {
      enable: false}};Copy the code

egg RESTful Style URL Definition to aviod define multi routes for one resource

before use router.resources:


//app/router.js
    //students router logic 
   router.get("/student", controller.student.index);
   router.get("/student/:id", controller.student.getId);
   router.get("/createStduent", controller.student.createStudentPage);
   router.post("/createStduent", controller.student.addStudent);

  
Copy the code

after use router.resources : we can handle this just by add one line!

//app/router.js
  // router.get("/student", controller.student.index);
  // router.get("/student/:id", controller.student.getId);
  // router.get("/createStduent", controller.student.createStudentPage);
  // router.post("/createStduent", controller.student.addStudent);

  router.resources("students"."/students", controller.student);
Copy the code

so we need to change to student controller to match the routers

student CURD demo :

  • http://127.0.0.1:7002/students get students list
  • http://127.0.0.1:7002/students/new get add students form page

"use strict";

const Controller = require("egg").Controller;

/ / define post datas here path: http://127.0.0.1:7002/students
let students = ["alex"."tom"."jony"];
class StudentController extends Controller {

//get method 
  async index() {
    const { ctx } = this;
    ctx.body = students;
  }

  / / show the add student post page path: http://127.0.0.1:7002/students/new
  async new() {
    const { ctx } = this;
    const formHtml = '
      
`
; ctx.body = formHtml; } //add a new student post method // enter the url will send a get request // click reload will repost async create() { let student = this.ctx.request.body; students.push(student.studentName); // remote this step // this.ctx.body = "add stducent success"; //redirect to student list this.ctx.redirect("/students"); } // /students/:id delete method async destory() {} //update put method /students/:id async update(){}}module.exports = StudentController; Copy the code

plugins

template engine npm i egg-view-nunjucks --save

  • templates for rendering
  • new.tpl github hackernews egg demo
npm i egg-view-nunjucks --save

then enable it 

// config/plugin.js
'use strict';

/ * *@type Egg.EggPlugin */
module.exports = {
  // had enabled by egg
  // static: {
  // enable: true,
  // }
  nunjucks = {
    enable: true.package: 'egg-view-nunjucks'}};//and also config 
// config/config.default.js
exports.keys = <YOUR_SECURITY_COOKE_KEYS>;
// add view's configurations
exports.view = {
  defaultViewEngine: 'nunjucks'.mapping: {
    '.tpl': 'nunjucks',}}; then change the home controller
"use strict";

const Controller = require("egg").Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    // ctx.body = "hello alex from egg";
    //techs is the data the index.html will show 
    await ctx.render("index", { techs: ["js"."html"."css"]}); }}module.exports = HomeController;


crate a new folder `view` in app  and a index.html and for loop the datas pass in the home controller 

//app/view/index.html<! DOCTYPE html><html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>techs :</h1>
    <ul>
      {% for tech in techs %}
      <li>{{ tech }}</li>
      {% endfor %}
    </ul>
  </body>
</html>


//more detail can reference egg hacknews demo https://sourcegraph.com/github.com/eggjs/examples@master/-/blob/hackernews/app/controller/news.js


Copy the code

The result is http://127.0.0.1:7001/ the router is/to homeController:

//app/router.js

"use strict";

/ * * *@param {Egg.Application} app - egg application
 */
module.exports = app= > {
  const { router, controller } = app;
  // / => home controller
  router.get("/", controller.home.index);
  router.resources("students"."/students", controller.student);
};

//home.controller app/controller/home.js

"use strict";

const Controller = require("egg").Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    // ctx.body = "hello alex from egg";
    await ctx.render("index", { techs: ["js"."html"."css"]}); }}module.exports = HomeController;



Copy the code

use template result in frontend :

cors

when front end (8080 port) send request to backend (7001) the browser will have a error

  • vue cli create a new project call clint cd clinet npm run serve then change the app.vue the project folder looks like this :
/client (vue.js forentend folder) in 8080 port
/server (egg.js backend server floder) in 7001 port
Copy the code
  • add axios in client foldernpm i axios -S

// client/src/app.vue

<template>
  <h1>{{message}}</h1>
</template>

<script>
import axios from "axios";
export default {
  name: "App".data() {
    return {
      message: "hello vue"
    };
  },
  created() {
    axios.get("http://127.0.0.1:7001/test").then(
      res= > {
        console.log(res);
        this.message = res.data;
      },
      err= > {
        console.log(err); }); }};</script>


Copy the code

When we visit this url in frontend we will encounter this error Access to XMLHttpRequest at ‘http://127.0.0.1:7001/test’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

so we need to config the cors in our backend

how to enable cors in egg.js ?

cd server/ && npm i -S egg-cors 


//plugin.js

  cors: {
    enable: true.package: "egg-cors"
  }
  
//config.default.js

  config.cors = {
    //allow all the origin
    origin: "*".allowMethods: "GET,HEAD,PUT,POST,DELETE,PATCH"
  };
  
 
 
Copy the code

after configing this we vist the frontend again will see the success result :

build a CURD demo with egg and vue

frontend vue app.vue

<template>
  <div>
    <h1>{{message}}</h1>
    <ul>
      <li v-for="(stu, index) of students" :key="index">{{stu}}</li>
    </ul>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App".data() {
    return {
      message: "hello vue".students: []}; },created() {
    //test cors
    / / axios. Get (" http://127.0.0.1:7001/test "). Then (
    // res => {
    // console.log(res);
    // this.message = res.data;
    / /},
    // err => {
    // console.log(err);
    / /}
    // );

    //get students
    axios.get("http://127.0.0.1:7001/students").then(
      res= > {
        console.log(res);
        //why use res.data because we can check the result data in chrome devtools
        this.students = res.data;
      },
      err= > {
        console.log(err); }); },methods: {}};</script>


Copy the code

backend student api js

the result islocalhost:8080 : the frontend show the backend data successfully

  • add post method in frontend

//client/src/app.vue

<template>
  <div>
    <h1>{{message}}</h1>
    <form @submit.prevent="postStudent">
      <input type="text" v-model="nowInputedSutdentName" />
      <button>add a student name</button>
    </form>
    <ul>
      <li v-for="(stu, index) of students" :key="index">{{stu}}</li>
    </ul>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App".data() {
    return {
      message: "hello vue".students: [].nowInputedSutdentName: ""
    };
  },
  created() {
    //test cors
    / / axios. Get (" http://127.0.0.1:7001/test "). Then (
    // res => {
    // console.log(res);
    // this.message = res.data;
    / /},
    // err => {
    // console.log(err);
    / /}
    // );

    //get students
    this.getStudent();
  },
  methods: {
    getStudent() {
      axios.get("http://127.0.0.1:7001/students").then(
        res= > {
          console.log(res);
          this.students = res.data;
        },
        err= > {
          console.log(err); }); },postStudent() {
      axios
        .post("http://127.0.0.1:7001/students", {
          studentName: this.nowInputedSutdentName
        })
        .then(() = > {
          //reset this.nowInputedSutdentName
          this.nowInputedSutdentName = "";
          this.getStudent(); }); }}};</script>


Copy the code

//the backend sutdent api is 

"use strict";

const Controller = require("egg").Controller;

//define post datas here
let students = ["alex"."tom"."jony"];
class StudentController extends Controller {
  async index() {
    const { ctx } = this;
    ctx.body = students;
  }

  //add a new student post method
  // enter the url will send a get request
  // click reload will repost
  // post method to add new student
  async create() {
    // {studentName : "xxx"}
    let student = this.ctx.request.body;
    students.push(student.studentName);
    // remote this step
    // this.ctx.body = "add stducent success";
    //redirect to student list
    this.ctx.redirect("/students");
  }

  // /students/:id delete method
  async destory() {
    let { ctx } = this;
    let id = ctx.params.id;
    students.splice(id, 1);
    ctx.body = "delete success";
  }

  //show add student post page
  async new() {
    const { ctx } = this;
    const formHtml = '
      
`
; ctx.body = formHtml; } //update put method /students/:id async update() {} // async getId() { // const { ctx } = this; // let id = ctx.params.id; // ctx.body = 'The id of the current request path is ${id}'; // } // async createStudentPage() { // const { ctx } = this; // const formHtml = ` //
// < form type="submit" value=" submit" > // / / `; // ctx.body = formHtml; // } // async addStudent() { // let student = this.ctx.request.body; // console.log(student); //{ studentName: 'wewwewe' } // students.push(student.studentName); // // this.ctx.body = "add stducent success"; // //redirect to student list // this.ctx.redirect("/student"); // } } module.exports = StudentController; Copy the code

the result is after user post a new student name. the list will get the students againg to show the newest datas :

next article we will talk about how to use middleware data persistence service deploy etc

middlewares

data persistence

- use `sequelize`(Object Relational Mapping ORM framework)  to interact with mysql. you don't need to write pure sql statement by yourself .
Copy the code

service

deploy

The last

  • Source code fe backend- Egg
  • The front-end GuanYu
  • “Nuggets of gold” pull you into the daily front-end early learning group, and friends to play front-end.
  • Thank you for reading