Cara Mengatur Proyek Backend API Ekspres Dengan PostgreSQL

Diterbitkan: 2022-03-10
Ringkasan cepat Pada artikel ini, kami akan membuat satu set titik akhir API menggunakan Express dari awal dalam sintaks ES6, dan mencakup beberapa praktik terbaik pengembangan. Cari tahu bagaimana semua bagian bekerja bersama saat Anda membuat proyek kecil menggunakan Integrasi Berkelanjutan dan Pengembangan Berbasis Uji sebelum menerapkan ke Heroku.

Kami 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.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

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.

  1. Hapus file index/users.js .
  2. Hapus folder public/ dan views/ .
  3. Ganti nama file bin/www menjadi bin/www.js .
  4. Uninstall jade dengan perintah yarn remove jade .
  5. Buat folder baru bernama src/ dan pindahkan yang berikut di dalamnya: 1. file app.js 2. folder bin/ 3. folder route routes/ di dalamnya.
  6. 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"
  1. skrip prestart membangun konten folder src/ dan meletakkannya di folder build/ . Saat Anda mengeluarkan perintah yarn start , skrip ini berjalan terlebih dahulu sebelum skrip start .
  2. skrip start sekarang menyajikan konten folder build/ alih-alih folder src/ 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.
  3. yarn startdev digunakan untuk memulai server selama pengembangan. Mulai sekarang kami akan menggunakan skrip ini saat kami mengembangkan aplikasi. Perhatikan bahwa kita sekarang menggunakan babel-node untuk menjalankan aplikasi alih-alih node biasa. Bendera --exec memaksa babel-node untuk melayani folder src/ . Untuk skrip start , kami menggunakan node karena file di folder build/ 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.

Laporan cakupan pengujian (Pratinjau besar)

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.

  1. Kunjungi travis-ci.com atau travis-ci.org dan buat akun jika Anda belum memilikinya. Anda harus mendaftar dengan akun GitHub Anda.
  2. Arahkan kursor ke panah tarik-turun di sebelah gambar profil Anda dan klik settings .
  3. Di bawah tab Repositories , klik Manage repositories on Github untuk dialihkan ke Github.
  4. Pada halaman GitHub, gulir ke bawah ke Repository access dan klik kotak centang di sebelah Only select repositories .
  5. Klik dropdown Select repositories dan temukan repo express-api-template . Klik untuk menambahkannya ke daftar repositori yang ingin Anda tambahkan ke travis-ci .
  6. Klik Approve and install dan tunggu untuk diarahkan kembali ke travis-ci .
  7. Di bagian atas halaman repo, dekat dengan nama repo, klik ikon build unknown . Dari modal Gambar Status, pilih penurunan harga dari dropdown format.
  8. Salin kode yang dihasilkan dan tempel di file README.md Anda.
  9. Pada halaman proyek, klik More options > Settings . Di bawah bagian Environment Variables , tambahkan variabel env TEST_ENV_VARIABLE . Saat memasukkan nilainya, pastikan untuk memilikinya dalam tanda kutip ganda seperti ini "Environment variable is coming across."
  10. 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.

  1. Kunjungi coveralls.io dan masuk atau daftar dengan akun Github Anda.
  2. Arahkan kursor ke sisi kiri layar untuk membuka menu navigasi. Klik ADD REPOS .
  3. Cari repo express-api-template dan nyalakan cakupan menggunakan tombol sakelar di sisi kiri. Jika Anda tidak dapat menemukannya, klik SYNC REPOS di pojok kanan atas dan coba lagi. Perhatikan bahwa repo Anda harus bersifat publik, kecuali Anda memiliki akun PRO.
  4. Klik detail untuk membuka halaman detail repo.
  5. 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 untuk repo_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.

  1. Kunjungi codeclimate.com dan klik 'Daftar dengan GitHub'. Masuk jika Anda sudah memiliki akun.
  2. Setelah di dasbor Anda, klik Add a repository .
  3. Temukan repo express-api-template dari daftar dan klik Add Repo .
  4. Tunggu hingga pembangunan selesai dan arahkan ke dasbor repo.
  5. Di bawah Codebase Summary , klik Test Coverage . Di bawah menu Test coverage , salin TEST REPORTER ID dan paste di .travis.yml Anda sebagai nilai CC_TEST_REPORTER_ID .
  6. Masih di halaman yang sama, di navigasi sebelah kiri, di bawah EXTRAS , klik Lencana. Salin lencana cakupan maintainability dan test 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 klik ADD .
  • Klik pada tab Settings . Klik Environment di navigasi kiri. Tambahkan TEST_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.

Repo CI/CD badge. (Pratinjau besar)

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.

ElephantSQL turtle plan details page (Large preview)

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

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

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

Let's connect node-postgres to our application.

 # install node-postgres yarn add pg

Open settings.js and add the line below:

 export const connectionString = process.env.CONNECTION_STRING;

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

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

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

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

  • pool.js
  • model.js

Open pools.js and paste the following code:

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

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

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

Open model.js and paste the following code:

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

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

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

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

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

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

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

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

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.

Pesan dari basis data. (Pratinjau besar)

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 
POST permintaan ke titik akhir pesan. (Pratinjau besar)
DAPATKAN permintaan yang menampilkan pesan yang baru ditambahkan. (Pratinjau besar)

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 bernama next .”

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.

Pesan dimodifikasi oleh middleware. (Pratinjau besar)

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

  1. Untuk memulai, buka https://www.heroku.com/ dan masuk atau daftar.
  2. Unduh dan instal Heroku CLI dari sini.
  3. 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
Log Heroku. (Pratinjau besar)

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