package.json文件

每個Nodejs項目的根目錄下面,一般都會有一個package.json文件。該文件可以由npm init生成,定義了項目所需要的各種模塊,以及項目的配置信息(比如名稱、版本、許可證等元數據)。package.json文件內部就是一個JSON對象,該對象的每一個成員就是當前項目的一項設置。

4、連接mysql數據庫

mysql模塊是node操作MySQL的引擎,可以在node.js環境下對MySQL數據庫進行建表,增、刪、改、查等操作。

 "dependencies": {
"glob": "^7.1.3",
"jsonwebtoken": "^8.4.0",
"koa": "^2.6.2",
"koa-body": "^4.0.4",
"koa-bodyparser": "^4.2.1",
"koa-cors": "0.0.16",
"koa-jwt": "^3.5.1",
"koa-router": "^7.4.0",
"koa-session": "^5.10.0",
"koa-static": "^5.0.0",
"mysql": "^2.16.0"
}

在package.json依賴中設置”mysql”: “^2.16.0”

const config = require('../config').database
const mysql = require('mysql')
const pool = mysql.createPool(config)
const query = function(sql,succCb,errCb){
pool.getConnection(function(err,conn){
if (err) {
let data = {
code:500,
message:"請求失敗",
data:err
};
errCb(data);
}else {
conn.query(sql,function(err,result){
if (err) {
let data = {
code:500,
message:"請求失敗",
data:err
};
errCb(data);
}else {
succCb(result);
conn.release();
}
})
}
})
}
module.exports = query;

const query = require('./query')
const Tools = require('./tools')
const mysql = require('mysql')
const config = require('../config').database
const pool = mysql.createPool(config)
const getByPage = function(tb,page,limit){
return new Promise((resolve,reject)=>{
let start = (page-1)*limit;
let command = select * from ${tb} limit ${start},${limit}; query(command,function(res){ let data = { code:200, message:'獲取成功', data:{ list:res, pagination:{ size:res.length, currentPage:parseInt(page) } } } query(select count(*) from ${tb},function(res){ data.data.pagination['total'] = res[0]["count(*)"]; data.data.pagination['totalPage'] = parseInt(res[0]["count(*)"]/limit) + ((res[0]["count(*)"]%limit)>0?1:0); resolve(data); },function(err){ resolve(data) }) },function(err){ resolve(err); }) }) } const getForeignInfo = function(tb,filter,foreign){//主表,篩選條件,外鍵信息 let queryStr = '';//查詢條件 for (let key in filter) { queryStr += ${tb}.${key}=${filter[key]}&; } queryStr = queryStr.substr(0,queryStr.length-1); let as = ''; let join = ''; let tables = from ${tb} ${tb}; for (let key1 in foreign) { let table = foreign[key1].table; let data = foreign[key1].data; let key = key1; join += join ${table} ${table} on ${tb}.${key}=${table}.id ; for(let key2 in data){ as += ,${table}.${key2} as ${data[key2]} } } let str = select ${tb}.*+as+tables+join+(queryStr==''?'':'where '+queryStr); console.log(str); return str; } const Sql = { queryAll:function(tb,filter,foreign){ //獲取表的全部記錄 if (filter && !Tools.isEmptyObject(filter)) { //分頁 return getByPage(tb,filter.page,filter.limit,foreign) }else { //全部 return new Promise((resolve,reject)=>{ let str = select * from ${tb}; if (foreign) { str = getForeignInfo(tb,filter,foreign); } query(str,function(res){ let data = { code:200, message:'獲取成功', data:{ list:res, size:res.length } } resolve(data); },function(err){ resolve(err); }) }) } }, query:function(tb,id,foreign){ //根據id獲取 return new Promise((resolve,reject)=>{ query(select * from ${tb} where id=${id},function(res){ let data = { code:200, message:res.length==0?'查無數據':'獲取成功', data:res.length==0?{}:res[0] } resolve(data); },function(err){ resolve(err); }) }) }, queryByField:function(tb,fieldName,fieldValue){ //根據field獲取 return new Promise((resolve,reject)=>{ query(select * from ${tb} where ${fieldName}="${fieldValue}",function(res){ let data = { code:200, message:res.length==0?'查無數據':'獲取成功', data:res.length==0?{}:res[0] } resolve(data); },function(err){ resolve(err); }) }) }, insert:function(tb,data){ //插入一條記錄 return new Promise((resolve,reject)=>{ let [keys,values] = [[],[]]; for (let key in data) { if (data.hasOwnProperty(key)) { keys.push(key); if (Object.prototype.toString.call(data[key]) == '[object String]') { values.push("${data[key]}") }else { values.push(data[key]) } } } query(insert into ${tb} (${keys}) values (${values}),function(res){ let id = res.insertId; let data = { code:200, message:'添加成功', data:res } query(select * from ${tb} where id=${id},function(res){ data.data = res[0]; resolve(data); },function(err){ resolve(data); }) },function(err){ resolve(err); }) }) }, insertRows:function(tb,arr){ //插入多條記錄 return new Promise((resolve,reject)=>{ let [keys,values] = [[],[]]; for (let i = 0; i < arr.length; i++) { let [data,value] = [arr[i],[]]; for (let key in data) { if (data.hasOwnProperty(key)) { if (i==0) { keys.push(key); } if (Object.prototype.toString.call(data[key]) == '[object String]') { value.push("${data[key]}") }else { value.push(data[key]) } } } values.push((${value})); } query(insert into ${tb} (${keys}) values ${values},function(res){ let data = { code:200, message:'添加成功', data:res } let ids = []; for (let i = 0; i < res.affectedRows; i++) { ids.push(res.insertId+i); } query(select * from ${tb} where id in (${ids}),function(res){ data.data = { list:res, size:res.length }; resolve(data); },function(err){ resolve(data); }) },function(err){ resolve(err); }) }) }, update:function(tb,id,data){ //根據id修改單條記錄 return new Promise((resolve,reject)=>{ let [str,index] = ['',0]; for (let key in data) { if (data.hasOwnProperty(key)) { if (index!=0) { str += ',' } if (Object.prototype.toString.call(data[key]) == '[object String]'){ str += ${key}="${data[key]}" } else { str += ${key}=${data[key]} } index++; } } query(update ${tb} set ${str} where id=${id},function(res){ let data = { code:200, message:'修改成功', data:res } query(select * from ${tb} where id=${id},function(res){ data.data = res[0]; resolve(data); },function(err){ resolve(data); }) },function(err){ resolve(err); }) }) }, updateRows:function(tb,arr){ //修改多條記錄 return new Promise((resolve,reject)=>{ let [str,ids,len,keys] = ['',[],arr.length,Object.keys(arr[0])]; for (let x = 0; x < len; x++) { ids.push(arr[x].id); } for (let i = 0; i < keys.length; i++) { let k = keys[i]; if (k!='id') { str += ${k} = case id ; for (let j = 0; j < len; j++) { str += when ${arr[j].id} then ; if (Object.prototype.toString.call(arr[j][k]) == '[object String]'){ str += "${arr[j][k]}" } else{ str += ${arr[j][k]} } } str += 'end' if (i<keys.length-1) { str += ',' } } } query(update ${tb} set ${str} where id in (${ids}),function(res){ let data = { code:200, message:'修改成功', data:res } query(select * from ${tb} where id in (${ids}),function(res){ data.data = { list:res, size:res.length }; resolve(data); },function(err){ resolve(data); }) },function(err){ resolve(err); }) }) }, delete:function(tb,id){ //根據id刪除單條記錄 return new Promise((resolve,reject)=>{ query(delete from ${tb} where id=${id},function(res){ let data = { code:200, message:'刪除成功', data:res } resolve(data); },function(err){ resolve(err); }) }) }, deleteRows:function(tb,data){ //根據id數組刪除多條記錄 return new Promise((resolve,reject)=>{ query(delete from ${tb} where id in (${data}),function(res){ let data = { code:200, message:'刪除成功', data:res } resolve(data); },function(err){ resolve(err); }) }) }, search:function(tb,data,foreign){ //根據條件準確查詢 let queryStr = '';//查詢條件 for (let key in data) { queryStr += ${key}=${data[key]}&; } queryStr = queryStr.substr(0,queryStr.length-1); let str; if (foreign) { str = getForeignInfo(tb,data,foreign); }else { str = select * from ${tb} where ${queryStr} } return new Promise((resolve,reject)=>{ query(str,function(res){ resolve({ code:200, message:'獲取成功', data:res }); },function(err){ resolve(err); }); }) }, searchVague:function(tb,val,fields,foreign){//根據條件模糊查詢 let str = select * from ${tb} where concat(; for (let i = 0; i < fields.length; i++) { str += ${fields[i]},; } str = str.substring(0,str.length-1); str += ) like %${val}%; if (fields.length==1){ str = select * from ${tb} where ${fields[0]} like '%${val}%'; } return new Promise((resolve,reject)=>{ query(str,function(res){ resolve({ code:200, message:'獲取成功', data:res }); },function(err){ resolve(err); }); }) } } module.exports = Sql;

5、登錄和JWT認證實現

引入?“jsonwebtoken”:?“^8.4.0”,模塊

const jwt = require('jsonwebtoken');
const Token = {
encrypt:function(data,time){ //data加密數據,time過期時間
return jwt.sign(data, 'token', {expiresIn:time})
},
decrypt:function(token){
try {
let data = jwt.verify(token, 'token');
return {
token:true,
id:data.id
};
} catch (e) {
return {
token:false,
data:e
}
}
}
}
module.exports = Token

登錄成功返回token代碼:

token = jwt.sign({id:res[0].id}, 'token', {expiresIn: '15d'})

6、restful api實現示例

需求:小程序用戶輸入單詞,返回這個單詞的詳細介紹,包含中文釋義和常用例句。

建立wordDesc表

添加一個routers文件

wordDesc.js

const router = require('koa-router')();   //路由
const Sql = require('../utils/sql');
const query = require('../utils/query');
const Tools = require('../utils/tools');
const jwt = require('jsonwebtoken');
const Token = require('../utils/token')
const tbName = 'worddesc';
const preUrl = '/api/worddesc';
let codeList = {};
api/worddesc/test
.get(${preUrl}/:word,async(ctx,next)=>{ //獲取word desc let word = ctx.params.word; let data = Token.decrypt(ctx.header.authorization); if (data.token) { let res = await Sql.queryByField(tbName,"word", word); ctx.body = res; }else { ctx.body = { code:401, message:'failed', data:data }; } }) { "code": 200, "message": "獲取成功", "data": { "id": 2, "word": "test", "description": "this is a test.", "bookid": "2", "createdtime": null, "lastupdatetime": null, "maxl": 2 }

分頁獲取列表數據

/api/worddesc?page=2&count=2

.get(${preUrl},async(ctx,next)=>{ //獲取word desc

   let data = Token.decrypt(ctx.header.authorization);
   if (data.token) {

     let page = ctx.query.page;
     if(page == undefined){
         page = 1;
     }
     let count = ctx.query.count;

     if(count ==undefined){
         count = 500;
     }

     let filter = {
         page:page,
         limit:count
     }

     let res = await Sql.queryAll(tbName,filter);
     ctx.body = res;
   }else {
     ctx.body = {
       code:401,
       message:'failed',
       data:data
     };
   }
 })
{
    "code": 200,
    "message": "獲取成功",
    "data": {
        "list": [
            {
                "id": 2,
                "word": "test",
                "description": "this is a test.",
                "bookid": "2",
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": 2
            },
            {
                "id": 3,
                "word": "year3",
                "description": "year3",
                "bookid": "3",
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": 3
            },
            {
                "id": 4,
                "word": "ljtest",
                "description": "desc........",
                "bookid": "2",
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": null
            },
            {
                "id": 6,
                "word": "lj2",
                "description": null,
                "bookid": null,
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": null
            },
            {
                "id": 7,
                "word": "lj3",
                "description": null,
                "bookid": null,
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": null
            },
            {
                "id": 8,
                "word": "lj4",
                "description": null,
                "bookid": null,
                "createdtime": null,
                "lastupdatetime": null,
                "maxl": null
            }
        ],
        "pagination": {
            "size": 6,
            "currentPage": 1,
            "total": 6,
            "totalPage": 1
        }
    }
}

同樣增加添加、修改、刪除api

.post(${preUrl},async(ctx,next)=>{ //添加信息
   let data = Token.decrypt(ctx.header.authorization);

   let word = ctx.request.body.word;

   if (data.token) {
     let wordRes = await  Sql.queryByField(tbName,"word",word);
     console.log("------------");
     console.log(wordRes);
     if(wordRes.data.id != undefined)
     {
        let res = await Sql.update(tbName,wordRes.data.id,ctx.request.body);
        ctx.body = res;
     }else{

        let res = await Sql.insert(tbName,ctx.request.body);
        ctx.body = res;
     }

   }else {
     ctx.body = {
       code:401,
       message:'failed',
       data:data
     };
   }
  })

  .delete(${preUrl}/:word,async(ctx,next)=>{ //修改信息
    let data = Token.decrypt(ctx.header.authorization);

    let word = ctx.params.word;

    if (data.token) {
        let wordRes = await  Sql.queryByField(tbName,"word",word);
        if(wordRes.data.id != undefined)
        {

            let res = await Sql.delete(tbName,wordRes.data.id );
            ctx.body = res;
        }else{

        ctx.body = {
        code:200,
        message:'no record',
        data:''
        };
    }

    }else {
        ctx.body = {
        code:401,
        message:'failed',
        data:data
        };
    }

   })

  .put(${preUrl}/:word,async(ctx,next)=>{ //修改信息
   let data = Token.decrypt(ctx.header.authorization);

   let word = ctx.params.word;
   console.log(word);

   if (data.token) {
     let wordRes = await  Sql.queryByField(tbName,"word",word);
     console.log("------------");
     console.log(wordRes);
     if(wordRes.data.id != undefined)
     {
        console.log("id------------");
        console.log(wordRes.data.id);
        console.log(ctx.request.body);
        let res = await Sql.update(tbName,wordRes.data.id ,ctx.request.body);
        ctx.body = res;
     }else{

       ctx.body = {
       code:200,
       message:'no record',
       data:''
     };
     }

   }else {
     ctx.body = {
       code:401,
       message:'failed',
       data:data
     };
   }
 })

7、管理和發布api

開發過程中命令行輸入 node app.js 可以打開命令窗口啟動運行,窗口中顯示調試或錯誤信息,關閉窗口則結束進程。

生產環境中可以使用pm2來啟動進程,M2是可以用于生產環境的Nodejs的進程管理工具,并且它內置一個負載均衡。它不僅可以保證服務不會中斷一直在線,并且提供0秒reload功能,還有其他一系列進程管理、監控功能。并且使用起來非常簡單。

安裝pm2 

npm install -g pm2

下面列出常用命令

$ npm install pm2 -g     # 命令行安裝 pm2 
$ pm2 start app.js -i 4 #后臺運行pm2,啟動4個app.js
# 也可以把'max' 參數傳遞給 start
# 正確的進程數目依賴于Cpu的核心數目
$ pm2 start app.js --name my-api # 命名進程
$ pm2 list # 顯示所有進程狀態
$ pm2 monit # 監視所有進程
$ pm2 logs # 顯示所有進程日志
$ pm2 stop all # 停止所有進程
$ pm2 restart all # 重啟所有進程
$ pm2 reload all # 0秒停機重載進程 (用于 NETWORKED 進程)
$ pm2 stop 0 # 停止指定的進程
$ pm2 restart 0 # 重啟指定的進程
$ pm2 startup # 產生 init 腳本 保持進程活著
$ pm2 web # 運行健壯的 computer API endpoint (http://localhost:9615)
$ pm2 delete 0 # 殺死指定的進程
$ pm2 delete all # 殺死全部進程

8、總結

通過這次nodejs開發實踐,對于nodejs能夠流行起來一點都不感到意外,開發起來太簡單和方便了。跟java這些傳統的技術相比,寫nodejs腳本甚至感覺不像是在編程,真的像玩一樣,極大了拉低了程序員的門檻。

還有一個事實就是JavaScript是Web開發者們熟知的語言,大部分人都了解JavaScript或多少使用過它。所以說,從其他技術轉型到Node.js是很簡單的。

跟java、.net這些傳統的技術路線相比,nodejs項目在安裝、調試、部署和發布都很方便,很多Web服務器和云服務提供商都支持Node.js的Web應用。

本文章轉載微信公眾號@程序你好

上一篇:

10個關于Node.js REST API 的最佳實踐

下一篇:

使用Express.js構建一個簡單的RESTful API,處理CRUD操作
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費