Cara Mengatur Proyek Backend API Ekspres Dengan PostgreSQL
Diterbitkan: 2022-03-10Kami akan mengambil pendekatan Test-Driven Development (TDD) dan tugas Continuous Integration (CI) untuk menjalankan pengujian kami secara otomatis di Travis CI dan AppVeyor, lengkap dengan kualitas kode dan pelaporan cakupan. Kita akan belajar tentang pengontrol, model (dengan PostgreSQL), penanganan kesalahan, dan middleware Express asinkron. Terakhir, kita akan menyelesaikan pipeline CI/CD dengan mengonfigurasi penerapan otomatis di Heroku.
Kedengarannya seperti banyak, tetapi tutorial ini ditujukan untuk pemula yang siap untuk mencoba proyek back-end dengan beberapa tingkat kerumitan, dan yang mungkin masih bingung tentang bagaimana semua bagian cocok bersama dalam proyek nyata .
Ini kuat tanpa berlebihan dan dipecah menjadi beberapa bagian yang dapat Anda selesaikan dalam jangka waktu yang wajar.
Mulai
Langkah pertama adalah membuat direktori baru untuk proyek dan memulai proyek simpul baru. Node diperlukan untuk melanjutkan tutorial ini. Jika Anda belum menginstalnya, kunjungi situs web resminya, unduh, dan instal sebelum melanjutkan.
Saya akan menggunakan benang sebagai manajer paket saya untuk proyek ini. Ada petunjuk penginstalan untuk sistem operasi spesifik Anda di sini. Jangan ragu untuk menggunakan npm jika Anda suka.
Buka terminal Anda, buat direktori baru, dan mulai proyek 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
Jawab pertanyaan berikut untuk menghasilkan file package.json . File ini menyimpan informasi tentang proyek Anda. Contoh informasi tersebut mencakup dependensi apa yang digunakannya, perintah untuk memulai proyek, dan seterusnya.
Anda sekarang dapat membuka folder proyek di editor pilihan Anda. Saya menggunakan kode visual studio. Ini adalah IDE gratis dengan banyak plugin untuk membuat hidup Anda lebih mudah, dan tersedia untuk semua platform utama. Anda dapat mengunduhnya dari situs web resmi.
Buat file berikut di folder proyek:
- README.md
- .editorconfig
Berikut adalah deskripsi tentang apa yang dilakukan .editorconfig dari situs web EditorConfig. (Anda mungkin tidak membutuhkannya jika Anda bekerja sendiri, tetapi tidak ada salahnya, jadi saya akan meninggalkannya di sini.)
“EditorConfig membantu mempertahankan gaya pengkodean yang konsisten untuk beberapa pengembang yang mengerjakan proyek yang sama di berbagai editor dan IDE.”
Buka .editorconfig
dan rekatkan kode berikut:
root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = false insert_final_newline = true
Tanda [*]
berarti kita ingin menerapkan aturan yang ada di bawahnya ke setiap file dalam proyek. Kami menginginkan ukuran indentasi dua spasi dan kumpulan karakter UTF-8
. Kami juga ingin memangkas spasi putih dan menyisipkan baris kosong terakhir di file kami.
Buka README.md dan tambahkan nama proyek sebagai elemen tingkat pertama.
# Express API template
Mari tambahkan kontrol versi segera.
# initialize the project folder as a git repository git init
Buat file .gitignore dan masukkan baris berikut:
node_modules/ yarn-error.log .env .nyc_output coverage build/
Ini semua file dan folder yang tidak ingin kami lacak. Kami belum memilikinya di proyek kami, tetapi kami akan melihatnya saat kami melanjutkan.
Pada titik ini, Anda harus memiliki struktur folder berikut.
EXPRESS-API-TEMPLATE ├── .editorconfig ├── .gitignore ├── package.json └── README.md
Saya menganggap ini sebagai poin yang baik untuk melakukan perubahan saya dan mendorongnya ke GitHub.
Memulai Proyek Ekspres Baru
Express adalah kerangka kerja Node.js untuk membangun aplikasi web. Menurut situs resminya, ini adalah
Kerangka kerja web minimalis yang cepat, tanpa opini, untuk Node.js.
Ada kerangka kerja aplikasi web hebat lainnya untuk Node.js, tetapi Express sangat populer, dengan lebih dari 47k bintang GitHub pada saat penulisan ini.
Dalam artikel ini, kita tidak akan banyak membahas semua bagian yang membentuk Express. Untuk diskusi itu, saya sarankan Anda memeriksa seri Jamie. Bagian pertama ada di sini, dan bagian kedua ada di sini.
Instal Express dan mulai proyek Express baru. Dimungkinkan untuk secara manual mengatur server Express dari awal tetapi untuk membuat hidup kita lebih mudah, kita akan menggunakan generator ekspres untuk menyiapkan kerangka aplikasi.
# 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
Bendera -f
memaksa Express untuk membuat proyek di direktori saat ini.
Sekarang kita akan melakukan beberapa operasi pembersihan rumah.
- Hapus file index/users.js .
- Hapus folder
public/
danviews/
. - Ganti nama file bin/www menjadi bin/www.js .
- Uninstall
jade
dengan perintahyarn remove jade
. - Buat folder baru bernama
src/
dan pindahkan yang berikut di dalamnya: 1. file app.js 2. folderbin/
3. folder routeroutes/
di dalamnya. - Buka package.json dan perbarui skrip
start
agar terlihat seperti di bawah ini.
"start": "node ./src/bin/www"
Pada titik ini, struktur folder proyek Anda terlihat seperti di bawah ini. Anda dapat melihat bagaimana Kode VS menyoroti perubahan file yang telah terjadi.
EXPRESS-API-TEMPLATE ├── node_modules ├── src | ├── bin │ │ ├── www.js │ ├── routes │ | ├── index.js │ └── app.js ├── .editorconfig ├── .gitignore ├── package.json ├── README.md └── yarn.lock
Buka src/app.js dan ganti konten dengan kode di bawah ini.
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;
Setelah membutuhkan beberapa library, kami menginstruksikan Express untuk menangani setiap permintaan yang datang ke /v1
dengan indexRouter
.
Ganti konten route/index.js dengan kode di bawah ini:
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;
Kami mengambil Express, membuat router darinya dan melayani rute /
, yang mengembalikan kode status 200
dan pesan JSON.
Mulai aplikasi dengan perintah di bawah ini:
# start the app yarn start
Jika Anda telah mengatur semuanya dengan benar, Anda hanya akan melihat $ node ./src/bin/www
di terminal Anda.
Kunjungi https://localhost:3000/v1
di browser Anda. Anda akan melihat pesan berikut:
{ "message": "Welcome to Express API template" }
Ini adalah poin yang baik untuk melakukan perubahan kami.
- Cabang yang sesuai di repo saya adalah 01-install-express.
Mengubah Kode Kita Menjadi ES6
Kode yang dihasilkan oleh express-generator
ada dalam ES5
, tetapi dalam artikel ini, kita akan menulis semua kode kita dalam sintaks ES6
. Jadi, mari kita ubah kode yang ada menjadi ES6
.
Ganti konten route/index.js dengan kode di bawah ini:
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;
Ini adalah kode yang sama seperti yang kita lihat di atas, tetapi dengan pernyataan import dan fungsi panah di handler /
route.
Ganti konten src/app.js dengan kode di bawah ini:
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;
Sekarang mari kita lihat isi dari src/bin/www.js . Kami akan membangunnya secara bertahap. Hapus konten src/bin/www.js
dan tempel di blok kode di bawah ini.
#!/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
Kode ini memeriksa apakah port kustom ditentukan dalam variabel lingkungan. Jika tidak ada yang disetel, nilai port default 3000
disetel pada instans aplikasi, setelah dinormalisasi menjadi string atau angka oleh normalizePort
. Server kemudian dibuat dari modul http
, dengan app
sebagai fungsi panggilan balik.
Baris #!/usr/bin/env node
adalah opsional karena kita akan menentukan node ketika kita ingin mengeksekusi file ini. Tetapi pastikan itu ada di baris 1 dari file src/bin/www.js atau hapus sepenuhnya.
Mari kita lihat fungsi penanganan kesalahan. Salin dan tempel blok kode ini setelah baris tempat server dibuat.
/** * 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);
Fungsi onError
mendengarkan kesalahan di server http dan menampilkan pesan kesalahan yang sesuai. Fungsi onListening
hanya menampilkan port yang didengarkan server ke konsol. Akhirnya, server mendengarkan permintaan masuk di alamat dan port yang ditentukan.
Pada titik ini, semua kode yang ada dalam sintaks ES6
. Hentikan server Anda (gunakan Ctrl + C ) dan jalankan yarn start
. Anda akan mendapatkan kesalahan SyntaxError: Invalid or unexpected token
. Ini terjadi karena Node (pada saat penulisan) tidak mendukung beberapa sintaks yang kami gunakan dalam kode kami.
Sekarang kita akan memperbaikinya di bagian berikut.
Mengonfigurasi Ketergantungan Pengembangan: babel
, nodemon
, eslint
, Dan prettier
Saatnya untuk menyiapkan sebagian besar skrip yang akan kita perlukan pada fase proyek ini.
Instal perpustakaan yang diperlukan dengan perintah di bawah ini. Anda cukup menyalin semuanya dan menempelkannya di terminal Anda. Baris komentar akan dilewati.
# install babel scripts yarn add @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/register @babel/runtime @babel/node --dev
Ini menginstal semua skrip babel yang terdaftar sebagai dependensi pengembangan. Periksa file package.json Anda dan Anda akan melihat bagian devDependencies
. Semua skrip yang diinstal akan terdaftar di sana.
Skrip babel yang kami gunakan dijelaskan di bawah ini:
@babel/cli | Instalasi yang diperlukan untuk menggunakan babel . Ini memungkinkan penggunaan Babel dari terminal dan tersedia sebagai ./node_modules/.bin/babel . |
@babel/core | Fungsionalitas Babel inti. Ini adalah instalasi yang diperlukan. |
@babel/node | Ini bekerja persis seperti CLI Node.js, dengan manfaat tambahan dari kompilasi dengan preset dan plugin babel . Ini diperlukan untuk digunakan dengan nodemon . |
@babel/plugin-transform-runtime | Ini membantu menghindari duplikasi dalam output yang dikompilasi. |
@babel/preset-env | Kumpulan plugin yang bertanggung jawab untuk melakukan transformasi kode. |
@babel/register | Ini mengkompilasi file dengan cepat dan ditetapkan sebagai persyaratan selama pengujian. |
@babel/runtime | Ini berfungsi bersama dengan @babel/plugin-transform-runtime . |
Buat file bernama .babelrc di root proyek Anda dan tambahkan kode berikut:
{ "presets": ["@babel/preset-env"], "plugins": ["@babel/transform-runtime"] }
Mari kita instal nodemon
# install nodemon yarn add nodemon --dev
nodemon
adalah perpustakaan yang memantau kode sumber proyek kami dan secara otomatis memulai ulang server kami setiap kali mengamati perubahan apa pun.
Buat file bernama nodemon.json di root proyek Anda dan tambahkan kode di bawah ini:
{ "watch": [ "package.json", "nodemon.json", ".eslintrc.json", ".babelrc", ".prettierrc", "src/" ], "verbose": true, "ignore": ["*.test.js", "*.spec.js"] }
Kunci watch
memberi tahu nodemon
file dan folder mana yang harus diperhatikan untuk perubahan. Jadi, setiap kali file-file ini berubah, nodemon me-restart server. Tombol ignore
memberi tahu file untuk tidak melihat perubahan.
Sekarang perbarui bagian scripts
file package.json Anda agar terlihat seperti berikut:
# 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"
- skrip
prestart
membangun konten foldersrc/
dan meletakkannya di folderbuild/
. Saat Anda mengeluarkan perintahyarn start
, skrip ini berjalan terlebih dahulu sebelum skripstart
. - skrip
start
sekarang menyajikan konten folderbuild/
alih-alih foldersrc/
yang kami sajikan sebelumnya. Ini adalah skrip yang akan Anda gunakan saat menyajikan file dalam produksi. Faktanya, layanan seperti Heroku secara otomatis menjalankan skrip ini saat Anda menerapkannya. -
yarn startdev
digunakan untuk memulai server selama pengembangan. Mulai sekarang kami akan menggunakan skrip ini saat kami mengembangkan aplikasi. Perhatikan bahwa kita sekarang menggunakanbabel-node
untuk menjalankan aplikasi alih-alihnode
biasa. Bendera--exec
memaksababel-node
untuk melayani foldersrc/
. Untuk skripstart
, kami menggunakannode
karena file di folderbuild/
telah dikompilasi ke ES5.
Jalankan yarn startdev
dan kunjungi https://localhost:3000/v1. Server Anda harus aktif dan berjalan kembali.
Langkah terakhir di bagian ini adalah mengkonfigurasi ESLint
dan prettier
. ESLint membantu menegakkan aturan sintaks sementara lebih cantik membantu memformat kode kita dengan benar agar mudah dibaca.
Tambahkan keduanya dengan perintah di bawah ini. Anda harus menjalankan ini di terminal terpisah sambil mengamati terminal tempat server kami berjalan. Anda akan melihat server memulai ulang. Ini karena kami memantau file package.json untuk perubahan.
# install elsint and prettier yarn add eslint eslint-config-airbnb-base eslint-plugin-import prettier --dev
Sekarang buat file .eslintrc.json di root
proyek dan tambahkan kode di bawah ini:
{ "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] } }
File ini sebagian besar mendefinisikan beberapa aturan yang dengannya eslint
akan memeriksa kode kita. Anda dapat melihat bahwa kami memperluas aturan gaya yang digunakan oleh Airbnb.
Di bagian "rules"
, kami menentukan apakah eslint
harus menampilkan peringatan atau kesalahan saat menemukan pelanggaran tertentu. Misalnya, ini menunjukkan pesan peringatan di terminal kami untuk setiap lekukan yang tidak menggunakan 2 spasi. Nilai [0]
mematikan aturan, yang berarti kita tidak akan mendapatkan peringatan atau kesalahan jika kita melanggar aturan itu.
Buat file bernama .prettierrc dan tambahkan kode di bawah ini:
{ "trailingComma": "es5", "tabWidth": 2, "semi": true, "singleQuote": true }
Kami menyetel lebar tab 2
dan menerapkan penggunaan tanda kutip tunggal di seluruh aplikasi kami. Periksa panduan yang lebih cantik untuk opsi gaya lainnya.
Sekarang tambahkan skrip berikut ke package.json Anda:
# add these one after the other "lint": "./node_modules/.bin/eslint ./src" "pretty": "prettier --write '**/*.{js,json}' '!node_modules/**'" "postpretty": "yarn lint --fix"
Jalankan yarn lint
. Anda akan melihat sejumlah kesalahan dan peringatan di konsol.
Perintah pretty
mempercantik kode kita. Perintah postpretty
dijalankan segera setelahnya. Ini menjalankan perintah lint
dengan flag --fix
ditambahkan. Bendera ini memberi tahu ESLint
untuk secara otomatis memperbaiki masalah linting umum. Dengan cara ini, saya kebanyakan menjalankan perintah yarn pretty
tanpa mempedulikan perintah lint
.
Jalankan yarn pretty
. Anda akan melihat bahwa kami hanya memiliki dua peringatan tentang keberadaan alert
di file bin/www.js .
Inilah yang tampak seperti struktur proyek kami pada saat ini.
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
Anda mungkin menemukan bahwa Anda memiliki file tambahan, yarn-error.log
di root proyek Anda. Tambahkan ke file .gitignore
. Komit perubahan Anda.
- Cabang yang sesuai pada saat ini di repo saya adalah 02-dev-dependencies.
Pengaturan Dan Variabel Lingkungan Di File .env Kami
Di hampir setiap proyek, Anda memerlukan tempat untuk menyimpan pengaturan yang akan digunakan di seluruh aplikasi Anda, misalnya kunci rahasia AWS. Kami menyimpan pengaturan seperti variabel lingkungan. Ini menjauhkan mereka dari pengintaian, dan kami dapat menggunakannya dalam aplikasi kami sesuai kebutuhan.
Saya suka memiliki file settings.js yang dengannya saya membaca semua variabel lingkungan saya. Kemudian, saya dapat merujuk ke file pengaturan dari mana saja dalam aplikasi saya. Anda bebas memberi nama file ini apa pun yang Anda inginkan, tetapi ada semacam konsensus tentang penamaan file tersebut settings.js atau config.js .
Untuk variabel lingkungan kami, kami akan menyimpannya dalam file .env
dan membacanya ke dalam file settings
kami dari sana.
Buat file .env di root proyek Anda dan masukkan baris di bawah ini:
TEST_ENV_VARIABLE="Environment variable is coming across"
Untuk dapat membaca variabel lingkungan ke dalam proyek kami, ada perpustakaan yang bagus, dotenv
yang membaca file .env
kami dan memberi kami akses ke variabel lingkungan yang ditentukan di dalamnya. Mari kita menginstalnya.
# install dotenv yarn add dotenv
Tambahkan file .env ke daftar file yang sedang ditonton oleh nodemon
.
Sekarang, buat file settings.js di dalam folder src/
dan tambahkan kode di bawah ini:
import dotenv from 'dotenv'; dotenv.config(); export const testEnvironmentVariable = process.env.TEST_ENV_VARIABLE;
Kami mengimpor paket dotenv
dan memanggil metode konfigurasinya. Kami kemudian mengekspor testEnvironmentVariable
yang kami atur di file .env
kami.
Buka src/routes/index.js dan ganti kode dengan kode di bawah ini.
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;
Satu-satunya perubahan yang kami buat di sini adalah kami mengimpor testEnvironmentVariable
dari file settings
kami dan menggunakannya sebagai pesan balasan untuk permintaan dari rute /
.
Kunjungi https://localhost:3000/v1 dan Anda akan melihat pesan, seperti yang ditunjukkan di bawah ini.
{ "message": "Environment variable is coming across." }
Dan itu saja. Mulai sekarang kita dapat menambahkan variabel lingkungan sebanyak yang kita inginkan dan kita dapat mengekspornya dari file settings.js kita.
Ini adalah poin yang baik untuk mengkomit kode Anda. Ingatlah untuk mempercantik dan membuat lint kode Anda.
- Cabang yang sesuai pada repo saya adalah 03-env-variables.
Menulis Tes Pertama Kami
Saatnya untuk memasukkan pengujian ke dalam aplikasi kita. Salah satu hal yang membuat pengembang percaya pada kode mereka adalah tes. Saya yakin Anda telah melihat banyak artikel di web yang memberitakan Test-Driven Development (TDD). Tidak dapat cukup ditekankan bahwa kode Anda memerlukan beberapa ukuran pengujian. TDD sangat mudah diikuti saat Anda bekerja dengan Express.js.
Dalam pengujian kami, kami akan melakukan panggilan ke titik akhir API kami dan memeriksa untuk melihat apakah yang dikembalikan adalah yang kami harapkan.
Instal dependensi yang diperlukan:
# install dependencies yarn add mocha chai nyc sinon-chai supertest coveralls --dev
Masing-masing perpustakaan ini memiliki perannya sendiri dalam pengujian kami.
mocha | pelari uji |
chai | digunakan untuk membuat pernyataan |
nyc | kumpulkan laporan cakupan pengujian |
sinon-chai | memperluas pernyataan chai |
supertest | digunakan untuk melakukan panggilan HTTP ke titik akhir API kami |
coveralls | untuk mengunggah cakupan tes ke coveralls.io |
Buat folder test/
baru di root proyek Anda. Buat dua file di dalam folder ini:
- tes/setup.js
- tes/index.test.js
Mocha akan menemukan folder test/
secara otomatis.
Buka test/setup.js dan paste kode di bawah ini. Ini hanyalah file pembantu yang membantu kami mengatur semua impor yang kami butuhkan dalam file pengujian kami.
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';
Ini seperti file pengaturan, tetapi untuk pengujian kami. Dengan cara ini kita tidak perlu menginisialisasi semua yang ada di dalam setiap file pengujian kita. Jadi kami mengimpor paket yang diperlukan dan mengekspor apa yang kami inisialisasi — yang kemudian dapat kami impor dalam file yang membutuhkannya.
Buka index.test.js dan rekatkan kode tes berikut.
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(); }); }); });
Di sini kami membuat permintaan untuk mendapatkan titik akhir dasar, yaitu /
dan menegaskan bahwa res.
objek body
memiliki kunci message
dengan nilai Environment variable is coming across.
Jika Anda tidak terbiasa dengan describe
it
polanya, saya mendorong Anda untuk melihat sekilas dokumen "Memulai" Mocha.
Tambahkan perintah tes ke bagian scripts
package.json .
"test": "nyc --reporter=html --reporter=text --reporter=lcov mocha -r @babel/register"
Skrip ini menjalankan pengujian kami dengan nyc
dan menghasilkan tiga jenis laporan cakupan: laporan HTML, dikeluarkan ke folder coverage/
; laporan teks dikeluarkan ke terminal dan laporan lcov dikeluarkan ke folder .nyc_output/
.
Sekarang jalankan yarn test
. Anda akan melihat laporan teks di terminal Anda seperti yang ada di foto di bawah ini.
Perhatikan bahwa dua folder tambahan dibuat:
-
.nyc_output/
-
coverage/
Lihat ke dalam .gitignore
dan Anda akan melihat bahwa kita telah mengabaikan keduanya. Saya mendorong Anda untuk membuka coverage/index.html
di browser dan melihat laporan pengujian untuk setiap file.
Ini adalah poin yang baik untuk melakukan perubahan Anda.
- Cabang yang sesuai di repo saya adalah 04-first-test.
Integrasi Berkelanjutan (CD) Dan Lencana: Travis, Baju, Iklim Kode, AppVeyor
Sekarang saatnya untuk mengonfigurasi alat continuous integration and deployment (CI/CD). Kami akan mengonfigurasi layanan umum seperti travis-ci
, coveralls
, AppVeyor
, dan codeclimate
dan menambahkan lencana ke file README kami.
Mari kita mulai.
Travis CI
Travis CI adalah alat yang menjalankan pengujian kami secara otomatis setiap kali kami mendorong komit ke GitHub (dan baru-baru ini, Bitbucket) dan setiap kali kami membuat permintaan tarik. Ini sangat berguna saat membuat permintaan tarik dengan menunjukkan kepada kami jika kode baru kami telah merusak salah satu pengujian kami.
- Kunjungi travis-ci.com atau travis-ci.org dan buat akun jika Anda belum memilikinya. Anda harus mendaftar dengan akun GitHub Anda.
- Arahkan kursor ke panah tarik-turun di sebelah gambar profil Anda dan klik
settings
. - Di bawah tab
Repositories
, klikManage repositories on Github
untuk dialihkan ke Github. - Pada halaman GitHub, gulir ke bawah ke
Repository access
dan klik kotak centang di sebelahOnly select repositories
. - Klik dropdown
Select repositories
dan temukan repoexpress-api-template
. Klik untuk menambahkannya ke daftar repositori yang ingin Anda tambahkan ketravis-ci
. - Klik
Approve and install
dan tunggu untuk diarahkan kembali ketravis-ci
. - Di bagian atas halaman repo, dekat dengan nama repo, klik ikon
build unknown
. Dari modal Gambar Status, pilih penurunan harga dari dropdown format. - Salin kode yang dihasilkan dan tempel di file README.md Anda.
- Pada halaman proyek, klik
More options
>Settings
. Di bawah bagianEnvironment Variables
, tambahkan variabel envTEST_ENV_VARIABLE
. Saat memasukkan nilainya, pastikan untuk memilikinya dalam tanda kutip ganda seperti ini"Environment variable is coming across."
- Buat file .travis.yml di root proyek Anda dan rekatkan kode di bawah ini (Kami akan menetapkan nilai
CC_TEST_REPORTER_ID
di bagian Kode Iklim).
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
Pertama, kami memberi tahu Travis untuk menjalankan pengujian kami dengan Node.js, lalu mengatur variabel lingkungan global CC_TEST_REPORTER_ID
(kita akan membahasnya di bagian Kode Iklim). Di bagian matrix
, kami memberi tahu Travis untuk menjalankan pengujian kami dengan Node.js v12. Kami juga ingin men-cache direktori node_modules/
sehingga tidak harus dibuat ulang setiap saat.
Kami menginstal dependensi kami menggunakan perintah yarn
yang merupakan singkatan dari yarn install
. Perintah before_script
dan after_script
digunakan untuk mengunggah hasil cakupan ke codeclimate
. Kami akan segera mengonfigurasi codeclimate
. Setelah yarn test
berjalan dengan sukses, kami juga ingin menjalankan yarn coverage
yang akan mengunggah laporan cakupan kami ke coveralls.io.
baju
Coverall mengunggah data cakupan pengujian untuk visualisasi yang mudah. Kami dapat melihat cakupan pengujian pada mesin lokal kami dari folder cakupan, tetapi Coveralls membuatnya tersedia di luar mesin lokal kami.
- Kunjungi coveralls.io dan masuk atau daftar dengan akun Github Anda.
- Arahkan kursor ke sisi kiri layar untuk membuka menu navigasi. Klik
ADD REPOS
. - Cari repo
express-api-template
dan nyalakan cakupan menggunakan tombol sakelar di sisi kiri. Jika Anda tidak dapat menemukannya, klikSYNC REPOS
di pojok kanan atas dan coba lagi. Perhatikan bahwa repo Anda harus bersifat publik, kecuali Anda memiliki akun PRO. - Klik detail untuk membuka halaman detail repo.
- Buat file .coveralls.yml di root proyek Anda dan masukkan kode di bawah ini. Untuk mendapatkan
repo_token
, klik pada detail repo. Anda akan menemukannya dengan mudah di halaman itu. Anda bisa melakukan pencarian browser untukrepo_token
.
repo_token: get-this-from-repo-settings-on-coveralls.io
Token ini memetakan data cakupan Anda ke repo di Coverall. Sekarang, tambahkan perintah coverage
ke bagian scripts
dari file package.json Anda:
"coverage": "nyc report --reporter=text-lcov | coveralls"
Perintah ini mengunggah laporan cakupan di folder .nyc_output
ke coveralls.io. Nyalakan koneksi Internet Anda dan jalankan:
yarn coverage
Ini harus mengunggah laporan cakupan yang ada ke baju kerja. Segarkan halaman repo pada baju untuk melihat laporan lengkapnya.
Pada halaman detail, gulir ke bawah untuk menemukan bagian BADGE YOUR REPO
. Klik pada dropdown EMBED
dan salin kode penurunan harga dan tempel ke file README Anda.
Kode Iklim
Code Climate adalah alat yang membantu kami mengukur kualitas kode. Ini menunjukkan kepada kita metrik pemeliharaan dengan memeriksa kode kita terhadap beberapa pola yang ditentukan. Ini mendeteksi hal-hal seperti pengulangan yang tidak perlu dan loop for yang sangat bersarang. Itu juga mengumpulkan data cakupan pengujian seperti coveralls.io.
- Kunjungi codeclimate.com dan klik 'Daftar dengan GitHub'. Masuk jika Anda sudah memiliki akun.
- Setelah di dasbor Anda, klik
Add a repository
. - Temukan repo
express-api-template
dari daftar dan klikAdd Repo
. - Tunggu hingga pembangunan selesai dan arahkan ke dasbor repo.
- Di bawah
Codebase Summary
, klikTest Coverage
. Di bawah menuTest coverage
, salinTEST REPORTER ID
dan paste di .travis.yml Anda sebagai nilaiCC_TEST_REPORTER_ID
. - Masih di halaman yang sama, di navigasi sebelah kiri, di bawah
EXTRAS
, klik Lencana. Salin lencana cakupanmaintainability
dantest coverage
dalam format penurunan harga dan tempelkan ke file README.md Anda.
Penting untuk dicatat bahwa ada dua cara untuk mengonfigurasi pemeriksaan rawatan. Ada pengaturan default yang diterapkan ke setiap repo, tetapi jika Anda mau, Anda bisa menyediakan file .codeclimate.yml di root proyek Anda. Saya akan menggunakan pengaturan default, yang dapat Anda temukan di bawah tab Maintainability
di halaman pengaturan repo. Saya mendorong Anda untuk melihat setidaknya. Jika Anda masih ingin mengonfigurasi pengaturan Anda sendiri, panduan ini akan memberikan semua informasi yang Anda butuhkan.
AplikasiVeyor
AppVeyor dan Travis CI keduanya adalah test runner otomatis. Perbedaan utamanya adalah travis-ci menjalankan tes di lingkungan Linux sementara AppVeyor menjalankan tes di lingkungan Windows. Bagian ini disertakan untuk menunjukkan cara memulai AppVeyor.
- Kunjungi AppVeyor dan masuk atau daftar.
- Pada halaman berikutnya, klik
NEW PROJECT
. - Dari daftar repo, temukan repo
express-api-template
. Arahkan kursor ke atasnya dan klikADD
. - Klik pada tab
Settings
. KlikEnvironment
di navigasi kiri. TambahkanTEST_ENV_VARIABLE
dan nilainya. Klik 'Simpan' di bagian bawah halaman. - Buat file appveyor.yml di root proyek Anda dan rekatkan kode di bawah ini.
environment: matrix: - nodejs_version: "12" install: - yarn test_script: - yarn test build: off
Kode ini menginstruksikan AppVeyor untuk menjalankan pengujian kami menggunakan Node.js v12. Kami kemudian menginstal dependensi proyek kami dengan perintah yarn
. test_script
menentukan perintah untuk menjalankan pengujian kami. Baris terakhir memberitahu AppVeyor untuk tidak membuat folder build.
Klik pada tab Settings
. Di navigasi sebelah kiri, klik lencana. Salin kode penurunan harga dan tempel di file README.md Anda.
Komit kode Anda dan dorong ke GitHub. Jika Anda telah melakukan semuanya seperti yang diinstruksikan, semua tes akan lulus dan Anda akan melihat lencana baru Anda yang mengkilap seperti yang ditunjukkan di bawah ini. Periksa kembali apakah Anda telah menyetel variabel lingkungan pada Travis dan AppVeyor.
Sekarang adalah saat yang tepat untuk melakukan perubahan.
- Cabang yang sesuai dalam repo saya adalah 05-ci.
Menambahkan Pengontrol
Saat ini, kami menangani permintaan GET
ke URL root, /v1
, di dalam src/routes/index.js . Ini berfungsi seperti yang diharapkan dan tidak ada yang salah dengan itu. Namun, seiring pertumbuhan aplikasi Anda, Anda ingin menjaga semuanya tetap rapi. Anda ingin masalah dipisahkan — Anda ingin pemisahan yang jelas antara kode yang menangani permintaan dan kode yang menghasilkan respons yang akan dikirim kembali ke klien. Untuk mencapai ini, kami menulis controllers
. Pengontrol hanyalah fungsi yang menangani permintaan yang datang melalui URL tertentu.
Untuk memulai, buat folder controllers/
di dalam folder src/
. Di dalam controllers
membuat dua file: index.js dan home.js . Kami akan mengekspor fungsi kami dari dalam index.js . Anda dapat memberi nama home.js apa pun yang Anda inginkan, tetapi biasanya Anda ingin memberi nama pengontrol setelah apa yang mereka kontrol. Misalnya, Anda mungkin memiliki file usersController.js untuk menampung setiap fungsi yang terkait dengan pengguna di aplikasi Anda.
Buka src/controllers/home.js dan masukkan kode di bawah ini:
import { testEnvironmentVariable } from '../settings'; export const indexPage = (req, res) => res.status(200).json({ message: testEnvironmentVariable });
Anda akan melihat bahwa kami hanya memindahkan fungsi yang menangani permintaan untuk rute /
.
Buka src/controllers/index.js dan masukkan kode di bawah ini.
// export everything from home.js export * from './home';
Kami mengekspor semuanya dari file home.js. Ini memungkinkan kami mempersingkat pernyataan impor untuk import { indexPage } from '../controllers';
Buka src/routes/index.js dan ganti kode di sana dengan yang di bawah ini:
import express from 'express'; import { indexPage } from '../controllers'; const indexRouter = express.Router(); indexRouter.get('/', indexPage); export default indexRouter;
Satu-satunya perubahan di sini adalah bahwa kami telah menyediakan fungsi untuk menangani permintaan ke /
route.
Anda baru saja berhasil menulis pengontrol pertama Anda. Dari sini masalah menambahkan lebih banyak file dan fungsi sesuai kebutuhan.
Silakan dan mainkan dengan aplikasi dengan menambahkan beberapa rute dan pengontrol lagi. Anda dapat menambahkan rute dan pengontrol untuk halaman about. Ingatlah untuk memperbarui tes Anda.
Jalankan yarn test
untuk mengonfirmasi bahwa kami tidak merusak apa pun. Apakah tes Anda lulus? Itu keren.
Ini adalah poin yang baik untuk melakukan perubahan kami.
- Cabang yang sesuai di repo saya adalah 06-controllers.
Menghubungkan Database PostgreSQL
Dan Menulis Model
Pengontrol kami saat ini mengembalikan pesan teks berkode keras. Dalam aplikasi dunia nyata, kita sering kali perlu menyimpan dan mengambil informasi dari database. Di bagian ini, kita akan menghubungkan aplikasi kita ke database PostgreSQL.
Kami akan menerapkan penyimpanan dan pengambilan pesan teks sederhana menggunakan database. Kami memiliki dua opsi untuk menyetel basis data: kami dapat menyediakan satu dari server cloud, atau kami dapat mengatur sendiri secara lokal.
Saya akan merekomendasikan Anda menyediakan database dari server cloud. ElephantSQL memiliki paket gratis yang memberikan 20MB penyimpanan gratis yang cukup untuk tutorial ini. 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.
Buka query.js dan paste kode berikut:
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';
Dalam file ini, kami mendefinisikan tiga string kueri SQL. Kueri pertama menghapus dan membuat ulang tabel messages
. Kueri kedua menyisipkan dua baris ke dalam tabel messages
. Jangan ragu untuk menambahkan lebih banyak item di sini. Kueri terakhir menjatuhkan/menghapus tabel messages
.
Buka queryFunctions.js dan rekatkan kode berikut:
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 ]);
Di sini, kami membuat fungsi untuk menjalankan kueri yang kami definisikan sebelumnya. Perhatikan bahwa fungsi executeQueryArray
mengeksekusi larik kueri dan menunggu setiap kueri selesai di dalam loop. (Jangan lakukan hal seperti itu dalam kode produksi). Kemudian, kami hanya menyelesaikan janji setelah kami menjalankan kueri terakhir dalam daftar. Alasan menggunakan array adalah bahwa jumlah kueri tersebut akan bertambah seiring dengan bertambahnya jumlah tabel dalam database kami.
Buka runQuery.js dan rekatkan kode berikut:
import { createTables, insertIntoTables } from './queryFunctions'; (async () => { await createTables(); await insertIntoTables(); })();
Di sinilah kita menjalankan fungsi untuk membuat tabel dan menyisipkan pesan ke dalam tabel. Mari tambahkan perintah di bagian scripts
package.json kita untuk mengeksekusi file ini.
"runQuery": "babel-node ./src/utils/runQuery"
Sekarang jalankan:
yarn runQuery
Jika Anda memeriksa database Anda, Anda akan melihat bahwa tabel messages
telah dibuat dan bahwa pesan telah dimasukkan ke dalam tabel.
Jika Anda menggunakan ElephantSQL, pada halaman detail database, klik BROWSER
dari menu navigasi kiri. Pilih tabel messages
dan klik Execute
. Anda akan melihat pesan dari file query.js .
Mari buat pengontrol dan rute untuk menampilkan pesan dari database kita.
Buat file pengontrol baru src/controllers/messages.js dan rekatkan kode berikut:
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 }); } };
Kami mengimpor kelas Model
kami dan membuat instance baru dari model itu. Ini mewakili tabel messages
di database kami. Kami kemudian menggunakan metode select
model untuk query database kami. Data ( name
dan message
) yang kami dapatkan dikirim sebagai JSON dalam respons.
Kami mendefinisikan pengontrol messagesPage
sebagai fungsi async
. Karena kueri node-postgres
mengembalikan janji, kami await
hasil kueri itu. Jika kami menemukan kesalahan selama kueri, kami menangkapnya dan menampilkan tumpukan kepada pengguna. Anda harus memutuskan bagaimana memilih untuk menangani kesalahan.
Tambahkan titik akhir dapatkan pesan ke src/routes/index.js dan perbarui baris impor.
# update the import line import { indexPage, messagesPage } from '../controllers'; # add the get messages endpoint indexRouter.get('/messages', messagesPage)
Kunjungi https://localhost:3000/v1/messages dan Anda akan melihat pesan yang ditampilkan seperti di bawah ini.
Sekarang, mari perbarui file pengujian kita. Saat melakukan TDD, Anda biasanya menulis tes Anda sebelum mengimplementasikan kode yang membuat tes lulus. Saya mengambil pendekatan yang berlawanan di sini karena kami masih bekerja untuk menyiapkan database.
Buat file baru, hooks.js di folder test/
dan masukkan kode di bawah ini:
import { dropTables, createTables, insertIntoTables, } from '../src/utils/queryFunctions'; before(async () => { await createTables(); await insertIntoTables(); }); after(async () => { await dropTables(); });
Saat pengujian kami dimulai, Mocha menemukan file ini dan menjalankannya sebelum menjalankan file pengujian apa pun. Ini mengeksekusi kait before
untuk membuat database dan memasukkan beberapa item ke dalamnya. File tes kemudian dijalankan setelah itu. Setelah tes selesai, Mocha menjalankan after
hook di mana kita menjatuhkan database. Ini memastikan bahwa setiap kali kami menjalankan pengujian kami, kami melakukannya dengan catatan yang bersih dan baru di database kami.
Buat file tes baru test/messages.test.js dan tambahkan kode di bawah ini:
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(); }); }); });
Kami menegaskan bahwa hasil dari panggilan ke /messages
adalah sebuah array. Untuk setiap objek pesan, kami menyatakan bahwa ia memiliki properti name
dan message
.
Langkah terakhir di bagian ini adalah memperbarui file CI.
Tambahkan bagian berikut ke file .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
Ini menginstruksikan Travis untuk memutar database PostgreSQL 10 sebelum menjalankan pengujian kami.
Tambahkan perintah untuk membuat database sebagai entri pertama di bagian before_script
:
# add this as the first line in the before_script section - psql -c 'create database testdb;' -U postgres
Buat variabel lingkungan CONNECTION_STRING
di Travis, dan gunakan nilai di bawah ini:
CONNECTION_STRING="postgresql://postgres:postgres@localhost:5432/testdb"
Tambahkan bagian berikut ke file .appveyor.yml :
before_test: - SET PGUSER=postgres - SET PGPASSWORD=Password12! - PATH=C:\Program Files\PostgreSQL\10\bin\;%PATH% - createdb testdb services: - postgresql101
Tambahkan variabel lingkungan string koneksi ke appveyor. Gunakan baris di bawah ini:
CONNECTION_STRING=postgresql://postgres:Password12!@localhost:5432/testdb
Sekarang komit perubahan Anda dan dorong ke GitHub. Pengujian Anda harus lulus pada Travis CI dan AppVeyor.
- Cabang yang sesuai di repo saya adalah 07-connect-postgres.
Catatan : Saya harap semuanya berfungsi dengan baik di pihak Anda, tetapi jika Anda mengalami masalah karena suatu alasan, Anda selalu dapat memeriksa kode saya di repo!
Sekarang, mari kita lihat bagaimana kita dapat menambahkan pesan ke database kita. Untuk langkah ini, kita memerlukan cara untuk mengirim permintaan POST
ke URL kita. Saya akan menggunakan Postman untuk mengirim permintaan POST
.
Mari kita pergi ke rute TDD dan memperbarui pengujian kita untuk mencerminkan apa yang kita harapkan untuk dicapai.
Buka test/message.test.js dan tambahkan test case di bawah ini:
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(); }); });
Tes ini membuat permintaan POST ke titik akhir /v1/messages
dan kami mengharapkan array dikembalikan. Kami juga memeriksa properti id
, name
, dan message
pada array.
Jalankan pengujian Anda untuk melihat bahwa kasus ini gagal. Sekarang mari kita perbaiki.
Untuk mengirim permintaan posting, kami menggunakan metode posting dari server. Kami juga mengirimkan nama dan pesan yang ingin kami masukkan. Kami mengharapkan responsnya berupa array, dengan id
properti dan info lain yang menyusun kueri. id
adalah bukti bahwa suatu record telah dimasukkan ke dalam database.
Buka src/models/model.js dan tambahkan metode insert
:
async insertWithReturn(columns, values) { const query = ` INSERT INTO ${this.table}(${columns}) VALUES (${values}) RETURNING id, ${columns} `; return this.pool.query(query); }
Ini adalah metode yang memungkinkan kita untuk memasukkan pesan ke dalam database. Setelah memasukkan item, ia mengembalikan id
, name
dan message
.
Buka src/controllers/messages.js dan tambahkan controller di bawah ini:
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 }); } };
Kami merusak badan permintaan untuk mendapatkan nama dan pesan. Kemudian kami menggunakan nilai untuk membentuk string kueri SQL yang kemudian kami jalankan dengan metode insertWithReturn
dari model kami.
Tambahkan titik akhir POST
di bawah ini ke /src/routes/index.js dan perbarui baris impor Anda.
import { indexPage, messagesPage, addMessage } from '../controllers'; indexRouter.post('/messages', addMessage);
Jalankan tes Anda untuk melihat apakah mereka lulus.
Buka Postman dan kirim permintaan POST
ke titik akhir messages
. Jika Anda baru saja menjalankan pengujian, ingatlah untuk menjalankan yarn query
untuk membuat ulang tabel messages
.
yarn query
Komit perubahan Anda dan dorong ke GitHub. Pengujian Anda harus melewati Travis dan AppVeyor. Cakupan pengujian Anda akan turun beberapa poin, tapi tidak apa-apa.
- Cabang yang sesuai pada repo saya adalah 08-post-to-db.
perangkat tengah
Diskusi kami tentang Express tidak akan lengkap tanpa berbicara tentang middleware. Dokumentasi Express menjelaskan middlewares sebagai:
“[...] fungsi yang memiliki akses ke objek permintaan (req
), objek respons (res
), dan fungsi middleware berikutnya dalam siklus permintaan-respons aplikasi. Fungsi middleware berikutnya biasanya dilambangkan dengan variabel bernamanext
.”
Sebuah middleware dapat melakukan sejumlah fungsi seperti otentikasi, memodifikasi badan permintaan, dan sebagainya. Lihat dokumentasi Express tentang penggunaan middleware.
Kita akan menulis middleware sederhana yang memodifikasi badan permintaan. Middleware kami akan menambahkan kata SAYS:
ke pesan masuk sebelum disimpan dalam database.
Sebelum kita mulai, mari kita modifikasi pengujian kita untuk mencerminkan apa yang ingin kita capai.
Buka test/messages.test.js dan ubah baris harapan terakhir dalam kasus uji posts message
:
it('posts messages', done => { ... expect(m).to.have.property('message', `SAYS: ${data.message}`); # update this line ... });
Kami menegaskan bahwa string SAYS:
telah ditambahkan ke pesan. Jalankan pengujian Anda untuk memastikan kasus pengujian ini gagal.
Sekarang, mari kita tulis kode untuk membuat tes lulus.
Buat folder middleware/
baru di dalam folder src/
. Buat dua file di dalam folder ini:
- middleware.js
- index.js
Masukkan kode di bawah ini di middleware.js :
export const modifyMessage = (req, res, next) => { req.body.message = `SAYS: ${req.body.message}`; next(); };
Di sini, kami menambahkan string SAYS:
ke pesan di badan permintaan. Setelah melakukan itu, kita harus memanggil fungsi next()
untuk meneruskan eksekusi ke fungsi berikutnya dalam rantai permintaan-respons. Setiap middleware harus memanggil fungsi next
untuk meneruskan eksekusi ke middleware berikutnya dalam siklus request-response.
Masukkan kode di bawah ini di index.js :
# export everything from the middleware file export * from './middleware';
Ini mengekspor middleware yang kita miliki di file /middleware.js . Untuk saat ini, kami hanya memiliki middleware modifyMessage
.
Buka src/routes/index.js dan tambahkan middleware ke rantai permintaan-tanggapan pesan pos.
import { modifyMessage } from '../middleware'; indexRouter.post('/messages', modifyMessage, addMessage);
Kita dapat melihat bahwa fungsi addMessage
modifyMessage
Kami memanggil fungsi addMessage
dengan memanggil next
di middleware modifyMessage
. Sebagai percobaan, beri komentar pada baris next()
di bagian tengah modifyMessage
dan perhatikan permintaan tersebut hang.
Buka Postman dan buat pesan baru. Anda akan melihat string yang ditambahkan.
Ini adalah poin yang baik untuk melakukan perubahan kami.
- Cabang yang sesuai di repo saya adalah 09-middleware.
Penanganan Kesalahan Dan Middleware Asinkron
Kesalahan tidak bisa dihindari dalam aplikasi apa pun. Tugas di hadapan pengembang adalah bagaimana menangani kesalahan seanggun mungkin.
Dalam Ekspres:
“ Penanganan Kesalahan mengacu pada bagaimana Express menangkap dan memproses kesalahan yang terjadi baik secara sinkron maupun asinkron.
Jika kami hanya menulis fungsi sinkron, kami mungkin tidak perlu terlalu khawatir tentang penanganan kesalahan karena Express sudah melakukan pekerjaan yang sangat baik untuk menanganinya. Menurut dokumen:
“Kesalahan yang terjadi dalam kode sinkron di dalam penangan rute dan middleware tidak memerlukan kerja ekstra.”
Tetapi begitu kita mulai menulis penangan router dan middleware asinkron, maka kita harus melakukan beberapa penanganan kesalahan.
Middleware modifyMessage
kami adalah fungsi sinkron. Jika terjadi kesalahan pada fungsi itu, Express akan menanganinya dengan baik. Mari kita lihat bagaimana kita menangani kesalahan di middleware asinkron.
Katakanlah, sebelum membuat pesan, kita ingin mendapatkan gambar dari API Lorem Picsum menggunakan URL ini https://picsum.photos/id/0/info
. Ini adalah operasi asinkron yang bisa berhasil atau gagal, dan itu menyajikan kasus untuk kita tangani.
Mulailah dengan menginstal Axios.
# install axios yarn add axios
Buka src/middleware/middleware.js dan tambahkan fungsi di bawah ini:
export const performAsyncAction = async (req, res, next) => { try { await axios.get('https://picsum.photos/id/0/info'); next(); } catch (err) { next(err); } };
Dalam fungsi async
ini, kami await
panggilan ke API (kami sebenarnya tidak membutuhkan data yang dikembalikan) dan setelah itu memanggil fungsi next
dalam rantai permintaan. Jika permintaan gagal, kami menangkap kesalahan dan meneruskannya ke next
. Setelah Express melihat kesalahan ini, ia akan melompati semua middleware lainnya dalam rantai. Jika kami tidak menelepon next(err)
, permintaan akan hang. Jika kita hanya memanggil next()
tanpa err
, permintaan akan berjalan seolah-olah tidak ada yang terjadi dan kesalahan tidak akan diketahui.
Impor fungsi ini dan tambahkan ke rantai middleware dari rute pesan pos:
import { modifyMessage, performAsyncAction } from '../middleware'; indexRouter.post('/messages', modifyMessage, performAsyncAction, addMessage);
Buka src/app.js dan tambahkan kode di bawah ini tepat sebelum baris export default app
.
app.use((err, req, res, next) => { res.status(400).json({ error: err.stack }); }); export default app;
Ini adalah penangan kesalahan kami. Menurut dokumen penanganan kesalahan Express:
“[...] fungsi penanganan kesalahan memiliki empat argumen, bukan tiga: (err, req, res, next)
.”
Perhatikan bahwa penangan kesalahan ini harus datang terakhir, setelah setiap panggilan app.use()
. Setelah kami menemukan kesalahan, kami mengembalikan jejak tumpukan dengan kode status 400
. Anda dapat melakukan apa pun yang Anda suka dengan kesalahan. Anda mungkin ingin mencatatnya atau mengirimkannya ke suatu tempat.
Ini adalah tempat yang baik untuk melakukan perubahan Anda.
- Cabang yang sesuai di repo saya adalah 10-async-middleware.
Terapkan Ke Heroku
- Untuk memulai, buka https://www.heroku.com/ dan masuk atau daftar.
- Unduh dan instal Heroku CLI dari sini.
- Buka terminal di folder proyek untuk menjalankan perintah.
# login to heroku on command line heroku login
Ini akan membuka jendela browser dan meminta Anda untuk masuk ke akun Heroku Anda.
Masuk untuk memberikan akses terminal Anda ke akun Heroku Anda, dan buat aplikasi heroku baru dengan menjalankan:
#app name is up to you heroku create app-name
Ini akan membuat aplikasi di Heroku dan mengembalikan dua URL.
# app production url and git url https://app-name.herokuapp.com/ | https://git.heroku.com/app-name.git
Salin URL di sebelah kanan dan jalankan perintah di bawah ini. Perhatikan bahwa langkah ini opsional karena Anda mungkin menemukan bahwa Heroku telah menambahkan URL jarak jauh.
# add heroku remote url git remote add heroku https://git.heroku.com/my-shiny-new-app.git
Buka terminal samping dan jalankan perintah di bawah ini. Ini menunjukkan kepada Anda log aplikasi secara real-time seperti yang ditunjukkan pada gambar.
# see process logs heroku logs --tail
Jalankan tiga perintah berikut untuk mengatur variabel lingkungan yang diperlukan:
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
Ingat dalam skrip kami, kami mengatur:
"prestart": "babel ./src --out-dir build", "start": "node ./build/bin/www",
Untuk memulai aplikasi, itu perlu dikompilasi ke ES5 menggunakan babel pada langkah prestart
karena babel hanya ada di dependensi pengembangan kami. Kita harus menyetel NPM_CONFIG_PRODUCTION
ke false
agar dapat menginstalnya juga.
Untuk memastikan semuanya sudah diatur dengan benar, jalankan perintah di bawah ini. Anda juga dapat mengunjungi tab settings
di halaman aplikasi dan mengklik Reveal Config Vars
.
# check configuration variables heroku config
Sekarang jalankan git push heroku
.
Untuk membuka aplikasi, jalankan:
# open /v1 route heroku open /v1 # open /v1/messages route heroku open /v1/messages
Jika seperti saya, Anda menggunakan database PostgresSQL yang sama untuk pengembangan dan produksi, Anda mungkin menemukan bahwa setiap kali Anda menjalankan pengujian, database dihapus. Untuk membuatnya kembali, Anda dapat menjalankan salah satu dari perintah berikut:
# run script locally yarn runQuery # run script with heroku heroku run yarn runQuery
Continuous Deployment (CD) Dengan Travis
Sekarang mari tambahkan Continuous Deployment (CD) untuk melengkapi alur CI/CD. Kami akan menerapkan dari Travis setelah setiap uji coba yang berhasil.
Langkah pertama adalah menginstal Travis CI. (Anda dapat menemukan petunjuk instalasi di sini.) Setelah berhasil menginstal Travis CI, login dengan menjalankan perintah di bawah ini. (Perhatikan bahwa ini harus dilakukan di repositori proyek Anda.)
# login to travis travis login --pro # use this if you're using two factor authentication travis login --pro --github-token enter-github-token-here
Jika proyek Anda dihosting di travis-ci.org, hapus tanda --pro
. Untuk mendapatkan token GitHub, kunjungi halaman pengaturan pengembang akun Anda dan buat satu. Ini hanya berlaku jika akun Anda diamankan dengan 2FA.
Buka .travis.yml Anda dan tambahkan bagian penerapan:
deploy: provider: heroku app: master: app-name
Di sini, kami menentukan bahwa kami ingin menyebarkan ke Heroku. Sub-bagian aplikasi menetapkan bahwa kami ingin menerapkan cabang master
repo kami ke app-name
aplikasi di Heroku. Dimungkinkan untuk menyebarkan cabang yang berbeda ke aplikasi yang berbeda. Anda dapat membaca lebih lanjut tentang opsi yang tersedia di sini.
Jalankan perintah di bawah ini untuk mengenkripsi kunci API Heroku Anda dan menambahkannya ke bagian penerapan:
# encrypt heroku API key and add to .travis.yml travis encrypt $(heroku auth:token) --add deploy.api_key --pro
Ini akan menambahkan sub-bagian di bawah ini ke bagian penerapan.
api_key: secure: very-long-encrypted-api-key-string
Sekarang komit perubahan Anda dan dorong ke GitHub sambil memantau log Anda. Anda akan melihat build terpicu segera setelah tes Travis selesai. Dengan cara ini, jika kita memiliki pengujian yang gagal, perubahan tidak akan pernah diterapkan. Demikian juga, jika build gagal, seluruh uji coba akan gagal. Ini melengkapi aliran CI/CD.
- Cabang yang sesuai di repo saya adalah 11-cd.
Kesimpulan
Jika Anda sudah sampai sejauh ini, saya katakan, “Acungi jempol!” Dalam tutorial ini, kami berhasil menyiapkan proyek Express baru. Kami melanjutkan untuk mengonfigurasi dependensi pengembangan serta Integrasi Berkelanjutan (CI). Kami kemudian menulis fungsi asinkron untuk menangani permintaan ke titik akhir API kami — dilengkapi dengan pengujian. Kami kemudian melihat secara singkat penanganan kesalahan. Terakhir, kami menerapkan proyek kami ke Heroku dan mengonfigurasi Continuous Deployment.
Anda sekarang memiliki template untuk proyek back-end berikutnya. Kami hanya melakukan cukup untuk membantu Anda memulai, tetapi Anda harus terus belajar untuk terus maju. Pastikan untuk memeriksa dokumen Express.js juga. Jika Anda lebih suka menggunakan MongoDB
daripada PostgreSQL
, saya memiliki template di sini yang melakukan hal itu. Anda dapat memeriksanya untuk pengaturan. Ini hanya memiliki beberapa poin perbedaan.
Sumber daya
- “Buat Backend API Ekspres Dengan MongoDB ,” Orji Chidi Matthew, GitHub
- “Panduan Singkat Untuk Menghubungkan Middleware,” Stephen Sugden
- “Templat API ekspres,” GitHub
- “AppVeyor vs Travis CI,” StackShare
- “The Heroku CLI,” Pusat Pengembang Heroku
- “Penempatan Heroku,” Travis CI
- “Menggunakan middleware,” Express.js
- “Penanganan Kesalahan,” Express.js
- “Memulai,” Mocha
-
nyc
(GitHub) - ElephantSQL
- Tukang pos
- cepat
- Travis CI
- Kode Iklim
- PostgreSQL
- pgAdmin