วิธีตั้งค่าโครงการแบ็กเอนด์ Express API ด้วย PostgreSQL

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างรวดเร็ว ↬ ในบทความนี้ เราจะสร้างชุดปลายทาง API โดยใช้ Express ตั้งแต่เริ่มต้นในไวยากรณ์ ES6 และครอบคลุมแนวทางปฏิบัติที่ดีที่สุดสำหรับการพัฒนาบางส่วน ค้นหาว่าชิ้นส่วนทั้งหมดทำงานร่วมกันอย่างไรในขณะที่คุณสร้างโครงการขนาดเล็กโดยใช้การบูรณาการอย่างต่อเนื่องและการพัฒนาที่ขับเคลื่อนด้วยการทดสอบก่อนที่จะปรับใช้กับ Heroku

เราจะใช้วิธี Test-Driven Development (TDD) และงานการตั้งค่า Continuous Integration (CI) เพื่อเรียกใช้การทดสอบของเราบน Travis CI และ AppVeyor โดยอัตโนมัติ พร้อมด้วยคุณภาพโค้ดและการรายงานความครอบคลุม เราจะเรียนรู้เกี่ยวกับคอนโทรลเลอร์ โมเดล (พร้อม PostgreSQL) การจัดการข้อผิดพลาด และมิดเดิลแวร์ Express แบบอะซิงโครนัส สุดท้าย เราจะดำเนินการไปป์ไลน์ CI/CD ให้เสร็จสมบูรณ์โดยกำหนดค่าการปรับใช้อัตโนมัติบน Heroku

ดูเหมือนเยอะ แต่บทช่วยสอนนี้มุ่งเป้าไปที่ผู้เริ่มต้นที่พร้อมจะลองใช้โปรเจ็กต์แบ็คเอนด์ที่มีความซับซ้อนในระดับหนึ่ง และอาจสับสนว่าชิ้นส่วนทั้งหมดเข้ากันได้อย่างไรในโครงการจริง .

มีความแข็งแกร่งโดยไม่ล้นหลามและแบ่งออกเป็นส่วนต่างๆ ที่คุณสามารถทำให้เสร็จในระยะเวลาที่เหมาะสม

เริ่มต้น

ขั้นตอนแรกคือการสร้างไดเร็กทอรีใหม่สำหรับโปรเจ็กต์และเริ่มต้นโปรเจ็กต์โหนดใหม่ ต้องใช้โหนดเพื่อดำเนินการต่อกับบทช่วยสอนนี้ หากคุณไม่ได้ติดตั้งไว้ ให้ไปที่เว็บไซต์อย่างเป็นทางการ ดาวน์โหลดและติดตั้งก่อนดำเนินการต่อ

ฉันจะใช้เส้นด้ายเป็นผู้จัดการแพคเกจของฉันสำหรับโครงการนี้ มีคำแนะนำในการติดตั้งสำหรับระบบปฏิบัติการเฉพาะของคุณที่นี่ รู้สึกอิสระที่จะใช้ npm หากคุณต้องการ

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

เปิดเทอร์มินัลของคุณ สร้างไดเร็กทอรีใหม่ และเริ่มโปรเจ็กต์ Node.js

 # create a new directory mkdir express-api-template # change to the newly-created directory cd express-api-template # initialize a new Node.js project npm init

ตอบคำถามที่ตามมาเพื่อสร้างไฟล์ package.json ไฟล์นี้มีข้อมูลเกี่ยวกับโครงการของคุณ ตัวอย่างของข้อมูลดังกล่าวรวมถึงสิ่งที่ขึ้นต่อกันที่ใช้ คำสั่งเพื่อเริ่มต้นโครงการ และอื่นๆ

ตอนนี้คุณสามารถเปิดโฟลเดอร์โครงการในโปรแกรมแก้ไขที่คุณเลือกได้ ฉันใช้รหัส visual studio เป็น IDE ฟรีที่มีปลั๊กอินมากมายที่จะทำให้ชีวิตของคุณง่ายขึ้น และใช้ได้กับทุกแพลตฟอร์มหลัก คุณสามารถดาวน์โหลดได้จากเว็บไซต์อย่างเป็นทางการ

สร้างไฟล์ต่อไปนี้ในโฟลเดอร์โครงการ:

  • README.md
  • .editorconfig

นี่คือคำอธิบายของสิ่งที่ .editorconfig ทำจากเว็บไซต์ EditorConfig (คุณอาจไม่ต้องการมันถ้าคุณทำงานคนเดียว แต่ก็ไม่เป็นอันตราย ฉันจะทิ้งมันไว้ที่นี่)

"EditorConfig ช่วยรักษารูปแบบการเข้ารหัสที่สอดคล้องกันสำหรับนักพัฒนาหลายคนที่ทำงานในโครงการเดียวกันในโปรแกรมแก้ไขและ IDE ต่างๆ"

เปิด .editorconfig และวางรหัสต่อไปนี้:

 root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = false insert_final_newline = true

[*] หมายความว่าเราต้องการใช้กฎที่มากับทุกไฟล์ในโครงการ เราต้องการขนาดเยื้องสองช่องว่างและชุด UTF-8 นอกจากนี้เรายังต้องการตัดช่องว่างสีขาวต่อท้ายและแทรกบรรทัดว่างสุดท้ายในไฟล์ของเรา

เปิด README.md และเพิ่มชื่อโครงการเป็นองค์ประกอบระดับแรก

 # Express API template

มาเพิ่มการควบคุมเวอร์ชันทันที

 # initialize the project folder as a git repository git init

สร้างไฟล์ .gitignore และป้อนบรรทัดต่อไปนี้:

 node_modules/ yarn-error.log .env .nyc_output coverage build/

นี่คือไฟล์และโฟลเดอร์ทั้งหมดที่เราไม่ต้องการติดตาม เรายังไม่มีพวกเขาในโครงการของเรา แต่เราจะเห็นพวกเขาเมื่อเราดำเนินการต่อไป

ณ จุดนี้ คุณควรมีโครงสร้างโฟลเดอร์ดังต่อไปนี้

 EXPRESS-API-TEMPLATE ├── .editorconfig ├── .gitignore ├── package.json └── README.md

ฉันถือว่าสิ่งนี้เป็นจุดที่ดีในการยอมรับการเปลี่ยนแปลงของฉันและผลักดันพวกเขาไปที่ GitHub

เริ่มต้นโครงการด่วนใหม่

Express เป็นเฟรมเวิร์ก Node.js สำหรับสร้างเว็บแอปพลิเคชัน ตามเว็บไซต์อย่างเป็นทางการมันคือ

เฟรมเวิร์กเว็บที่เรียบง่าย รวดเร็ว ไม่ได้รับความคิดเห็นสำหรับ Node.js

มีเฟรมเวิร์กเว็บแอปพลิเคชันที่ยอดเยี่ยมอื่นๆ สำหรับ Node.js แต่ Express ได้รับความนิยมอย่างมาก โดยมี GitHub มากกว่า 47k ดวงในขณะที่เขียนบทความนี้

ในบทความนี้ เราจะไม่ต้องพูดถึงส่วนต่างๆ ของ Express มากนัก สำหรับการสนทนานั้น ฉันแนะนำให้คุณดูซีรีส์ของเจมี่ ภาคแรกมาแล้ว ภาคสองมาแล้ว

ติดตั้ง Express และเริ่มโครงการ Express ใหม่ เป็นไปได้ที่จะตั้งค่าเซิร์ฟเวอร์ Express ด้วยตนเองตั้งแต่เริ่มต้น แต่เพื่อให้ชีวิตของเราง่ายขึ้น เราจะใช้ตัวสร้างด่วนเพื่อตั้งค่าโครงร่างแอป

 # install the express generator globally yarn global add express-generator # install express yarn add express # generate the express project in the current folder express -f

แฟล็ก -f บังคับให้ Express สร้างโปรเจ็กต์ในไดเร็กทอรีปัจจุบัน

ตอนนี้เราจะดำเนินการทำความสะอาดบ้าน

  1. ลบไฟล์ index/users.js
  2. ลบโฟลเดอร์ public/ และ views/
  3. เปลี่ยนชื่อไฟล์ bin/www เป็น bin/www.js
  4. ถอนการติดตั้ง jade ด้วยคำสั่ง yarn remove jade
  5. สร้างโฟลเดอร์ใหม่ชื่อ src/ และย้ายข้อมูลต่อไปนี้เข้าไปข้างใน: 1. ไฟล์ app.js 2. bin/ โฟลเดอร์ 3. routes/ โฟลเดอร์ภายใน
  6. เปิด package.json และอัปเดตสคริปต์ start ให้มีลักษณะดังนี้
 "start": "node ./src/bin/www"

ณ จุดนี้ โครงสร้างโฟลเดอร์โครงการของคุณมีลักษณะดังนี้ คุณสามารถดูว่า VS Code เน้นการเปลี่ยนแปลงไฟล์ที่เกิดขึ้นได้อย่างไร

 EXPRESS-API-TEMPLATE ├── node_modules ├── src | ├── bin │ │ ├── www.js │ ├── routes │ | ├── index.js │ └── app.js ├── .editorconfig ├── .gitignore ├── package.json ├── README.md └── yarn.lock

เปิด src/app.js และแทนที่เนื้อหาด้วยโค้ดด้านล่าง

 var logger = require('morgan'); var express = require('express'); var cookieParser = require('cookie-parser'); var indexRouter = require('./routes/index'); var app = express(); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.use('/v1', indexRouter); module.exports = app;

หลังจากที่ต้องใช้บางไลบรารี เราสั่งให้ Express จัดการทุกคำขอที่มาถึง /v1 ด้วย indexRouter

แทนที่เนื้อหาของ routes/index.js ด้วยรหัสด้านล่าง:

 var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { return res.status(200).json({ message: 'Welcome to Express API template' }); }); module.exports = router;

เราคว้า Express สร้างเราเตอร์จากนั้นให้บริการเส้นทาง / ซึ่งส่งคืนรหัสสถานะ 200 และข้อความ JSON

เริ่มแอพด้วยคำสั่งด้านล่าง:

 # start the app yarn start

หากคุณตั้งค่าทุกอย่างถูกต้องแล้ว คุณจะเห็นเพียง $ node ./src/bin/www ในเทอร์มินัลของคุณ

ไปที่ https://localhost:3000/v1 ในเบราว์เซอร์ของคุณ คุณควรเห็นข้อความต่อไปนี้:

 { "message": "Welcome to Express API template" }

นี่เป็นจุดที่ดีในการยอมรับการเปลี่ยนแปลงของเรา

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 01-install-express

การแปลงรหัสของเรา ES6

โค้ดที่สร้างโดย express-generator อยู่ใน ES5 แต่ในบทความนี้ เราจะเขียนโค้ดทั้งหมดในรูปแบบ ES6 ดังนั้น มาแปลงรหัสที่มีอยู่ของเราเป็น ES6

แทนที่เนื้อหาของ routes/index.js ด้วยรหัสด้านล่าง:

 import express from 'express'; const indexRouter = express.Router(); indexRouter.get('/', (req, res) => res.status(200).json({ message: 'Welcome to Express API template' }) ); export default indexRouter;

เป็นรหัสเดียวกับที่เราเห็นด้านบน แต่มีคำสั่งนำเข้าและฟังก์ชันลูกศรในตัวจัดการเส้นทาง /

แทนที่เนื้อหาของ src/app.js ด้วยรหัสด้านล่าง:

 import logger from 'morgan'; import express from 'express'; import cookieParser from 'cookie-parser'; import indexRouter from './routes/index'; const app = express(); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.use('/v1', indexRouter); export default app;

ตอนนี้เรามาดูเนื้อหาของ src/bin/www.js กัน เราจะสร้างมันขึ้นเรื่อยๆ ลบเนื้อหาของ src/bin/www.js และวางในช่องโค้ดด้านล่าง

 #!/usr/bin/env node /** * Module dependencies. */ import debug from 'debug'; import http from 'http'; import app from '../app'; /** * Normalize a port into a number, string, or false. */ const normalizePort = val => { const port = parseInt(val, 10); if (Number.isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; }; /** * Get port from environment and store in Express. */ const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ const server = http.createServer(app); // next code block goes here

รหัสนี้ตรวจสอบว่ามีการระบุพอร์ตที่กำหนดเองในตัวแปรสภาพแวดล้อมหรือไม่ หากไม่มีการตั้งค่าพอร์ตเริ่มต้นที่ 3000 จะถูกตั้งค่าบนอินสแตนซ์ของแอป หลังจากปรับให้เป็นมาตรฐานเป็นสตริงหรือตัวเลขโดย normalizePort จากนั้นเซิร์ฟเวอร์จะถูกสร้างขึ้นจากโมดูล http โดยมี app เป็นฟังก์ชันเรียกกลับ

บรรทัด #!/usr/bin/env node เป็นทางเลือก เนื่องจากเราจะระบุโหนดเมื่อเราต้องการเรียกใช้ไฟล์นี้ แต่ตรวจสอบให้แน่ใจว่าอยู่ในบรรทัดที่ 1 ของไฟล์ src/bin/www.js หรือลบออกทั้งหมด

มาดูฟังก์ชันการจัดการข้อผิดพลาดกัน คัดลอกและวางบล็อคโค้ดนี้ต่อจากบรรทัดที่สร้างเซิร์ฟเวอร์

 /** * Event listener for HTTP server "error" event. */ const onError = error => { if (error.syscall !== 'listen') { throw error; } const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': alert(`${bind} requires elevated privileges`); process.exit(1); break; case 'EADDRINUSE': alert(`${bind} is already in use`); process.exit(1); break; default: throw error; } }; /** * Event listener for HTTP server "listening" event. */ const onListening = () => { const addr = server.address(); const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; debug(`Listening on ${bind}`); }; /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening);

ฟังก์ชัน onError จะรับฟังข้อผิดพลาดในเซิร์ฟเวอร์ http และแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสม ฟังก์ชัน onListening จะแสดงผลพอร์ตที่เซิร์ฟเวอร์กำลังฟังบนคอนโซลเท่านั้น สุดท้าย เซิร์ฟเวอร์จะรับฟังคำขอที่เข้ามาตามที่อยู่และพอร์ตที่ระบุ

ณ จุดนี้ โค้ดที่มีอยู่ทั้งหมดของเราอยู่ในรูปแบบ ES6 หยุดเซิร์ฟเวอร์ของคุณ (ใช้ Ctrl + C ) และเรียกใช้ yarn start คุณจะได้รับข้อผิดพลาด SyntaxError: Invalid or unexpected token สิ่งนี้เกิดขึ้นเนื่องจาก Node (ในขณะที่เขียน) ไม่รองรับไวยากรณ์บางอย่างที่เราใช้ในโค้ดของเรา

ตอนนี้เราจะแก้ไขในส่วนต่อไปนี้

การกำหนดค่าการพึ่งพาการพัฒนา: eslint , babel , nodemon และ prettier

ถึงเวลาตั้งค่าสคริปต์ส่วนใหญ่ที่เราต้องการในขั้นตอนนี้ของโครงการ

ติดตั้งไลบรารีที่จำเป็นด้วยคำสั่งด้านล่าง คุณเพียงแค่คัดลอกทุกอย่างแล้ววางลงในเทอร์มินัลของคุณ บรรทัดความคิดเห็นจะถูกข้าม

 # install babel scripts yarn add @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/register @babel/runtime @babel/node --dev

สิ่งนี้จะติดตั้งสคริปต์ Babel ที่ระบุไว้ทั้งหมดเป็นการพึ่งพาการพัฒนา ตรวจสอบไฟล์ package.json ของคุณและคุณควรเห็นส่วน devDependencies สคริปต์ที่ติดตั้งทั้งหมดจะแสดงอยู่ที่นั่น

สคริปต์ Babel ที่เราใช้อยู่มีคำอธิบายด้านล่าง:

@babel/cli การติดตั้งที่จำเป็นสำหรับการใช้ babel อนุญาตให้ใช้ Babel จากเทอร์มินัลและพร้อมใช้งานเป็น . ./node_modules/.bin/babel
@babel/core ฟังก์ชันหลักของ Babel นี่คือการติดตั้งที่จำเป็น
@babel/node การทำงานนี้เหมือนกับ Node.js CLI โดยมีประโยชน์เพิ่มเติมในการรวบรวมด้วย babel รีเซ็ตและปลั๊กอินของ Babel สิ่งนี้จำเป็นสำหรับใช้กับ nodemon
@babel/plugin-transform-runtime ซึ่งจะช่วยหลีกเลี่ยงความซ้ำซ้อนในผลลัพธ์ที่คอมไพล์แล้ว
@babel/preset-env ชุดของปลั๊กอินที่รับผิดชอบในการแปลงรหัส
@babel/register ไฟล์นี้จะรวบรวมไฟล์ได้ทันทีและกำหนดเป็นข้อกำหนดระหว่างการทดสอบ
@babel/runtime นี้ทำงานร่วมกับ @babel/plugin-transform-runtime

สร้างไฟล์ชื่อ .babelrc ที่รูทของโปรเจ็กต์ของคุณ และเพิ่มโค้ดต่อไปนี้:

 { "presets": ["@babel/preset-env"], "plugins": ["@babel/transform-runtime"] }

มาติดตั้ง nodemon

 # install nodemon yarn add nodemon --dev

nodemon คือไลบรารีที่ตรวจสอบซอร์สโค้ดของโปรเจ็กต์ของเรา และรีสตาร์ทเซิร์ฟเวอร์ของเราโดยอัตโนมัติทุกครั้งที่สังเกตเห็นการเปลี่ยนแปลงใดๆ

สร้างไฟล์ชื่อ nodemon.json ที่รูทของโปรเจ็กต์ของคุณ และเพิ่มโค้ดด้านล่าง:

 { "watch": [ "package.json", "nodemon.json", ".eslintrc.json", ".babelrc", ".prettierrc", "src/" ], "verbose": true, "ignore": ["*.test.js", "*.spec.js"] }

คีย์ watch จะบอก nodemon ว่าไฟล์และโฟลเดอร์ใดที่จะคอยดูการเปลี่ยนแปลง ดังนั้น เมื่อใดก็ตามที่ไฟล์เหล่านี้เปลี่ยนแปลง nodemon จะรีสตาร์ทเซิร์ฟเวอร์ ปุ่ม ignore จะบอกไฟล์ว่าไม่ต้องคอยดูการเปลี่ยนแปลง

ตอนนี้อัปเดตส่วน scripts ของไฟล์ package.json ของคุณให้มีลักษณะดังนี้:

 # build the content of the src folder "prestart": "babel ./src --out-dir build" # start server from the build folder "start": "node ./build/bin/www" # start server in development mode "startdev": "nodemon --exec babel-node ./src/bin/www"
  1. สคริปต์แบบ prestart สร้างเนื้อหาของโฟลเดอร์ src/ และวางไว้ในโฟลเดอร์ build/ เมื่อคุณออกคำสั่ง yarn start สคริปต์นี้จะรันก่อนสคริปต์ start
  2. สคริปต์ start ให้บริการเนื้อหาของโฟลเดอร์ build/ แทนโฟลเดอร์ src/ ที่เราให้บริการก่อนหน้านี้ นี่คือสคริปต์ที่คุณจะใช้เมื่อให้บริการไฟล์ในการผลิต อันที่จริง บริการต่างๆ เช่น Heroku จะเรียกใช้สคริปต์นี้โดยอัตโนมัติเมื่อคุณปรับใช้
  3. yarn startdev ใช้เพื่อเริ่มเซิร์ฟเวอร์ระหว่างการพัฒนา จากนี้ไปเราจะใช้สคริปต์นี้ในการพัฒนาแอป สังเกตว่าตอนนี้เรากำลังใช้ babel-node เพื่อเรียกใช้แอปแทน node ปกติ แฟ --exec บังคับ babel-node ให้บริการโฟลเดอร์ src/ สำหรับสคริปต์ start เราใช้ node เนื่องจากไฟล์ในโฟลเดอร์ build/ ได้รับการคอมไพล์เป็น ES5 แล้ว

เรียกใช้ yarn startdev และไปที่ https://localhost:3000/v1. เซิร์ฟเวอร์ของคุณควรกลับมาใช้งานได้อีกครั้ง

ขั้นตอนสุดท้ายในส่วนนี้คือการกำหนดค่า ESLint และ prettier ESLint ช่วยในการบังคับใช้กฎไวยากรณ์ ในขณะที่รูปแบบที่สวยงามกว่าช่วยในการจัดรูปแบบโค้ดของเราอย่างเหมาะสมเพื่อให้สามารถอ่านได้

เพิ่มทั้งคู่ด้วยคำสั่งด้านล่าง คุณควรเรียกใช้สิ่งนี้บนเทอร์มินัลที่แยกต่างหากในขณะที่สังเกตเทอร์มินัลที่เซิร์ฟเวอร์ของเราทำงานอยู่ คุณควรเห็นการรีสตาร์ทเซิร์ฟเวอร์ เนื่องจากเรากำลังตรวจสอบการเปลี่ยนแปลงไฟล์ package.json

 # install elsint and prettier yarn add eslint eslint-config-airbnb-base eslint-plugin-import prettier --dev

ตอนนี้สร้างไฟล์ . eslintrc.json ใน root ทของโปรเจ็กต์และเพิ่มโค้ดด้านล่าง:

 { "env": { "browser": true, "es6": true, "node": true, "mocha": true }, "extends": ["airbnb-base"], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "rules": { "indent": ["warn", 2], "linebreak-style": ["error", "unix"], "quotes": ["error", "single"], "semi": ["error", "always"], "no-console": 1, "comma-dangle": [0], "arrow-parens": [0], "object-curly-spacing": ["warn", "always"], "array-bracket-spacing": ["warn", "always"], "import/prefer-default-export": [0] } }

ไฟล์นี้ส่วนใหญ่กำหนดกฎเกณฑ์บางอย่างที่ eslint จะตรวจสอบโค้ดของเรา คุณจะเห็นได้ว่าเรากำลังขยายกฎของรูปแบบที่ Airbnb ใช้

ในส่วน "rules" เรากำหนดว่า eslint ควรแสดงคำเตือนหรือข้อผิดพลาดเมื่อพบการละเมิดบางอย่างหรือไม่ ตัวอย่างเช่น มันแสดงข้อความเตือนบนเทอร์มินัลของเราสำหรับการเยื้องที่ไม่ได้ใช้ 2 ช่องว่าง ค่า [0] จะปิดกฎ ซึ่งหมายความว่าเราจะไม่ได้รับคำเตือนหรือข้อผิดพลาดหากเราละเมิดกฎนั้น

สร้างไฟล์ชื่อ .prettierrc และเพิ่มโค้ดด้านล่าง:

 { "trailingComma": "es5", "tabWidth": 2, "semi": true, "singleQuote": true }

เรากำลังตั้งค่าความกว้างของแท็บเป็น 2 และบังคับใช้การใช้เครื่องหมายคำพูดเดี่ยวตลอดแอปพลิเคชันของเรา ตรวจสอบคำแนะนำที่สวยกว่าสำหรับตัวเลือกการจัดสไตล์เพิ่มเติม

เพิ่มสคริปต์ต่อไปนี้ใน package.json ของคุณ:

 # add these one after the other "lint": "./node_modules/.bin/eslint ./src" "pretty": "prettier --write '**/*.{js,json}' '!node_modules/**'" "postpretty": "yarn lint --fix"

เรียกใช้ yarn lint คุณควรเห็นข้อผิดพลาดและคำเตือนจำนวนหนึ่งในคอนโซล

คำสั่ง pretty ทำให้โค้ดของเราสวยงามขึ้น คำสั่ง postpretty จะถูกรันทันทีหลังจากนั้น มันรันคำสั่ง lint โดยมีแฟล็ก --fix ต่อท้าย การตั้งค่าสถานะนี้บอกให้ ESLint แก้ไขปัญหาผ้าสำลีทั่วไปโดยอัตโนมัติ ด้วยวิธีนี้ ฉันมักจะรันคำสั่ง yarn pretty โดยไม่สนใจคำสั่ง lint

วิ่ง yarn pretty คุณควรเห็นว่าเรามีคำเตือนเพียงสองครั้งเกี่ยวกับการมีอยู่ของ alert ในไฟล์ bin/www.js

นี่คือลักษณะโครงสร้างโครงการของเรา ณ จุดนี้

 EXPRESS-API-TEMPLATE ├── build ├── node_modules ├── src | ├── bin │ │ ├── www.js │ ├── routes │ | ├── index.js │ └── app.js ├── .babelrc ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── nodemon.json ├── package.json ├── README.md └── yarn.lock

คุณอาจพบว่าคุณมีไฟล์เพิ่มเติมคือ yarn-error.log ในรูทโปรเจ็กต์ของคุณ เพิ่มลงในไฟล์ .gitignore ยอมรับการเปลี่ยนแปลงของคุณ

  • สาขาที่เกี่ยวข้อง ณ จุดนี้ใน repo ของฉันคือ 02-dev-dependencies

ตัวแปรการตั้งค่าและสภาพแวดล้อมในไฟล์ . env ของเรา

ในเกือบทุกโครงการ คุณจะต้องมีสถานที่จัดเก็บการตั้งค่าที่จะใช้ทั่วทั้งแอปของคุณ เช่น รหัสลับของ AWS เราเก็บการตั้งค่าดังกล่าวเป็นตัวแปรสภาพแวดล้อม วิธีนี้ช่วยไม่ให้พวกมันถูกสอดรู้สอดเห็น และเราสามารถนำมาใช้ภายในแอปพลิเคชันของเราได้ตามต้องการ

ฉันชอบมีไฟล์ settings.js ซึ่งฉันอ่านตัวแปรสภาพแวดล้อมทั้งหมด จากนั้น ฉันสามารถอ้างถึงไฟล์การตั้งค่าได้จากทุกที่ภายในแอพของฉัน คุณมีอิสระที่จะตั้งชื่อไฟล์นี้ตามที่คุณต้องการ แต่มีความเห็นเป็นเอกฉันท์เกี่ยวกับการตั้งชื่อไฟล์ดังกล่าว settings.js หรือ config.js

สำหรับตัวแปรสภาพแวดล้อมของเรา เราจะเก็บไว้ในไฟล์ .env และอ่านมันในไฟล์ settings จากที่นั่น

สร้างไฟล์ .env ที่รูทของโปรเจ็กต์ของคุณและป้อนบรรทัดด้านล่าง:

 TEST_ENV_VARIABLE="Environment variable is coming across"

เพื่อให้สามารถอ่านตัวแปรสภาพแวดล้อมในโครงการของเราได้ มีห้องสมุดที่ดี dotenv ที่อ่านไฟล์ .env ของเราและทำให้เราสามารถเข้าถึงตัวแปรสภาพแวดล้อมที่กำหนดไว้ภายในได้ มาติดตั้งกันเลย

 # install dotenv yarn add dotenv

เพิ่มไฟล์ .env ในรายการไฟล์ที่ nodemon กำลังดู nodemon

ตอนนี้สร้างไฟล์ settings.js ภายในโฟลเดอร์ src/ และเพิ่มโค้ดด้านล่าง:

 import dotenv from 'dotenv'; dotenv.config(); export const testEnvironmentVariable = process.env.TEST_ENV_VARIABLE;

เรานำเข้าแพ็คเกจ dotenv และเรียกวิธีการกำหนดค่า จากนั้นเราจะส่งออก testEnvironmentVariable ซึ่งเราตั้งค่าไว้ในไฟล์ . .env ของเรา

เปิด src/routes/index.js และแทนที่โค้ดด้านล่าง

 import express from 'express'; import { testEnvironmentVariable } from '../settings'; const indexRouter = express.Router(); indexRouter.get('/', (req, res) => res.status(200).json({ message: testEnvironmentVariable })); export default indexRouter;

การเปลี่ยนแปลงเพียงอย่างเดียวที่เราทำที่นี่คือเรานำเข้า testEnvironmentVariable จากไฟล์ settings ของเรา และใช้เป็นข้อความส่งคืนสำหรับคำขอจากเส้นทาง /

ไปที่ https://localhost:3000/v1 และคุณจะเห็นข้อความดังที่แสดงด้านล่าง

 { "message": "Environment variable is coming across." }

และนั่นแหล่ะ จากนี้ไป เราสามารถเพิ่มตัวแปรสภาพแวดล้อมได้มากเท่าที่เราต้องการ และเราสามารถส่งออกได้จากไฟล์ settings.js ของเรา

นี่เป็นจุดที่ดีในการคอมมิตโค้ดของคุณ อย่าลืมแต่งแต้มและร้อยโค้ดของคุณ

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 03-env-variables

การเขียนแบบทดสอบครั้งแรกของเรา

ถึงเวลารวมการทดสอบเข้ากับแอปของเราแล้ว สิ่งหนึ่งที่ทำให้นักพัฒนามั่นใจในโค้ดของพวกเขาคือการทดสอบ ฉันแน่ใจว่าคุณเคยเห็นบทความมากมายบนเว็บเกี่ยวกับการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ (TDD) ไม่สามารถเน้นได้มากพอที่โค้ดของคุณจำเป็นต้องมีการวัดผลการทดสอบ TDD นั้นง่ายต่อการติดตามเมื่อคุณทำงานกับ Express.js

ในการทดสอบของเรา เราจะทำการเรียกไปยังตำแหน่งข้อมูล API ของเรา และตรวจสอบเพื่อดูว่าสิ่งที่ส่งคืนเป็นสิ่งที่เราคาดหวังหรือไม่

ติดตั้งการพึ่งพาที่จำเป็น:

 # install dependencies yarn add mocha chai nyc sinon-chai supertest coveralls --dev

แต่ละไลบรารีเหล่านี้มีบทบาทของตนเองในการทดสอบของเรา

mocha นักวิ่งทดสอบ
chai ใช้ในการยืนยัน
nyc รวบรวมรายงานผลการทดสอบ
sinon-chai ขยายคำยืนยันของชัย
supertest ใช้ในการเรียก HTTP ไปยังจุดปลาย API ของเรา
coveralls สำหรับการอัปโหลดครอบคลุมการทดสอบไปยัง coveralls.io

สร้างการ test/ โฟลเดอร์ใหม่ที่รากของโครงการของคุณ สร้างสองไฟล์ภายในโฟลเดอร์นี้:

  • ทดสอบ/setup.js
  • test/index.test.js

มอคค่าจะค้นหาการ test/ โฟลเดอร์โดยอัตโนมัติ

เปิด test/setup.js และวางโค้ดด้านล่าง นี่เป็นเพียงไฟล์ตัวช่วยที่ช่วยให้เราจัดระเบียบการนำเข้าทั้งหมดที่เราต้องการในไฟล์ทดสอบของเรา

 import supertest from 'supertest'; import chai from 'chai'; import sinonChai from 'sinon-chai'; import app from '../src/app'; chai.use(sinonChai); export const { expect } = chai; export const server = supertest.agent(app); export const BASE_URL = '/v1';

นี่เป็นเหมือนไฟล์การตั้งค่า แต่สำหรับการทดสอบของเรา วิธีนี้ทำให้เราไม่ต้องเริ่มต้นทุกอย่างภายในไฟล์ทดสอบแต่ละไฟล์ ดังนั้นเราจึงนำเข้าแพ็คเกจที่จำเป็นและส่งออกสิ่งที่เราเริ่มต้น - ซึ่งเราสามารถนำเข้าในไฟล์ที่ต้องการได้

เปิด index.test.js และวางโค้ดทดสอบต่อไปนี้

 import { expect, server, BASE_URL } from './setup'; describe('Index page test', () => { it('gets base url', done => { server .get(`${BASE_URL}/`) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.message).to.equal( 'Environment variable is coming across.' ); done(); }); }); });

ที่นี่เราทำการร้องขอเพื่อรับจุดปลายฐาน ซึ่งก็คือ / และยืนยันว่าราย res. body มีคีย์ message ที่มีค่าของ Environment variable is coming across.

หากคุณไม่คุ้นเคยกับ describe รูปแบบ it กล่าว เราขอแนะนำให้คุณดูเอกสาร "เริ่มต้นใช้งาน" ของ Mocha อย่างรวดเร็ว

เพิ่มคำสั่งทดสอบในส่วน scripts ของ package.json

 "test": "nyc --reporter=html --reporter=text --reporter=lcov mocha -r @babel/register"

สคริปต์นี้ดำเนินการทดสอบของเรากับ nyc และสร้างรายงานความครอบคลุมสามประเภท: รายงาน HTML ที่ส่งออกไปยัง coverage/ โฟลเดอร์ รายงานข้อความที่ส่งออกไปยังเทอร์มินัล และรายงาน lcov ที่ส่งออกไปยังโฟลเดอร์ . .nyc_output/

ตอนนี้เรียกใช้ yarn test คุณควรเห็นรายงานข้อความในเทอร์มินัลของคุณเหมือนกับที่แสดงในรูปภาพด้านล่าง

รายงานความครอบคลุมการทดสอบ (ตัวอย่างขนาดใหญ่)

ขอให้สังเกตว่ามีการสร้างโฟลเดอร์เพิ่มเติมสองโฟลเดอร์:

  • .nyc_output/
  • coverage/

ดูข้างใน .gitignore แล้วคุณจะเห็นว่าเราละเลยทั้งสองอย่างไปแล้ว ขอแนะนำให้คุณเปิด coverage/index.html ในเบราว์เซอร์และดูรายงานการทดสอบของแต่ละไฟล์

นี่เป็นจุดที่ดีในการยอมรับการเปลี่ยนแปลงของคุณ

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 04-first-test

การบูรณาการอย่างต่อเนื่อง (CD) และตราสัญลักษณ์: Travis, Coveralls, Code Climate, AppVeyor

ถึงเวลากำหนดค่าเครื่องมือการผสานรวมและการปรับใช้อย่างต่อเนื่อง (CI/CD) เราจะกำหนดค่าบริการทั่วไป เช่น travis-ci , coveralls , AppVeyor และ codeclimate และเพิ่ม badges ให้กับไฟล์ README ของเรา

มาเริ่มกันเลย.

Travis CI

Travis CI เป็นเครื่องมือที่ทำการทดสอบของเราโดยอัตโนมัติทุกครั้งที่เราส่งคำสั่งไปยัง GitHub (และล่าสุดคือ Bitbucket) และทุกครั้งที่เราสร้างคำขอดึง สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อทำการดึงคำขอโดยแสดงให้เราเห็นว่าโค้ดใหม่ของเราทำการทดสอบไม่ได้หรือไม่

  1. ไปที่ travis-ci.com หรือ travis-ci.org และสร้างบัญชีหากคุณยังไม่มี คุณต้องลงทะเบียนด้วยบัญชี GitHub ของคุณ
  2. วางเมาส์เหนือลูกศรแบบเลื่อนลงข้างรูปโปรไฟล์ของคุณ แล้วคลิก settings
  3. ภายใต้แท็บ Repositories คลิก Manage repositories on Github เพื่อเปลี่ยนเส้นทางไปยัง Github
  4. ในหน้า GitHub ให้เลื่อนลงไปที่ Repository access และคลิกช่องทำเครื่องหมายถัดจาก Only select repositories
  5. คลิกดรอปดาวน์ Select repositories และค้นหา express-api-template repo คลิกเพื่อเพิ่มลงในรายการที่เก็บที่คุณต้องการเพิ่มใน travis-ci
  6. คลิก Approve and install และรอเพื่อเปลี่ยนเส้นทางกลับไปที่ travis-ci
  7. ที่ด้านบนของหน้า repo ใกล้กับชื่อ repo ให้คลิกที่ไอคอน build unknown จากโมดอลรูปภาพสถานะ เลือกมาร์กดาวน์จากดรอปดาวน์รูปแบบ
  8. คัดลอกโค้ดผลลัพธ์แล้ววางลงในไฟล์ README.md ของคุณ
  9. ในหน้าโครงการ ให้คลิก More options > Settings ภายใต้ส่วน Environment Variables เพิ่มตัวแปร env TEST_ENV_VARIABLE เมื่อป้อนค่า ต้องแน่ใจว่าใส่ไว้ในเครื่องหมายคำพูดคู่ เช่น "Environment variable is coming across."
  10. สร้างไฟล์ .travis.yml ที่รูทของโปรเจ็กต์ของคุณ และวางโค้ดด้านล่าง (เราจะตั้งค่า CC_TEST_REPORTER_ID ในส่วน Code Climate)
 language: node_js env: global: - CC_TEST_REPORTER_ID=get-this-from-code-climate-repo-page matrix: include: - node_js: '12' cache: directories: [node_modules] install: yarn after_success: yarn coverage before_script: - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - chmod +x ./cc-test-reporter - ./cc-test-reporter before-build script: - yarn test after_script: - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESUL

ขั้นแรก เราบอกให้ Travis รันการทดสอบของเรากับ Node.js จากนั้นตั้งค่าตัวแปรสภาพแวดล้อมส่วนกลาง CC_TEST_REPORTER_ID (เราจะพูดถึงสิ่งนี้ในส่วน Code Climate) ในส่วน matrix เราบอกให้ Travis ทำการทดสอบด้วย Node.js v12 นอกจากนี้เรายังต้องการแคช node_modules/ ดังนั้นจึงไม่จำเป็นต้องสร้างใหม่ทุกครั้ง

เราติดตั้งการพึ่งพาของเราโดยใช้คำสั่ง yarn ซึ่งเป็นชวเลขสำหรับ yarn install คำสั่ง before_script และ after_script ใช้เพื่ออัปโหลดผลการครอบคลุมไปยัง codeclimate เราจะกำหนดค่า codeclimate ในไม่ช้า หลังจาก yarn test สำเร็จ เราต้องการเรียกใช้ yarn coverage ซึ่งจะอัปโหลดรายงานความครอบคลุมของเราไปที่ coveralls.io

เสื้อคลุม

Coveralls อัปโหลดข้อมูลการทดสอบความครอบคลุมเพื่อให้เห็นภาพได้ง่าย เราสามารถดูขอบเขตการทดสอบบนเครื่องในพื้นที่ของเราได้จากโฟลเดอร์ความครอบคลุม แต่ Coveralls จะทำให้ใช้งานได้นอกเครื่องของเรา

  1. เยี่ยมชม coveralls.io และลงชื่อเข้าใช้หรือลงทะเบียนด้วยบัญชี Github ของคุณ
  2. วางเมาส์เหนือด้านซ้ายของหน้าจอเพื่อแสดงเมนูการนำทาง คลิกที่ ADD REPOS
  3. ค้นหา express-api-template repo และเปิดการครอบคลุมโดยใช้ปุ่มสลับที่ด้านซ้ายมือ หากคุณหาไม่พบ ให้คลิกที่ SYNC REPOS ที่มุมขวาบนแล้วลองอีกครั้ง โปรดทราบว่า repo ของคุณต้องเป็นสาธารณะ เว้นแต่คุณจะมีบัญชี PRO
  4. คลิกรายละเอียดเพื่อไปที่หน้ารายละเอียดการซื้อคืน
  5. สร้างไฟล์ .coveralls.yml ที่รูทของโปรเจ็กต์และป้อนโค้ดด้านล่าง หากต้องการรับ repo_token ให้คลิกที่รายละเอียด repo คุณจะพบได้อย่างง่ายดายในหน้านั้น คุณสามารถค้นหาเบราว์เซอร์ repo_token ได้
 repo_token: get-this-from-repo-settings-on-coveralls.io

โทเค็นนี้จะจับคู่ข้อมูลความครอบคลุมของคุณกับซื้อคืนบน Coveralls ตอนนี้ เพิ่มคำสั่ง coverage ไปยังส่วน scripts ของไฟล์ package.json ของคุณ:

 "coverage": "nyc report --reporter=text-lcov | coveralls"

คำสั่งนี้จะอัปโหลดรายงานความครอบคลุมในโฟลเดอร์ . .nyc_output ไปยัง coveralls.io เปิดการเชื่อมต่ออินเทอร์เน็ตของคุณและเรียกใช้:

 yarn coverage

สิ่งนี้ควรอัปโหลดรายงานความครอบคลุมที่มีอยู่ไปยังชุดคลุม รีเฟรชหน้า repo บน coveralls เพื่อดูรายงานฉบับเต็ม

ในหน้ารายละเอียด ให้เลื่อนลงมาเพื่อค้นหาส่วน BADGE YOUR REPO คลิกที่ดรอปดาวน์ EMBED และคัดลอกโค้ดมาร์กดาวน์แล้ววางลงใน ไฟล์ README ของคุณ

รหัสภูมิอากาศ

Code Climate เป็นเครื่องมือที่ช่วยให้เราวัดคุณภาพของโค้ดได้ มันแสดงให้เราเห็นถึงตัวชี้วัดการบำรุงรักษาโดยการตรวจสอบโค้ดของเรากับรูปแบบที่กำหนดไว้ โดยจะตรวจจับสิ่งต่างๆ เช่น การทำซ้ำที่ไม่จำเป็นและการซ้อนลูปอย่างล้ำลึก นอกจากนี้ยังรวบรวมข้อมูลความครอบคลุมการทดสอบเช่นเดียวกับ coveralls.io

  1. ไปที่ codeclimate.com และคลิกที่ 'ลงทะเบียนกับ GitHub' เข้าสู่ระบบถ้าคุณมีบัญชีอยู่แล้ว
  2. เมื่ออยู่ในแดชบอร์ดของคุณ ให้คลิกที่ Add a repository
  3. ค้นหา express-api-template repo จากรายการและคลิกที่ Add Repo
  4. รอให้บิลด์เสร็จสมบูรณ์และเปลี่ยนเส้นทางไปยังแดชบอร์ด repo
  5. ใต้ Codebase Summary ให้คลิกที่ Test Coverage ใต้เมนู Test coverage ให้คัดลอก TEST REPORTER ID แล้ววางลงใน . travis.yml ของคุณเป็นค่า CC_TEST_REPORTER_ID
  6. ยังคงอยู่ในหน้าเดียวกัน ที่การนำทางด้านซ้ายมือ ภายใต้ EXTRAS ให้คลิกที่ป้าย คัดลอกตราความสามารถในการ maintainability และ test coverage ในรูปแบบมาร์กดาวน์ และวางลงในไฟล์ README.md ของคุณ

โปรดทราบว่ามีสองวิธีในการกำหนดค่าการตรวจสอบความสามารถในการบำรุงรักษา มีการตั้งค่าเริ่มต้นที่ใช้กับทุก repo แต่หากต้องการ คุณสามารถจัดเตรียมไฟล์ .codeclimate.yml ที่รูทของโปรเจ็กต์ของคุณ ฉันจะใช้การตั้งค่าเริ่มต้น ซึ่งคุณจะพบได้ในแท็บ Maintainability ของหน้าการตั้งค่า repo ฉันขอแนะนำให้คุณดูอย่างน้อย หากคุณยังต้องการกำหนดค่าการตั้งค่าของคุณเอง คู่มือนี้จะให้ข้อมูลทั้งหมดที่คุณต้องการ

AppVeyor

AppVeyor และ Travis CI เป็นทั้งนักวิ่งทดสอบอัตโนมัติ ข้อแตกต่างที่สำคัญคือ travis-ci ทำการทดสอบในสภาพแวดล้อม Linux ในขณะที่ AppVeyor ทำการทดสอบในสภาพแวดล้อม Windows ส่วนนี้รวมไว้เพื่อแสดงวิธีเริ่มต้นใช้งาน AppVeyor

  • เยี่ยมชม AppVeyor และเข้าสู่ระบบหรือลงทะเบียน
  • ในหน้าถัดไป ให้คลิกที่ NEW PROJECT
  • จากรายการ repo ให้ค้นหา express-api-template repo วางเมาส์เหนือมันแล้วคลิก ADD
  • คลิกที่แท็บ Settings คลิกที่ Environment ในการนำทางด้านซ้าย เพิ่ม TEST_ENV_VARIABLE และค่าของมัน คลิก 'บันทึก' ที่ด้านล่างของหน้า
  • สร้างไฟล์ appveyor.yml ที่รูทของโปรเจ็กต์ของคุณ และวางโค้ดด้านล่าง
 environment: matrix: - nodejs_version: "12" install: - yarn test_script: - yarn test build: off

รหัสนี้สั่งให้ AppVeyor ทำการทดสอบของเราโดยใช้ Node.js v12 จากนั้นเราติดตั้งการพึ่งพาโปรเจ็กต์ของเราด้วยคำสั่ง yarn test_script ระบุคำสั่งเพื่อเรียกใช้การทดสอบของเรา บรรทัดสุดท้ายบอก AppVeyor ไม่ให้สร้างโฟลเดอร์บิลด์

คลิกที่แท็บ Settings ที่การนำทางด้านซ้ายมือ ให้คลิกที่ป้าย คัดลอกโค้ด markdown และวางลงในไฟล์ README.md ของคุณ

ยืนยันรหัสของคุณและกดไปที่ GitHub หากคุณทำทุกอย่างตามคำแนะนำ การทดสอบทั้งหมดควรผ่าน และคุณจะเห็นป้ายสถานะใหม่ที่แสดงด้านล่าง ตรวจสอบอีกครั้งว่าคุณได้ตั้งค่าตัวแปรสภาพแวดล้อมบน Travis และ AppVeyor

ป้าย Repo CI/CD (ตัวอย่างขนาดใหญ่)

ตอนนี้เป็นเวลาที่ดีที่จะยอมรับการเปลี่ยนแปลงของเรา

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 05-ci

การเพิ่มคอนโทรลเลอร์

ขณะนี้ เรากำลังจัดการคำขอ GET ไปยัง root URL /v1 ภายใน src/routes/index.js ใช้งานได้ตามที่คาดไว้และไม่มีอะไรผิดปกติกับมัน อย่างไรก็ตาม เมื่อแอปพลิเคชันของคุณเติบโตขึ้น คุณต้องการทำให้สิ่งต่างๆ เป็นระเบียบเรียบร้อย คุณต้องการแยกข้อกังวลออก — คุณต้องการแยกที่ชัดเจนระหว่างรหัสที่จัดการคำขอและรหัสที่สร้างการตอบสนองที่จะถูกส่งกลับไปยังไคลเอนต์ เพื่อให้บรรลุสิ่งนี้ เราเขียน controllers ตัวควบคุมเป็นเพียงฟังก์ชันที่จัดการคำขอที่มาจาก URL เฉพาะ

ในการเริ่มต้น ให้สร้าง controllers/ โฟลเดอร์ภายในโฟลเดอร์ src/ controllers ภายในสร้างสองไฟล์: index.js และ home.js เราจะส่งออกฟังก์ชันของเราจากภายใน index.js คุณสามารถตั้งชื่อ home.js อะไรก็ได้ตามต้องการ แต่โดยทั่วไปแล้วคุณต้องการตั้งชื่อคอนโทรลเลอร์ตามสิ่งที่พวกเขาควบคุม ตัวอย่างเช่น คุณอาจมีไฟล์ usersController.js เพื่อเก็บทุกฟังก์ชันที่เกี่ยวข้องกับผู้ใช้ในแอปของคุณ

เปิด src/controllers/home.js แล้วป้อนรหัสด้านล่าง:

 import { testEnvironmentVariable } from '../settings'; export const indexPage = (req, res) => res.status(200).json({ message: testEnvironmentVariable });

คุณจะสังเกตเห็นว่าเราย้ายเฉพาะฟังก์ชันที่จัดการคำขอสำหรับเส้นทาง /

เปิด src/controllers/index.js แล้วป้อนโค้ดด้านล่าง

 // export everything from home.js export * from './home';

เราส่งออกทุกอย่างจากไฟล์ home.js ซึ่งช่วยให้เราย่อคำสั่งนำเข้าเพื่อ import { indexPage } from '../controllers';

เปิด src/routes/index.js และแทนที่รหัสด้วยรหัสด้านล่าง:

 import express from 'express'; import { indexPage } from '../controllers'; const indexRouter = express.Router(); indexRouter.get('/', indexPage); export default indexRouter;

การเปลี่ยนแปลงเพียงอย่างเดียวที่นี่คือเราได้จัดเตรียมฟังก์ชันเพื่อจัดการกับคำขอไปยังเส้นทาง /

คุณเพิ่งเขียนคอนโทรลเลอร์ตัวแรกของคุณสำเร็จ จากนี้ไปจะเป็นการเพิ่มไฟล์และฟังก์ชันเพิ่มเติมตามต้องการ

ไปข้างหน้าและเล่นกับแอพโดยเพิ่มเส้นทางและตัวควบคุมอีกสองสามรายการ คุณสามารถเพิ่มเส้นทางและตัวควบคุมสำหรับหน้าเกี่ยวกับ อย่าลืมอัปเดตการทดสอบของคุณ

รัน yarn test เพื่อยืนยันว่าเราไม่ได้หักอะไรเลย การทดสอบของคุณผ่านหรือไม่ ที่เย็น

นี่เป็นจุดที่ดีในการยอมรับการเปลี่ยนแปลงของเรา

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 06-controllers

การเชื่อมต่อฐานข้อมูล PostgreSQL และการเขียนแบบจำลอง

ขณะนี้ตัวควบคุมของเราส่งคืนข้อความแบบกำหนดค่าตายตัว ในแอพในโลกแห่งความเป็นจริง เรามักจะต้องจัดเก็บและดึงข้อมูลจากฐานข้อมูล ในส่วนนี้ เราจะเชื่อมต่อแอพของเรากับฐานข้อมูล PostgreSQL

เราจะใช้การจัดเก็บและดึงข้อความธรรมดาโดยใช้ฐานข้อมูล เรามีสองตัวเลือกในการตั้งค่าฐานข้อมูล: เราสามารถจัดเตรียมหนึ่งรายการจากเซิร์ฟเวอร์คลาวด์ หรือเราสามารถตั้งค่าของเราเองในเครื่อง

ฉันอยากจะแนะนำให้คุณจัดเตรียมฐานข้อมูลจากเซิร์ฟเวอร์คลาวด์ ElephantSQL has a free plan that gives 20MB of free storage which is sufficient for this tutorial. Visit the site and click on Get a managed database today . Create an account (if you don't have one) and follow the instructions to create a free plan. Take note of the URL on the database details page. We'll be needing it soon.

ElephantSQL turtle plan details page (Large preview)

If you would rather set up a database locally, you should visit the PostgreSQL and PgAdmin sites for further instructions.

Once we have a database set up, we need to find a way to allow our Express app to communicate with our database. Node.js by default doesn't support reading and writing to PostgreSQL database, so we'll be using an excellent library, appropriately named, node-postgres.

node-postgres executes SQL queries in node and returns the result as an object, from which we can grab items from the rows key.

Let's connect node-postgres to our application.

 # install node-postgres yarn add pg

Open settings.js and add the line below:

 export const connectionString = process.env.CONNECTION_STRING;

Open your .env file and add the CONNECTION_STRING variable. This is the connection string we'll be using to establish a connection to our database. The general form of the connection string is shown below.

 CONNECTION_STRING="postgresql://dbuser:dbpassword@localhost:5432/dbname"

If you're using elephantSQL you should copy the URL from the database details page.

Inside your /src folder, create a new folder called models/ . Inside this folder, create two files:

  • pool.js
  • model.js

Open pools.js and paste the following code:

 import { Pool } from 'pg'; import dotenv from 'dotenv'; import { connectionString } from '../settings'; dotenv.config(); export const pool = new Pool({ connectionString });

First, we import the Pool and dotenv from the pg and dotenv packages, and then import the settings we created for our postgres database before initializing dotenv . We establish a connection to our database with the Pool object. In node-postgres , every query is executed by a client. A Pool is a collection of clients for communicating with the database.

To create the connection, the pool constructor takes a config object. You can read more about all the possible configurations here. It also accepts a single connection string, which I will use here.

Open model.js and paste the following code:

 import { pool } from './pool'; class Model { constructor(table) { this.pool = pool; this.table = table; this.pool.on('error', (err, client) => `Error, ${err}, on idle client${client}`); } async select(columns, clause) { let query = `SELECT ${columns} FROM ${this.table}`; if (clause) query += clause; return this.pool.query(query); } } export default Model;

We create a model class whose constructor accepts the database table we wish to operate on. We'll be using a single pool for all our models.

We then create a select method which we will use to retrieve items from our database. This method accepts the columns we want to retrieve and a clause, such as a WHERE clause. It returns the result of the query, which is a Promise . Remember we said earlier that every query is executed by a client, but here we execute the query with pool. This is because, when we use pool.query , node-postgres executes the query using the first available idle client.

The query you write is entirely up to you, provided it is a valid SQL statement that can be executed by a Postgres engine.

The next step is to actually create an API endpoint to utilize our newly connected database. Before we do that, I'd like us to create some utility functions. The goal is for us to have a way to perform common database operations from the command line.

Create a folder, utils/ inside the src/ folder. Create three files inside this folder:

  • queries.js
  • queryFunctions.js
  • runQuery.js

We're going to create functions to create a table in our database, insert seed data in the table, and to delete the table.

เปิด query.js และวางรหัสต่อไปนี้:

 export const createMessageTable = ` DROP TABLE IF EXISTS messages; CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, name VARCHAR DEFAULT '', message VARCHAR NOT NULL ) `; export const insertMessages = ` INSERT INTO messages(name, message) VALUES ('chidimo', 'first message'), ('orji', 'second message') `; export const dropMessagesTable = 'DROP TABLE messages';

ในไฟล์นี้ เรากำหนดสตริงการสืบค้น SQL สามสตริง แบบสอบถามแรกลบและสร้างตาราง messages ใหม่ แบบสอบถามที่สองแทรกสองแถวลงในตาราง messages รู้สึกอิสระที่จะเพิ่มรายการเพิ่มเติมที่นี่ แบบสอบถามสุดท้ายลด/ลบตาราง messages

เปิด queryFunctions.js และวางโค้ดต่อไปนี้:

 import { pool } from '../models/pool'; import { insertMessages, dropMessagesTable, createMessageTable, } from './queries'; export const executeQueryArray = async arr => new Promise(resolve => { const stop = arr.length; arr.forEach(async (q, index) => { await pool.query(q); if (index + 1 === stop) resolve(); }); }); export const dropTables = () => executeQueryArray([ dropMessagesTable ]); export const createTables = () => executeQueryArray([ createMessageTable ]); export const insertIntoTables = () => executeQueryArray([ insertMessages ]);

ที่นี่ เราสร้างฟังก์ชันเพื่อดำเนินการค้นหาที่เรากำหนดไว้ก่อนหน้านี้ โปรดทราบว่าฟังก์ชัน executeQueryArray จะรันอาร์เรย์ของเคียวรีและรอให้แต่ละอันเสร็จสิ้นภายในลูป (อย่าทำสิ่งนี้ในรหัสการผลิต) จากนั้น เราจะแก้ไขสัญญาเมื่อเราดำเนินการค้นหาล่าสุดในรายการเท่านั้น เหตุผลในการใช้อาร์เรย์ก็คือจำนวนการสืบค้นข้อมูลดังกล่าวจะเพิ่มขึ้นตามจำนวนตารางในฐานข้อมูลของเราที่เพิ่มขึ้น

เปิด runQuery.js และวางรหัสต่อไปนี้:

 import { createTables, insertIntoTables } from './queryFunctions'; (async () => { await createTables(); await insertIntoTables(); })();

นี่คือที่ที่เราเรียกใช้ฟังก์ชันเพื่อสร้างตารางและแทรกข้อความในตาราง มาเพิ่มคำสั่งในส่วน scripts ของ package.json เพื่อรันไฟล์นี้กัน

 "runQuery": "babel-node ./src/utils/runQuery"

ตอนนี้เรียกใช้:

 yarn runQuery

หากคุณตรวจสอบฐานข้อมูลของคุณ คุณจะเห็นว่าตาราง messages ถูกสร้างขึ้นและข้อความนั้นถูกแทรกลงในตารางแล้ว

หากคุณกำลังใช้ ElephantSQL ในหน้ารายละเอียดฐานข้อมูล ให้คลิกที่ BROWSER จากเมนูการนำทางด้านซ้าย เลือกตาราง messages และคลิก Execute คุณควรเห็นข้อความจากไฟล์ query.js

มาสร้างตัวควบคุมและเส้นทางเพื่อแสดงข้อความจากฐานข้อมูลของเรา

สร้างไฟล์คอนโทรลเลอร์ใหม่ src/controllers/messages.js แล้ววางโค้ดต่อไปนี้:

 import Model from '../models/model'; const messagesModel = new Model('messages'); export const messagesPage = async (req, res) => { try { const data = await messagesModel.select('name, message'); res.status(200).json({ messages: data.rows }); } catch (err) { res.status(200).json({ messages: err.stack }); } };

เรานำเข้าคลาส Model ของเราและสร้างอินสแตนซ์ใหม่ของโมเดลนั้น นี่แสดงถึงตาราง messages ในฐานข้อมูลของเรา จากนั้นเราใช้วิธีการ select ของแบบจำลองเพื่อสืบค้นฐานข้อมูลของเรา ข้อมูล ( name และ message ) ที่เราได้รับจะถูกส่งเป็น JSON ในการตอบกลับ

เรากำหนดตัวควบคุม messagesPage เป็นฟังก์ชัน async เนื่องจากข้อความค้นหา node-postgres ส่งคืนสัญญา เราจึง await ผลลัพธ์ของข้อความค้นหานั้น หากเราพบข้อผิดพลาดระหว่างการสืบค้น เราจะตรวจจับและแสดงสแต็กให้ผู้ใช้เห็น คุณควรตัดสินใจว่าจะเลือกจัดการกับข้อผิดพลาดอย่างไร

เพิ่มปลายทางรับข้อความไปที่ src/routes/index.js และอัปเดตบรรทัดการนำเข้า

 # update the import line import { indexPage, messagesPage } from '../controllers'; # add the get messages endpoint indexRouter.get('/messages', messagesPage)

ไปที่ https://localhost:3000/v1/messages และคุณควรเห็นข้อความที่แสดงดังที่แสดงด้านล่าง

ข้อความจากฐานข้อมูล (ตัวอย่างขนาดใหญ่)

ตอนนี้ มาอัปเดตไฟล์ทดสอบของเรากัน เมื่อทำ TDD คุณมักจะเขียนการทดสอบก่อนที่จะติดตั้งโค้ดที่ทำให้การทดสอบผ่าน ฉันกำลังใช้แนวทางที่ตรงกันข้ามกับที่นี้ เพราะเรากำลังดำเนินการตั้งค่าฐานข้อมูลอยู่

สร้างไฟล์ใหม่ hooks.js ในโฟลเดอร์ test/ และป้อนรหัสด้านล่าง:

 import { dropTables, createTables, insertIntoTables, } from '../src/utils/queryFunctions'; before(async () => { await createTables(); await insertIntoTables(); }); after(async () => { await dropTables(); });

เมื่อการทดสอบของเราเริ่มต้นขึ้น Mocha จะค้นหาไฟล์นี้และดำเนินการก่อนที่จะเรียกใช้ไฟล์ทดสอบใดๆ มันรัน before hook เพื่อสร้างฐานข้อมูลและแทรกบางรายการเข้าไป ไฟล์ทดสอบจะทำงานหลังจากนั้น เมื่อการทดสอบเสร็จสิ้น Mocha จะรัน after hook ที่เราปล่อยฐานข้อมูล สิ่งนี้ทำให้มั่นใจได้ว่าทุกครั้งที่เราทำการทดสอบ เราทำด้วยบันทึกที่สะอาดและใหม่ในฐานข้อมูลของเรา

สร้างไฟล์ทดสอบใหม่ test/messages.test.js และเพิ่มโค้ดด้านล่าง:

 import { expect, server, BASE_URL } from './setup'; describe('Messages', () => { it('get messages page', done => { server .get(`${BASE_URL}/messages`) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.messages).to.be.instanceOf(Array); res.body.messages.forEach(m => { expect(m).to.have.property('name'); expect(m).to.have.property('message'); }); done(); }); }); });

เราขอยืนยันว่าผลลัพธ์ของการเรียกไปยัง /messages เป็นอาร์เรย์ สำหรับแต่ละอ็อบเจ็กต์ข้อความ เรายืนยันว่ามีคุณสมบัติ name และ message

ขั้นตอนสุดท้ายในส่วนนี้คือการอัปเดตไฟล์ CI

เพิ่มส่วนต่อไปนี้ในไฟล์ .travis.yml :

 services: - postgresql addons: postgresql: "10" apt: packages: - postgresql-10 - postgresql-client-10 before_install: - sudo cp /etc/postgresql/{9.6,10}/main/pg_hba.conf - sudo /etc/init.d/postgresql restart

สิ่งนี้แนะนำให้ Travis หมุนฐานข้อมูล PostgreSQL 10 ก่อนทำการทดสอบของเรา

เพิ่มคำสั่งเพื่อสร้างฐานข้อมูลเป็นรายการแรกในส่วน before_script :

 # add this as the first line in the before_script section - psql -c 'create database testdb;' -U postgres

สร้างตัวแปรสภาพแวดล้อม CONNECTION_STRING บน Travis และใช้ค่าด้านล่าง:

 CONNECTION_STRING="postgresql://postgres:postgres@localhost:5432/testdb"

เพิ่มส่วนต่อไปนี้ในไฟล์ .appveyor.yml :

 before_test: - SET PGUSER=postgres - SET PGPASSWORD=Password12! - PATH=C:\Program Files\PostgreSQL\10\bin\;%PATH% - createdb testdb services: - postgresql101

เพิ่มตัวแปรสภาพแวดล้อมสตริงการเชื่อมต่อไปยัง appveyor ใช้บรรทัดด้านล่าง:

 CONNECTION_STRING=postgresql://postgres:Password12!@localhost:5432/testdb

ตอนนี้ยอมรับการเปลี่ยนแปลงของคุณและกดไปที่ GitHub การทดสอบของคุณควรผ่านทั้ง Travis CI และ AppVeyor

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 07-connect-postgres

หมายเหตุ : ฉันหวังว่าทุกอย่างจะทำงานได้ดีในตอนท้ายของคุณ แต่ในกรณีที่คุณควรประสบปัญหาด้วยเหตุผลบางประการ คุณสามารถตรวจสอบรหัสของฉันใน repo ได้ตลอดเวลา!

ตอนนี้ มาดูกันว่าเราจะเพิ่มข้อความไปยังฐานข้อมูลของเราได้อย่างไร สำหรับขั้นตอนนี้ เราต้องการวิธีส่งคำขอ POST ไปยัง URL ของเรา ฉันจะใช้บุรุษไปรษณีย์เพื่อส่งคำขอ POST

ไปที่เส้นทาง TDD และอัปเดตการทดสอบของเราเพื่อสะท้อนถึงสิ่งที่เราคาดว่าจะบรรลุ

เปิด test/message.test.js และเพิ่มกรณีทดสอบด้านล่าง:

 it('posts messages', done => { const data = { name: 'some name', message: 'new message' }; server .post(`${BASE_URL}/messages`) .send(data) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.messages).to.be.instanceOf(Array); res.body.messages.forEach(m => { expect(m).to.have.property('id'); expect(m).to.have.property('name', data.name); expect(m).to.have.property('message', data.message); }); done(); }); });

การทดสอบนี้ส่งคำขอ POST ไปยังปลายทาง /v1/messages และเราคาดว่าอาร์เรย์จะถูกส่งคืน เรายังตรวจสอบคุณสมบัติ id name และ message ในอาร์เรย์ด้วย

ทำการทดสอบเพื่อดูว่ากรณีนี้ล้มเหลว ตอนนี้ขอแก้ไขมัน

ในการส่งคำขอโพสต์ เราใช้วิธีการโพสต์ของเซิร์ฟเวอร์ เรายังส่งชื่อและข้อความที่เราต้องการแทรก เราคาดว่าการตอบกลับจะเป็นอาร์เรย์ พร้อมด้วย id คุณสมบัติและข้อมูลอื่นๆ ที่ประกอบเป็นข้อความค้นหา id เป็นหลักฐานว่ามีการแทรกบันทึกลงในฐานข้อมูล

เปิด src/models/model.js และเพิ่มวิธีการ insert :

 async insertWithReturn(columns, values) { const query = ` INSERT INTO ${this.table}(${columns}) VALUES (${values}) RETURNING id, ${columns} `; return this.pool.query(query); }

ซึ่งเป็นวิธีการที่ช่วยให้เราสามารถแทรกข้อความลงในฐานข้อมูลได้ หลังจากแทรกรายการแล้ว จะส่งคืน id name และ message

เปิด src/controllers/messages.js และเพิ่มคอนโทรลเลอร์ด้านล่าง:

 export const addMessage = async (req, res) => { const { name, message } = req.body; const columns = 'name, message'; const values = `'${name}', '${message}'`; try { const data = await messagesModel.insertWithReturn(columns, values); res.status(200).json({ messages: data.rows }); } catch (err) { res.status(200).json({ messages: err.stack }); } };

เราทำลายโครงสร้างคำขอเพื่อรับชื่อและข้อความ จากนั้นเราใช้ค่าเพื่อสร้างสตริงการสืบค้น SQL ซึ่งเราดำเนินการด้วยเมธอด insertWithReturn ของโมเดลของเรา

เพิ่มจุดสิ้นสุด POST ด้านล่างไปที่ /src/routes/index.js และอัปเดตบรรทัดการนำเข้าของคุณ

 import { indexPage, messagesPage, addMessage } from '../controllers'; indexRouter.post('/messages', addMessage);

ทำการทดสอบของคุณเพื่อดูว่าผ่านหรือไม่

เปิดบุรุษไปรษณีย์และส่งคำขอ POST ไปยังปลายทาง messages หากคุณเพิ่งทำการทดสอบ อย่าลืมเรียกใช้ yarn query เพื่อสร้างตาราง messages ขึ้นใหม่

 yarn query 
คำขอ POST ไปยังจุดสิ้นสุดข้อความ (ตัวอย่างขนาดใหญ่)
GET ขอแสดงข้อความที่เพิ่มใหม่ (ตัวอย่างขนาดใหญ่)

ยอมรับการเปลี่ยนแปลงของคุณและกดไปที่ GitHub การทดสอบของคุณควรผ่านทั้ง Travis และ AppVeyor ความครอบคลุมในการทดสอบของคุณจะลดลงเล็กน้อย แต่ก็ไม่เป็นไร

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 08-post-to-db

มิดเดิลแวร์

การสนทนาเกี่ยวกับ Express ของเราจะไม่สมบูรณ์หากไม่ได้พูดถึงมิดเดิลแวร์ เอกสาร Express อธิบายมิดเดิลแวร์ดังนี้:

“[...] ฟังก์ชั่นที่มีสิทธิ์เข้าถึงวัตถุคำขอ ( req ) วัตถุตอบสนอง ( res ) และฟังก์ชั่นมิดเดิลแวร์ถัดไปในรอบการตอบกลับคำขอของแอปพลิเคชัน ฟังก์ชันมิดเดิลแวร์ตัวถัดไปจะแสดงโดยตัวแปรชื่อ next

มิดเดิลแวร์สามารถทำหน้าที่ได้หลายอย่าง เช่น การรับรองความถูกต้อง การแก้ไขเนื้อหาคำขอ และอื่นๆ ดูเอกสาร Express เกี่ยวกับการใช้มิดเดิลแวร์

เราจะเขียนมิดเดิลแวร์อย่างง่ายที่ปรับเปลี่ยนเนื้อหาคำขอ มิดเดิลแวร์ของเราจะผนวกคำว่า SAYS: ต่อท้ายข้อความขาเข้าก่อนที่จะบันทึกลงในฐานข้อมูล

ก่อนที่เราจะเริ่มต้น ให้ปรับเปลี่ยนการทดสอบของเราเพื่อให้สะท้อนถึงสิ่งที่เราต้องการบรรลุ

เปิด test/messages.test.js และแก้ไขบรรทัดที่คาดหวังสุดท้ายในกรณีทดสอบ posts message :

 it('posts messages', done => { ... expect(m).to.have.property('message', `SAYS: ${data.message}`); # update this line ... });

เรากำลังยืนยันว่าได้เพิ่มสตริง SAYS: ต่อท้ายข้อความแล้ว เรียกใช้การทดสอบของคุณเพื่อให้แน่ใจว่ากรณีทดสอบนี้ล้มเหลว

ตอนนี้ มาเขียนโค้ดเพื่อให้ผ่านการทดสอบกัน

สร้าง middleware/ โฟลเดอร์ใหม่ภายในโฟลเดอร์ src/ สร้างสองไฟล์ภายในโฟลเดอร์นี้:

  • Middleware.js
  • index.js

ป้อนรหัสด้านล่างใน Middleware.js :

 export const modifyMessage = (req, res, next) => { req.body.message = `SAYS: ${req.body.message}`; next(); };

ที่นี่ เราผนวกสตริง SAYS: ต่อท้ายข้อความในเนื้อหาของคำขอ หลังจากทำเช่นนั้น เราต้องเรียกใช้ฟังก์ชัน next() เพื่อส่งผ่านการดำเนินการไปยังฟังก์ชันถัดไปในห่วงโซ่การตอบกลับคำขอ มิดเดิลแวร์ทุกตัวจะต้องเรียกใช้ฟังก์ชัน next เพื่อส่งการดำเนินการไปยังมิดเดิลแวร์ตัวถัดไปในวงจรการตอบกลับคำขอ

ป้อนรหัสด้านล่างใน index.js :

 # export everything from the middleware file export * from './middleware';

สิ่งนี้จะส่งออกมิดเดิลแวร์ที่เรามีในไฟล์ /middleware.js สำหรับตอนนี้ เรามีเพียงมิดเดิลแวร์ modifyMessage

เปิด src/routes/index.js และเพิ่มมิดเดิลแวร์ในห่วงโซ่การตอบกลับคำขอข้อความโพสต์

 import { modifyMessage } from '../middleware'; indexRouter.post('/messages', modifyMessage, addMessage);

เราจะเห็นว่าฟังก์ชัน modifiedMessage มาก่อน addMessage modifyMessage เราเรียกใช้ฟังก์ชัน addMessage โดยการเรียก next ในมิดเดิลแวร์ modifyMessage ในการทดลอง ให้แสดงความคิดเห็นในบรรทัด next() ตรงกลาง modifyMessage และดูคำขอหยุดทำงาน

เปิดบุรุษไปรษณีย์และสร้างข้อความใหม่ คุณควรเห็นสตริงที่ต่อท้าย

ข้อความที่แก้ไขโดยมิดเดิลแวร์ (ตัวอย่างขนาดใหญ่)

นี่เป็นจุดที่ดีในการยอมรับการเปลี่ยนแปลงของเรา

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 09-middleware

การจัดการข้อผิดพลาดและมิดเดิลแวร์แบบอะซิงโครนัส

ข้อผิดพลาดหลีกเลี่ยงไม่ได้ในแอปพลิเคชันใด ๆ งานต่อหน้านักพัฒนาคือวิธีจัดการกับข้อผิดพลาดให้สวยงามที่สุด

ในด่วน:

การจัดการข้อผิดพลาด หมายถึงวิธีที่ Express ตรวจจับและประมวลผลข้อผิดพลาดที่เกิดขึ้นทั้งแบบซิงโครนัสและแบบอะซิงโครนัส

หากเราเขียนเพียงฟังก์ชันซิงโครนัส เราอาจไม่ต้องกังวลมากเกี่ยวกับการจัดการข้อผิดพลาดเนื่องจาก Express จัดการสิ่งเหล่านั้นได้อย่างดีเยี่ยมอยู่แล้ว ตามเอกสาร:

“ข้อผิดพลาดที่เกิดขึ้นในโค้ดซิงโครนัสภายในตัวจัดการเส้นทางและมิดเดิลแวร์ไม่ต้องดำเนินการใดๆ เพิ่มเติม”

แต่เมื่อเราเริ่มเขียนตัวจัดการเราเตอร์และมิดเดิลแวร์แบบอะซิงโครนัสแล้ว เราก็ต้องทำการจัดการข้อผิดพลาด

มิดเดิลแวร์ modifyMessage ของเราเป็นฟังก์ชันแบบซิงโครนัส หากเกิดข้อผิดพลาดในฟังก์ชันนั้น Express จะจัดการกับมันได้ดี มาดูกันว่าเราจัดการกับข้อผิดพลาดในมิดเดิลแวร์แบบอะซิงโครนัสอย่างไร

ก่อนสร้างข้อความ เราต้องการรูปภาพจาก Lorem Picsum API โดยใช้ URL นี้ https://picsum.photos/id/0/info นี่เป็นการดำเนินการแบบอะซิงโครนัสที่อาจสำเร็จหรือล้มเหลว และนั่นก็เป็นกรณีที่เราจะต้องรับมือ

เริ่มต้นด้วยการติดตั้ง Axios

 # install axios yarn add axios

เปิด src/middleware/middleware.js และเพิ่มฟังก์ชันด้านล่าง:

 export const performAsyncAction = async (req, res, next) => { try { await axios.get('https://picsum.photos/id/0/info'); next(); } catch (err) { next(err); } };

ในฟังก์ชัน async นี้ เรา await การเรียกไปยัง API (จริงๆ แล้วเราไม่ต้องการข้อมูลที่ส่งคืน) และหลังจากนั้นจะเรียกใช้ฟังก์ชัน next ในห่วงโซ่คำขอ หากคำขอล้มเหลว เราจะตรวจพบข้อผิดพลาดและส่งต่อไปยัง next เมื่อ Express เห็นข้อผิดพลาดนี้ จะข้ามมิดเดิลแวร์อื่นๆ ทั้งหมดในเชน หากเราไม่โทร next(err) คำขอจะหยุดทำงาน หากเราเรียกเพียง next() โดยไม่ err คำขอจะดำเนินการราวกับว่าไม่มีอะไรเกิดขึ้นและข้อผิดพลาดจะไม่ถูกจับ

นำเข้าฟังก์ชันนี้และเพิ่มไปยังกลุ่มมิดเดิลแวร์ของเส้นทางข้อความโพสต์:

 import { modifyMessage, performAsyncAction } from '../middleware'; indexRouter.post('/messages', modifyMessage, performAsyncAction, addMessage);

เปิด src/app.js และเพิ่มโค้ดด้านล่างก่อนบรรทัด export default app

 app.use((err, req, res, next) => { res.status(400).json({ error: err.stack }); }); export default app;

นี่คือตัวจัดการข้อผิดพลาดของเรา ตามเอกสารการจัดการข้อผิดพลาด Express:

“[...] ฟังก์ชั่นการจัดการข้อผิดพลาดมีสี่อาร์กิวเมนต์แทนที่จะเป็นสาม: (err, req, res, next)

โปรดทราบว่าตัวจัดการข้อผิดพลาดนี้ต้องอยู่ล่าสุด หลังจากการ app.use() ทุกครั้ง เมื่อเราพบข้อผิดพลาด เราจะส่งคืนการติดตามสแต็กด้วยรหัสสถานะ 400 คุณสามารถทำสิ่งที่คุณต้องการด้วยข้อผิดพลาด คุณอาจต้องการบันทึกหรือส่งไปที่อื่น

นี่เป็นสถานที่ที่ดีในการยอมรับการเปลี่ยนแปลงของคุณ

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 10-async-middleware

ปรับใช้กับ Heroku

  1. ในการเริ่มต้น ไปที่ https://www.heroku.com/ และเข้าสู่ระบบหรือลงทะเบียน
  2. ดาวน์โหลดและติดตั้ง Heroku CLI จากที่นี่
  3. เปิดเทอร์มินัลในโฟลเดอร์โครงการเพื่อเรียกใช้คำสั่ง
 # login to heroku on command line heroku login

การดำเนินการนี้จะเปิดหน้าต่างเบราว์เซอร์และขอให้คุณลงชื่อเข้าใช้บัญชี Heroku ของคุณ

เข้าสู่ระบบเพื่อให้เทอร์มินัลเข้าถึงบัญชี Heroku ของคุณ และสร้างแอป heroku ใหม่โดยเรียกใช้:

 #app name is up to you heroku create app-name

การดำเนินการนี้จะสร้างแอปบน Heroku และส่งคืน URL สองรายการ

 # app production url and git url https://app-name.herokuapp.com/ | https://git.heroku.com/app-name.git

คัดลอก URL ทางด้านขวาและเรียกใช้คำสั่งด้านล่าง โปรดทราบว่าขั้นตอนนี้เป็นทางเลือก เนื่องจากคุณอาจพบว่า Heroku ได้เพิ่ม URL ระยะไกลแล้ว

 # add heroku remote url git remote add heroku https://git.heroku.com/my-shiny-new-app.git

เปิดเทอร์มินัลด้านข้างและเรียกใช้คำสั่งด้านล่าง ข้อมูลนี้จะแสดงบันทึกแอปแบบเรียลไทม์ตามที่แสดงในรูปภาพ

 # see process logs heroku logs --tail
บันทึกของ Heroku (ตัวอย่างขนาดใหญ่)

รันคำสั่งสามคำสั่งต่อไปนี้เพื่อตั้งค่าตัวแปรสภาพแวดล้อมที่จำเป็น:

 heroku config:set TEST_ENV_VARIABLE="Environment variable is coming across." heroku config:set CONNECTION_STRING=your-db-connection-string-here. heroku config:set NPM_CONFIG_PRODUCTION=false

อย่าลืมในสคริปต์ของเรา เราตั้งค่า:

 "prestart": "babel ./src --out-dir build", "start": "node ./build/bin/www",

ในการเริ่มแอป จะต้องคอมไพล์ลงไปที่ ES5 โดยใช้ babel ในขั้นตอนแบบ prestart เพราะ babel มีอยู่ในการพึ่งพาการพัฒนาของเราเท่านั้น เราต้องตั้งค่า NPM_CONFIG_PRODUCTION false เพื่อให้สามารถติดตั้งได้เช่นกัน

หากต้องการยืนยันว่าตั้งค่าทุกอย่างถูกต้อง ให้เรียกใช้คำสั่งด้านล่าง คุณยังสามารถไปที่แท็บ settings ในหน้าแอปและคลิก Reveal Config Vars

 # check configuration variables heroku config

ตอนนี้รัน git push heroku

หากต้องการเปิดแอป ให้เรียกใช้:

 # open /v1 route heroku open /v1 # open /v1/messages route heroku open /v1/messages

เช่นเดียวกับฉัน คุณกำลังใช้ฐานข้อมูล PostgresSQL เดียวกันสำหรับทั้งการพัฒนาและการผลิต คุณอาจพบว่าทุกครั้งที่คุณทำการทดสอบ ฐานข้อมูลจะถูกลบ หากต้องการสร้างใหม่ คุณสามารถเรียกใช้คำสั่งใดคำสั่งหนึ่งต่อไปนี้:

 # run script locally yarn runQuery # run script with heroku heroku run yarn runQuery

การปรับใช้อย่างต่อเนื่อง (CD) ด้วย Travis

ตอนนี้เรามาเพิ่ม Continuous Deployment (CD) เพื่อทำให้โฟลว์ CI/CD สมบูรณ์ เราจะปรับใช้จาก Travis หลังจากทำการทดสอบสำเร็จทุกครั้ง

ขั้นตอนแรกคือการติดตั้ง Travis CI (คุณสามารถดูคำแนะนำในการติดตั้งได้ที่นี่) หลังจากติดตั้ง Travis CI สำเร็จแล้ว ให้เข้าสู่ระบบโดยใช้คำสั่งด้านล่าง (โปรดทราบว่าควรทำสิ่งนี้ในที่เก็บโครงการของคุณ)

 # login to travis travis login --pro # use this if you're using two factor authentication travis login --pro --github-token enter-github-token-here

หากโปรเจ็กต์ของคุณโฮสต์บน travis-ci.org ให้ลบ --pro แฟล็ก หากต้องการรับโทเค็น GitHub ให้ไปที่หน้าการตั้งค่านักพัฒนาซอฟต์แวร์ในบัญชีของคุณแล้วสร้างใหม่ ใช้ได้เฉพาะเมื่อบัญชีของคุณปลอดภัยด้วย 2FA

เปิด .travis.yml และเพิ่มส่วนปรับใช้:

 deploy: provider: heroku app: master: app-name

ที่นี่เราระบุว่าเราต้องการปรับใช้กับ Heroku ส่วนย่อยของแอประบุว่าเราต้องการปรับใช้สาขา master ของ repo ของเรากับ app-name แอปบน Heroku เป็นไปได้ที่จะปรับใช้สาขาต่างๆ กับแอพต่างๆ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับตัวเลือกที่มีได้ที่นี่

เรียกใช้คำสั่งด้านล่างเพื่อเข้ารหัสคีย์ API ของ Heroku และเพิ่มลงในส่วนการปรับใช้:

 # encrypt heroku API key and add to .travis.yml travis encrypt $(heroku auth:token) --add deploy.api_key --pro

สิ่งนี้จะเพิ่มส่วนย่อยด้านล่างในส่วนการปรับใช้

 api_key: secure: very-long-encrypted-api-key-string

ตอนนี้ยอมรับการเปลี่ยนแปลงของคุณและกดไปที่ GitHub ขณะตรวจสอบบันทึกของคุณ คุณจะเห็นบิลด์ทริกเกอร์ทันทีที่การทดสอบ Travis เสร็จสิ้น ด้วยวิธีนี้ หากเรามีการทดสอบที่ล้มเหลว การเปลี่ยนแปลงจะไม่ถูกนำไปใช้ ในทำนองเดียวกัน หากบิลด์ล้มเหลว การทดสอบทั้งหมดจะล้มเหลว ขั้นตอน CI/CD เสร็จสมบูรณ์

  • สาขาที่เกี่ยวข้องใน repo ของฉันคือ 11-cd

บทสรุป

ถ้าคุณมาไกลถึงขนาดนี้แล้ว ฉันพูดว่า "ยกนิ้วให้!" ในบทช่วยสอนนี้ เราสร้างโปรเจ็กต์ Express ใหม่ได้สำเร็จ เราดำเนินการกำหนดค่าการพึ่งพาการพัฒนาและการรวมอย่างต่อเนื่อง (CI) จากนั้นเราเขียนฟังก์ชันอะซิงโครนัสเพื่อจัดการคำขอไปยังจุดปลาย API ของเรา — เสร็จสิ้นด้วยการทดสอบ จากนั้นเราดูสั้น ๆ เกี่ยวกับการจัดการข้อผิดพลาด สุดท้าย เราได้ปรับใช้โครงการของเรากับ Heroku และกำหนดค่าการปรับใช้อย่างต่อเนื่อง

ตอนนี้คุณมีเทมเพลตสำหรับโปรเจ็กต์ส่วนหลังถัดไปแล้ว เราได้ทำเพียงพอที่จะให้คุณเริ่มต้นได้ แต่คุณควรเรียนรู้ต่อไปเพื่อก้าวต่อไป อย่าลืมตรวจสอบเอกสาร Express.js ด้วย หากคุณต้องการใช้ MongoDB แทน PostgreSQL ฉันมีเทมเพลตที่ทำหน้าที่นั้นอย่างแน่นอน คุณสามารถตรวจสอบการตั้งค่า มีความแตกต่างเพียงไม่กี่จุด

ทรัพยากร

  • “สร้างแบ็กเอนด์ Express API ด้วย MongoDB ” Orji Chidi Matthew, GitHub
  • “คำแนะนำสั้น ๆ ในการเชื่อมต่อมิดเดิลแวร์” Stephen Sugden
  • “เทมเพลต Express API” GitHub
  • “AppVeyor กับ Travis CI” StackShare
  • “The Heroku CLI,” Heroku Dev Center
  • “การปรับใช้ Heroku” Travis CI
  • “การใช้มิดเดิลแวร์” Express.js
  • “การจัดการข้อผิดพลาด” Express.js
  • “เริ่มต้น” มอคค่า
  • nyc (GitHub)
  • ElephantSQL
  • บุรุษไปรษณีย์
  • ด่วน
  • Travis CI
  • รหัสภูมิอากาศ
  • PostgreSQL
  • pgAdmin