Koa start

Koa is a new Web framework, built by the same people behind Express, that aims to be a smaller, more expressive, and more robust cornerstone of web application and API development. By making use of async functions, Koa helps you discard callback functions and greatly enhances error handling. Koa does not bundle any middleware, but rather provides an elegant way to help you write server-side applications quickly and happily.

The above is excerpted from the KOA website

const http = require('http');
const server = http.createServer((req, res) = > {
  res.writeHead(200);
  res.end('hello world');
})
server.listen(3000.() = > console.log('server start at 3000'));
Copy the code

This is an example of Node natively creating an HTTP server. With this introduction, we can guess what koA should look like.

  • She should haveasync/awaitIs asynchronous operation;
  • She should have error handling mechanisms;
  • She should havehttpA more elegant approach to modules;

Look at the first example from KOA; Of course you need to download it first (make sure your Node is above 7.6.0 to support async/await operations).

npm install koa --save-dev
Copy the code
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
  ctx.body = 'hello world'
});
app.listen(3000.() = > console.log('server start at 3000'));
Copy the code

The code above is sample code for KOA; You can see that in this code

  • asyncAsynchronous though it doesn’t appearawait
  • req resTurned out to bectxThat iscontextcontext
  • Error handling hasn’t been seen yet, it says later

Summary: The bottom layer is still HTTP through a variety of encapsulation is more comfortable, of course, this is only the surface, certainly still need to understand;

Now, app is an instance of Koa, so what’s in that instance? I’m going to skip over the instance properties here and just take a look at a couple of methods that I think are important

  • App.use () : adds the given middleware method to the application, and this method returns this, which means it can be called chained

  • App.callback () : returns a callback that applies to http.createserver (), which is another way to create a server. For example, create multiple servers 😆

  • App.listen () : Koa applications are not 1-to-1 representations of HTTP servers. One or more Koa applications can be installed together to form a larger application with a single HTTP server.

    const Koa = require('koa');
    const app = new Koa;
    app.use(async ctx => {
        ctx.body = { msg: 'hello koa' };
    });
    app.listen(3000) // This is the createServer syntax sugar
    
    // -------------------------------------------------------
    
    const http = require('http');
    const Koa = require('koa');
    const app = new Koa()
    
    app.use(async ctx => {
      ctx.body = { msg: 'hello koa' };
    });
    
    http.createServer(app.callback()).listen(3000);
    Copy the code
  • App. on(‘error’, function) : Listen for error events on app for further unified handling and centralized management of these errors.

    app.on('error'.async (err, ctx) => {
      console.log('err', err)
    })
    app.use(async ctx => {
      // ReferenceError: tx is not defind
      ctx.body = { msg: tx }; 
    });
    Copy the code

Middleware Middleware

Imagine that we now want to implement a login function; The main business must be the user name, password and then compare the database live;

/ / pseudo code
app.use(async ctx => {
  const user = 'admin'
  const pwd = '123456'
  const { username, password } = ctx.query
  if (user === username && pwd === password) {
    ctx.body = { status: 200.msg: 'success'}}});Copy the code

The code logic looks fine, but returns a successful code if it is equal; Now there is a new requirement, which is to add logs to the program when comparing

At this point, we actually need the second middleware parameter next

When one middleware calls next(), the function pauses and passes control to the next middleware defined. When there are no more middleware executions downstream, the stack expands and each middleware resumes performing its upstream behavior.

How do you understand that? So let’s optimize the code above

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  ctx.state.user = 'admin'
  ctx.state.pwd = '111'
  console.log('about to start comparing user:${ctx.state.user} pwd: ${ctx.state.pwd}`)
  await next()
  console.log('username:${ctx.query.username}, password: ${ctx.query.password}`)
})

app.use(async ctx => {
  const { username, password } = ctx.query
  if (ctx.state.user === username && ctx.state.pwd === password) {
    ctx.body = { status: 200.msg: 'success'}}}); app.listen(3000.() = > console.log('server start at 3000'));
Copy the code

If you look at the results of the console it makes a lot of sense in understanding the paragraph above; So far can finally use this rotten map out 😂 is really in use, too classic!!

I if pure put a graph to you too sorry you, in case you do not understand my praise did not 😆, so I again specialized to you steal a more straightforward

When a middleware callnext()The function pauses and passes control to the next middleware defined. When there are no more middleware executions downstream, the stack expands and each middleware resumes performing its upstream behavior.

Context Context

app.use(async ctx => {
  const username = ctx.query.name
  ctx.body = { code: 200.msg: 'success'}})Copy the code

I believe there must be a question in the above CTX, why CTX has the ability to operate both request and response; This is certainly intentional, but also for the convenience of our development, for example, you can also do so

app.use(async ctx => {
  const username = ctx.request.query.name
  ctx.response.body = { code: 200.msg: 'success'}})Copy the code

Koa has helped us put some of the most common methods in the context of CTX. What are they? I also need to go to the official website to check 😂. I think there are too many and there is no need to list them. You can go to the official website to take a look, but besides request and response, there are of course some attributes and methods of CTX. Let’s list a few that I think are important

  • Ctx. request: indicates the REQUEST object of Koa

  • Ctx. response: Koa’s response object

  • Ctx. state: the namespace recommended by Koa for middleware transmission

  • Ctx. throw: Koa’s method of manually throwing exceptions. Http-errors can also be used to create errors

    ctx.throw(404.'NotFound');
    Copy the code
  • Ctx. assert: Throws an exception when an asserted value is absent or an error occurs

    // This error occurs if ctx.state.name is not defined
    ctx.assert(ctx.state.name, 401.'user is not defined');
    Copy the code
  • Ctx.cookie. get CTx.cookie. set: indicates the operation of cookies. You can also use the cookies module according to official recommendations. Here I will give a simple example. There are still a lot of knowledge points to go into cookies, and I have to search 😂 on Baidu (I do not know much about it).

    ctx.cookies.get(name, [options])
    ctx.cookies.set(name, value, [options])
    Copy the code
    app.use(async ctx => {
      ctx.cookies.set('login_cookie'.'123456', {
        maxAge: 36000 // The millisecond timestamp from date.now ()
      })
      const loginCookie = ctx.cookies.get('login_cookie')
      console.log(loginCookie)
      ctx.response.body = { code: 200.msg: 'success'}})Copy the code

Request Response

There are request and Response objects under the Context; These objects encapsulate Node’s native REQ and RES and provide a number of features useful for HTTP server development.

go request

go response

Koa – the router (routing)

At this stage, indirect proof of the koA usage level is basically covered, but from the beginning we have introduced the characteristics of the framework koA is not bundled with any middleware so we would like to implement routing as follows

app.use(async ctx => {
  if (ctx.url === '/') {
    ctx.body = { msg: 'index page' };
  } else if (ctx.url === '/detail') {
    ctx.body = { msg: 'detail page'}; }});Copy the code

This…… It’s not very elegant, and the community has provided us with a good middleware library, koA-Router

npm install koa-router --save-dev
Copy the code

Let’s start with an example to implement the above inelegant code

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
// Instantiate the route
const router = new Router()
// Register the route
router.get('/'.async ctx => {
  ctx.body = { msg: 'index page' };
});
router.get('/detail'.async ctx => {
  ctx.body = { msg: 'detail page' };
});
// Register routing middleware
app.use(router.routes())
app.listen(3000);
Copy the code

The readability of the above code is not a level, so let’s take a look at her basic usage.

  • Route prefix; At the time of instantiation routing add prefix attribute will be adding prefixes, routing code such as appeal if add prefix, so again during a visit to the detail, the full path is http://localhost:3000/user/detail

    const router = new Router({ prefix: '/user' })
    Copy the code
  • Request mode; The KOA-router supports common request modes. You are advised to use get post PUT Delete Patch Head options

    / / sample
    router.get(...)
    Copy the code
  • Middleware registration; As you can see from the example above, the Router and KOA were not associated before app.use, and the following code is needed to associate them

    app.use(router.routes())
    Copy the code
  • Redirect C. The first parameter of the method is the source of the request, the second parameter is the destination, both of which can be replaced by the alias of the path pattern, and the third parameter is the status code, which defaults to 302. This method is placed in the context

    router.get('/home'.async ctx => {
      ctx.redirect('http://baidu.com');
    });
    Copy the code
  • Route parameters Params and QueryString Route parameters params and QueryString route parameters params and QueryString

    http://localhost/users/1 // params
    http://localhost/users/id=1 // querystring
    Copy the code
    • Params: It is important to note that it is a strong route, which will change the original resource address; The instance

      // In :id indicates an uncertain parameter
      // Access localhost:3000/user/1
      // The value of params in the url can be any value
      router.get('/:id'.async ctx => {
        ctx.body = {
          msg: 'index page'.params: ctx.params.id // ctx.params.id is 1
        };
      });
      Copy the code
    • Querystring: This method will fetch the parameter user= Admin&PWd =234 at the end of the route

      router.get('/'.async ctx => {
        ctx.body = {
          msg: 'index page'.query: ctx.query // ctx.query retrieves the parameter object that follows the URL
        };
      });
      Copy the code
  • AllowedMethod: For this method, its purpose is to handle request errors. Suppose we now implement a /user interface for get requests

    • router.get('/user'.async ctx => {});
      app.use(router.routes());
      // If this line is not present, a 404 will be prompted when user is requested using another request
      // If this line exists, 405 method not allowed is displayed when other request methods are used to request the user
      app.use(router.allowedMethod());
      Copy the code

Koa – Static (Static Web Hosting service)

The most commonly used static file hosting middleware in KOA, for example, we want to save images, but personal projects do not want to get cloud services, we can use this function to meet our needs, and for example, we can also host front-end projects here;

npm install koa-static --save-dev
Copy the code
const staitc = require('koa-static');
// static fill in relative to your current directory so that it finally concatenates into an absolute path
app.use(static(path.join(__dirname, './static')));
Copy the code

Now if we want to access those resources; After the service is started, it can be accessed in the following ways;

http://localhost:3000/xxx.img or some CSS, js files, and so on

Koa-views (Template Rendering Engine Middleware)

In fact, this middleware is not used much (now are some of the front and back end separation of the project), mainly used for some template rendering, such as EJS PUG; Of course, you can not specify the template;

npm intsall koa-views --save-dev
Copy the code
const views = require('koa-views');
// The views directory is created relative to the current directory. The latest internal file is used as a template
app.use(views(path.resolve(__dirname, 'views')));

// After the middleware is mounted, a render function is mounted in the CTX context
app.get('/'.async ctx => {
  // index is the index.html file in the views folder
  await ctx.render('index')});Copy the code

The above code may not explain it very well or there may be some additional caveats

  • The render function is async and must await it

  • If the template is.html, you don’t need to write the suffix. Of course, some other templates are also supported. If you need to register middleware, provide the second parameter options

    app.use(views(path.resolve(__dirname, 'views')), {
      extension: 'ejs' // It can be pug or nunjucks. Remember to use NPM to install the corresponding dependency
    });
    Copy the code

    If we were using another template then the render function would have to change as well

    await ctx.render('index.ejs');
    Copy the code
  • The Render function also takes a second argument, receiving an object; Then the corresponding template can obtain this data;

    await ctx.render('index.ejs', { msg: 'This is the data that's passed from the back end.' });
    Copy the code

    About template syntax, here do not explain, you can go to the corresponding official website to view puG EJS

  • Note that you have already downloaded koa-static and registered the middleware in the main file, so the static directory is the root of your website. Do not import static resources from the template. Suppose your directory looks something like this

    +---static
    |       index.css
    |       test.png
    |       
    \---views
    |       index.ejs
    |   index.js
    Copy the code
    • Wrong example

      <link rel="stylesheet" href="/static/index.css">
      Copy the code
    • Correct example

      <link rel="stylesheet" href="index.css">
      Copy the code

Koa-body (Request body data processing)

Before, if we need to parse json data, we need the intermediate koa-bodyParser. If we need to upload files, we can use the koa-multer. Now we can use the koa-body to solve both problems.

There is an incompatibility between koa-multer and Koa-route (note that it is not koa-router)

If we don’t use this middleware to do something, like the client submits some random data, then in order to accept that data, the server-side code might look like this, right

router.post('/'.async ctx => {
  let bstr = ' ';
  // Read and merge through the native methods exposed by KOA
  ctx.req.on('data'.(data) = > {
    bstr += data
  })
  ctx.req.on('end'.() = > {
    console.log(bstr)
  })
  ctx.body = { code: 200}})Copy the code

Imagine if every time you listen to the data in order to receive the data the data end must be unreasonable;

npm install koa-body --save-dev
Copy the code
const bodyparser = require('koa-body');
// After the middleware is registered, the CTx.request. body property is injected into the CTX context to get the data passed by the client
app.use(bodyparser());

router.post('/'.async ctx => {
  ctx.body = {
    code: 200.// Body can get form data, json data
    msg: ctx.request.body
  }
})
Copy the code

If we also need to upload files, we need to add some configuration during registration

app.use(bodyparser({
  multipart: true.// Support file upload
  formidable: {
    // The default path for saving the file
    uploadDir: path.join(__dirname, 'static/uploads'), 
    // Keep the file extension name
    keepExtensions: true}}));Copy the code

You can do this when you receive data

router.post('/'.async ctx => {
  // files is the collection of objects after the file is uploaded. File is the corresponding key to be passed
  const fileInfo= ctx.request.files.file;
  // Returns the last part of the temporary path as the file name
  const filePath = path.basename(fileInfo.path);
  ctx.body = { 
      url: `${ctx.origin}/uploads/${filePath}`.info: ctx.request.files
  }
})
Copy the code

@KOA/CORS (handling cross-domain middleware)

I believe that when you see this article, most of you are also a front-end, cross-domain problems I do not need to do too much explanation, you can refer to this article

npm install @koa/cors --save-dev
Copy the code
const cors = require('@koa/cors');
app.use(cors());
Copy the code

Koa-json-error (Error handling Middleware)

A middleware that handles KOA errors. After using this middleware, koA throws exceptions, are converted to JSON and returned to the client, greatly improving the robustness and stability of the program

npm install koa-json-error --save-dev
Copy the code
const error = require('koa-json-error');
app.use(error({
  // By default, the stack is also sent to the client, which is obviously not reasonable, so a simple layer of processing is done
  postFormat(e, { stack, ... rest }) {
    return process.env.NODE_ENV === 'production'? rest : { stack, ... rest} } }));Copy the code

To verify this functionality, use the throw error method provided natively by KOA

app.use(async ctx => {
  if(ctx.request.body.name ! = ='admin') {
    // The information is generated according to the status code. You can also customize some error data in the second parameter
    ctx.throw(401, {msg: 'Authentication failed'}); }});Copy the code

If a logical error such as a is not defind can also be caught and thrown, it will not be demonstrated here;

Koa-parameter (parameter verification middleware)

In the daily development work, the robustness of the program or to be more good, how to understand? Let’s say you want the user to pass you a string, and the user just gives you a number, and you have an error in your program, which is obviously not going to work, but the first thing that comes to mind is if xx == xx and then xx would look pretty bad, If we need to solve this problem elegantly, we can use this middleware;

NPM address

Making the address

npm install koa-parameter --save-dev
Copy the code
const parameter = require('koa-parameter');
parameter(app);
Copy the code

After the above operations, the CTX context has an additional validation function, verifyParams

router.post('/'.async ctx => {
  // Receive an object
  ctx.verifyParams({
    // Short form of validation
    username: 'string'.password: { type: 'string'.required: true } // Or you can do that
  })
  ctx.body = { code: 1}})Copy the code

To verify simple parameters, you can refer to the following methods

And, of course, there are some special checks that are provided

ctx.verifyParams({
    // You must enter the URL format
    address: { type: 'url'.required: true },
    // The email format must be entered
    email: { type: 'email'.required: true}})Copy the code

If it is more responsible, it can also be verified, of course, it can not cover the whole scene, but this has saved us a lot of effort

// an item is an object, and a field is an array
ctx.verifyParams({
   obj: { type: 'object'.rule: { children: 'array'}}})// an item is an array containing objects
ctx.verifyParams({
   // List is an array, each of which is an object. The object must contain the info field
   list: { type: 'array'.itemType: 'object'.rule: {info: 'string'}}})Copy the code

Mysql > select * from mysql2;

The author has not had a thorough understanding of why mysql2 was used instead of mysql. I vaguely remember that mysql2 has good support for Promise and provides a special API that is more convenient to use.

npm install mysql2 --save-dev
Copy the code

The use level is also relatively simple;

const mysql = require('mysql2');
// Create a connection
const connection = mysql.createConnection({
  host: 'xx.xx.xx.xx'.user: 'root'.password: '123456'.port: 3306.database: 'test'
});
// Basic add, delete, change, check
const [row] = await connection.promise().query('SELECT * FROM users WHERE username=? AND password=? '['lisi'.'li']);
const [info] = await connection.promise().query('UPDATE users SET username=? WHERE password=? '['hello'.'wang']);
const [info] = await connection.promise().query('DELETE FROM users WHERE username=? '['hello']);
const [info] = await connection.promise().query('INSERT INTO users (id, username, password) VALUES (? ,? ,?) '['id1'.'username1'.'password1']);
Copy the code

Note that you can use [row] to deconstruct the results of a query, but update, modify, or delete operations do not return the values you deleted. Instead, they return an operation object containing the necessary information.

JWT authentication

JWT(JSON Web Token) is an open jSON-based standard implemented to pass declarations between network application environments

Koa relies on two packages koA-jWT jSONWebToken to perform such authentication

npm install koa-jwt jsonwebtoken --save-dev
Copy the code

The usage level is mainly divided into two steps. The first step is to generate the token, which is generally returned to the user at login. Generate tokens using the sign method

To generate the token

JWT. Sign (' to encrypt the contents of the string | Buffer | object ', 'the encryption key,} {configuration object)// Set two important parameters of the object
{
  algorithm: "HS256" | "HS384" | "HS512" |
    "RS256" | "RS384" | "RS512" |
    "ES256" | "ES384" | "ES512" |
    "PS256" | "PS384" | "PS512" |
    "none"; // Encryption mode
  expiresIn: "2d" // Can be a number, in seconds if array, or 2d/10h 2 days 10 hours
}
Copy the code
const jwt = require('jsonwebtoken');
router.post('/'.async ctx => {
  // Structure the username and password from the request body
  const { username, password } = ctx.request.body;
  
  const token = jwt.sign(
    { username, password },
  	'auth-key',
    { 
      algorithm: 'HS256'.// You don't have to write it
      expiresIn: '2d'}); ctx.body = { token }; })Copy the code

jwt.verifyCheck token

Jwt. verify('token', 'encrypted key', {config object})Copy the code
router.post('/auth'.async ctx => {
  const token = ctx.header.authorization
  try {
    // This token check needs additional processing
    jwt.verify(token.split(' ') [1].'auth-key', { algorithms: 'HS256' });
  } catch (e) {
    ctx.throw(401);
  }
  ctx.body = { code: 1 };
})
Copy the code

koa-jwtCheck token

const koaJwt = require('koa-jwt');
// If there is a cross-domain configuration must be written below the cross-domain related configuration
// The first argument to koaJwt is an object, which is basically the same as the configuration object described above.
// Path can accept strings, arrays, and regular expressions to filter certain urls without authentication
app.use(koaJwt({ secret: 'auth-key' }).unless({ path: [/\user/]}))Copy the code

koa-generator

If we want to create a koA server application, we have to create files from scratch, download middleware and so on, which is a bit of a hassle. The Generator is designed to take us out of the chore, but it is a bit old for your reference and is not recommended for full use

npm install koa-generator -g
Copy the code

This is a command line tool. After a global installation, you can view the help information from the command line using koA2 –help

After creating the project, everything is ok except for the files in the bin folder, so let’s have a look

The figure can be roughly classified into two categories;

  • Why is there no suffix? What does the first line mean? Here is an article to help you solve this question point here

  • The second problem is that the port can be set on the command line, but Windows and Liunx have different commands. Code is cross end, run a script cross end pit; Again, a package called cross-env is recommended

    npm install cross-env --dev
    Copy the code

    It is also very simple to use, such as the previous startup script nodemon app.js

    Cross-env PORT=3001 nodemon app.js and then run the project again and it will be on PORT 3001

    Of course, you can also set environment variables like

    cross-env NODE_ENV=production PORT=3001 nodemon app.js
    Copy the code

End scatter flower 🌼