วิธีตั้งค่าโครงการแบ็กเอนด์ Express API ด้วย PostgreSQL
เผยแพร่แล้ว: 2022-03-10เราจะใช้วิธี 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 สร้างโปรเจ็กต์ในไดเร็กทอรีปัจจุบัน
ตอนนี้เราจะดำเนินการทำความสะอาดบ้าน
- ลบไฟล์ index/users.js
- ลบโฟลเดอร์
public/
และviews/
- เปลี่ยนชื่อไฟล์ bin/www เป็น bin/www.js
- ถอนการติดตั้ง
jade
ด้วยคำสั่งyarn remove jade
- สร้างโฟลเดอร์ใหม่ชื่อ
src/
และย้ายข้อมูลต่อไปนี้เข้าไปข้างใน: 1. ไฟล์ app.js 2.bin/
โฟลเดอร์ 3.routes/
โฟลเดอร์ภายใน - เปิด 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"
- สคริปต์แบบ
prestart
สร้างเนื้อหาของโฟลเดอร์src/
และวางไว้ในโฟลเดอร์build/
เมื่อคุณออกคำสั่งyarn start
สคริปต์นี้จะรันก่อนสคริปต์start
- สคริปต์
start
ให้บริการเนื้อหาของโฟลเดอร์build/
แทนโฟลเดอร์src/
ที่เราให้บริการก่อนหน้านี้ นี่คือสคริปต์ที่คุณจะใช้เมื่อให้บริการไฟล์ในการผลิต อันที่จริง บริการต่างๆ เช่น Heroku จะเรียกใช้สคริปต์นี้โดยอัตโนมัติเมื่อคุณปรับใช้ -
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) และทุกครั้งที่เราสร้างคำขอดึง สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อทำการดึงคำขอโดยแสดงให้เราเห็นว่าโค้ดใหม่ของเราทำการทดสอบไม่ได้หรือไม่
- ไปที่ travis-ci.com หรือ travis-ci.org และสร้างบัญชีหากคุณยังไม่มี คุณต้องลงทะเบียนด้วยบัญชี GitHub ของคุณ
- วางเมาส์เหนือลูกศรแบบเลื่อนลงข้างรูปโปรไฟล์ของคุณ แล้วคลิก
settings
- ภายใต้แท็บ
Repositories
คลิกManage repositories on Github
เพื่อเปลี่ยนเส้นทางไปยัง Github - ในหน้า GitHub ให้เลื่อนลงไปที่
Repository access
และคลิกช่องทำเครื่องหมายถัดจากOnly select repositories
- คลิกดรอปดาวน์
Select repositories
และค้นหาexpress-api-template
repo คลิกเพื่อเพิ่มลงในรายการที่เก็บที่คุณต้องการเพิ่มในtravis-ci
- คลิก
Approve and install
และรอเพื่อเปลี่ยนเส้นทางกลับไปที่travis-ci
- ที่ด้านบนของหน้า repo ใกล้กับชื่อ repo ให้คลิกที่ไอคอน
build unknown
จากโมดอลรูปภาพสถานะ เลือกมาร์กดาวน์จากดรอปดาวน์รูปแบบ - คัดลอกโค้ดผลลัพธ์แล้ววางลงในไฟล์ README.md ของคุณ
- ในหน้าโครงการ ให้คลิก
More options
>Settings
ภายใต้ส่วนEnvironment Variables
เพิ่มตัวแปร envTEST_ENV_VARIABLE
เมื่อป้อนค่า ต้องแน่ใจว่าใส่ไว้ในเครื่องหมายคำพูดคู่ เช่น"Environment variable is coming across."
- สร้างไฟล์ .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 จะทำให้ใช้งานได้นอกเครื่องของเรา
- เยี่ยมชม coveralls.io และลงชื่อเข้าใช้หรือลงทะเบียนด้วยบัญชี Github ของคุณ
- วางเมาส์เหนือด้านซ้ายของหน้าจอเพื่อแสดงเมนูการนำทาง คลิกที่
ADD REPOS
- ค้นหา
express-api-template
repo และเปิดการครอบคลุมโดยใช้ปุ่มสลับที่ด้านซ้ายมือ หากคุณหาไม่พบ ให้คลิกที่SYNC REPOS
ที่มุมขวาบนแล้วลองอีกครั้ง โปรดทราบว่า repo ของคุณต้องเป็นสาธารณะ เว้นแต่คุณจะมีบัญชี PRO - คลิกรายละเอียดเพื่อไปที่หน้ารายละเอียดการซื้อคืน
- สร้างไฟล์ .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
- ไปที่ codeclimate.com และคลิกที่ 'ลงทะเบียนกับ GitHub' เข้าสู่ระบบถ้าคุณมีบัญชีอยู่แล้ว
- เมื่ออยู่ในแดชบอร์ดของคุณ ให้คลิกที่
Add a repository
- ค้นหา
express-api-template
repo จากรายการและคลิกที่Add Repo
- รอให้บิลด์เสร็จสมบูรณ์และเปลี่ยนเส้นทางไปยังแดชบอร์ด repo
- ใต้
Codebase Summary
ให้คลิกที่Test Coverage
ใต้เมนูTest coverage
ให้คัดลอกTEST REPORTER ID
แล้ววางลงใน . travis.yml ของคุณเป็นค่าCC_TEST_REPORTER_ID
- ยังคงอยู่ในหน้าเดียวกัน ที่การนำทางด้านซ้ายมือ ภายใต้
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 ของฉันคือ 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.
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
ยอมรับการเปลี่ยนแปลงของคุณและกดไปที่ 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
- ในการเริ่มต้น ไปที่ https://www.heroku.com/ และเข้าสู่ระบบหรือลงทะเบียน
- ดาวน์โหลดและติดตั้ง Heroku CLI จากที่นี่
- เปิดเทอร์มินัลในโฟลเดอร์โครงการเพื่อเรียกใช้คำสั่ง
# 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 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