Node.js, Express і MongoDB: API за півгодини

Починаючому програмісту розробка для Node.js може здатися справжнім кошмаром. Виною всьому – гнучкість цієї платформи і відсутність чітких настанов. Але, насправді, все не так уже й страшно.


Ось, наприклад, типова задача: розробка REST API, серверної частини якогось програми. Велика кількість власних можливостей Node і безліч додаткових модулів, які здатні допомогти в рішенні цієї задачі, здатні завести новачка в глухий кут, викликаний багатством вибору. Основні питання тут полягають у підборі компонентів і настроювання їх спільної роботи.

Один із способів створення серверної частини програми полягає у застосуванні зв'язки з Node.js, фреймворку Express і СУБД MongoDB. Власне кажучи, сьогодні я розповім про те, як створити робочий макет API, який може слугувати основою для практично будь-якого додатку. Тут ми реалізуємо основні маршрути REST, будемо взаємодіяти з API по HTTP і використовувати прості варіанти роботи з базою даних.

Для того, щоб успішно освоїти цей матеріал, вам треба розуміти, що таке REST API, мати уявлення про операції CRUD та володіти базовими знаннями в області JavaScript. Тут я використовую ES6, нічого особливо складного, в основному – стрілочні функції.

Ми розробимо скелет серверної частини програми для створення заміток, схожого на Google Keep. При цьому з нотатками можна буде виконувати всі чотири CRUD-дії, а саме – створення (create), читання (read), оновлення (update), і видалення (delete).

Попередня підготовка
Якщо Node у вас поки немає, саме час його встановити. Після установки створіть папку і виконайте в ній команду ініціалізації нового проекту:

npm init

В ході ініціалізації дайте відповіді на запитання, зокрема, дайте додатком ім'я «notable» (або, якщо хочете, будь-яке інше).

Тепер в папці повинен з'явитися файл package.json. Це означає, що можна почати встановлювати додаткові пакети, від яких залежить проект.

В якості фреймворку ми плануємо використовувати Express. Системою управління базами даних буде MongoDB. Крім того, у якості допоміжного засобу для роботи з JSON, використовуємо пакет body-parser. Встановимо все це:

npm install --save express mongodb body-parser

Ще, я дуже рекомендую встановити Nodemon як dev-залежність. Це простий маленький пакет, який, при зміні файлів, автоматично перезапускає сервер.

Для установки цього пакета виконайте команду:

npm install --save-dev nodemon

Потім можна додати наступний скрипт в файл package.json:

// package.json
"scripts": {
"dev": "nodemon server.js"
},

Готовий package.json буде виглядати приблизно так:

// package.json
{
"name": "notable",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"dev": "nodemon server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.15.2",
"express": "^4.14.0",
"mongodb": "^2.2.16"
},
"devDependencies": {
"nodemon": "^1.11.0"
}
}

Тепер створимо файл server.js і приступимо до роботи над API.

Сервер
Почнемо з підключення залежностей у файлі server.js.

// server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();

MongoClient будемо використовувати для взаємодії з базою даних. Крім того, тут ми ініціалізуємо масив
app
, що символізує наш додаток, примірником фреймворку Express. Щоб сервер запрацював, залишилося лише вказати програмі на те, щоб воно почало прослуховувати HTTP-запити.

Тут вкажемо порт і запустимо прослуховування наступним чином:

// server.js
const port = 8000;
app.слухати(port, () => {
console.log('We are live on' + port);
});

Тепер, якщо виконати команду
npm run dev
(чи
node server.js
, якщо ви не встановлювали Nodemon), в терміналі повинно з'явитися повідомлення: «We are live on port 8000».

Отже, сервер працює. Але зараз він не робить абсолютно нічого корисного. Давайте з цим розберемося.

Маршрути, орієнтовані на CRUD-операції
Ми плануємо створити 4 маршруту. А саме:

  • CREATE – створення нотаток.
  • READ –читання заміток.
  • UPDATE –оновлення нотаток.
  • DELETE –видалення заміток.
Освоївши цю схему, ви зможете зрозуміти, як, з допомогою Node, організувати практично будь-який необхідний REST-маршрут.

Для того, щоб протестувати API, знадобиться щось, здатне імітувати запити клієнтської частини програми. Вирішити цю задачу нам допоможе чудова програма, яка називається Postman. Вона дозволяє виконувати прості HTTP-запити з заданим тілом і параметрами.

Встановіть Postman. Тепер все готово до створення маршрутів.

ПРО структуру проекту
У більшості посібників з Node.js (і в безлічі реальних додатків) всі маршрути розміщують в одному великому файлі route.js. Мені такий підхід не дуже подобається. Якщо розкласти файли з різних папок, це поліпшить читаність коду, додатком буде легше керувати.

Наше додаток великим не назвеш, але пропоную зробити все як треба, враховуючи, все ж, його скромні масштаби. Створіть наступні папки: папку app, а всередині неї – routes. В папці routes створіть файли index.js note_routes.js. Іншими словами, структура проекту буде виглядати так: root > app > routes > index.js note_routes.js.

mkdir app
cd app
mkdir routes
cd routes
touch index.js
touch note_routes.js

Така структура, для маленького програми, може здатися надмірною, але вона виявиться дуже до речі в більш великої системи, побудованої на базі нашого прикладу. До того ж, будь-які проекти краще всього починати, використовуючи кращі з існуючих напрацювань.

Створення нотаток: маршрут CREATE
Почнемо з маршруту CREATE. Для цього відповімо на питання: «Як створити замітку?».
Перш ніж приступити до створення заміток, нам знадобиться розширити інфраструктуру програми. У Express маршрути обертають у функцію, яка приймає примірник Express і базу даних як аргументи.

Виглядати це може таким чином:

// routes/note_routes.js
module.exports = function(app, db) {
};

Тепер можна експортувати цю функцію через index.js:

// routes/index.js
const noteRoutes = require('./note_routes');
module.exports = function(app, db) {
noteRoutes(app, db);
// Тут, пізніше, будуть і інші обробники маршрутів 
};

Імпортуємо те, що вийшло, server.js:

// server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();
const port = 8000;
require('./app/routes')(app, {});
app.слухати(port, () => {
console.log('We are live on' + port);
});

Зверніть увагу на те, що так як базу даних ми поки що не налаштували, другим аргументом передається порожній об'єкт.

Тепер створюємо маршрут CREATE. Синтаксис тут досить простий:

module.exports = function(app, db) {
app.post('/notes', (req, res) => {
// Тут будемо створювати замітку.
res.send('Hello')
});
};

Коли додаток отримує POST-запит по дорозі '/notes', воно виконає код всередині функції зворотного виклику, передавши їй об'єкт запиту (який містить параметри запиту або JSON-дані) і об'єкт відповіді (який, зрозуміло, використовується для відповіді).

Те, що у нас вийшло, вже можна протестувати. Відправимо, з допомогою Postman, POST-запит за адресою localhost:8000/notes.


У відповідь на запит має прийти «Hello»

Відмінно. Перший маршрут створено. Наступний крок – додавання до запиту параметрів, обробка їх в API, і, нарешті збереження замітки в базі даних.

Параметри запиту
У Postman перейдіть на вкладку Body і додайте кілька пар ключ-значення, вибравши радіокнопку x-www-form-urlencoded. А саме, першим ключем буде title, його значення – My Note Title. Другий ключ – body, його значення – What a great note.

Це додасть до запиту закодовані дані, які можна буде обробити в API.


Заголовок моєї замітки, та й вона сама – дуже прості, а ви тут можете проявити фантазію

У файлі note_route.js, просто виведемо тіло замітки в консоль.

// note_routes.js
module.exports = function(app, db) {
app.post('/notes', (req, res) => {
console.log(req.body)
res.send('Hello')
});
};

Спробуйте надіслати запит за допомогою Postman, і ви побачите…
undefined
.

На жаль, Express не може самостійно обробляти форми в URL-кодуванні. Тут нам на допомогу прийде раніше встановлений пакет body-parser.

// server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
require('./app/routes')(app, {});
app.слухати(port, () => {
console.log('We are live on' + port);
});

Тепер, після виконання POST-запиту, його тіло можна буде побачити в терміналі у вигляді об'єкта.

{ title: 'My Note Title', body: 'What a great note.' }

Щоб перший маршрут повністю запрацював, залишилося лише налаштувати базу даних і додати в неї замітку.

Для швидкого створення і налаштування бази даних скористаємося сервісом mLab. Працювати з ним легко, для маленьких обсягів інформації він безкоштовний.

Створіть обліковий запис на сайті mLab і розгорніть нову базу даних MongoDB. Для цього натисніть на кнопку Create New у розділі MongoDB Deployments, у вікні, в розділі Plan виберіть Single-node. У списку Standard Line виберіть Sandbox і дайте базі даних ім'я. Далі, у вікні керування базою, перейдіть на вкладку Users і додайте користувача бази даних, задавши ім'я і пароль


Новий користувач бази даних

Скопіюйте з тієї ж сторінки другої URL – рядок підключення до бази даних.


URL для підключення до бази даних

В корінь проекту додайте директорію config, створіть в ній файл db.js.

mkdir config 
cd config
touch db.js

Файл db.js додайте наступне:

module.exports = {
url : тут буде ваш URL
};

Не забудьте додати в URL ім'я користувача і пароль (не ті, що від облікового запису в mLab, а ті, що створювали для бази даних). Якщо ви розміщуєте проект на Github, не забудьте включити в нього файл .gitignore (зразок цієї). Так ви не зробите загальним надбанням ім'я і пароль для роботи з базою.

Тепер, server.js, можна використовувати MongoClient для підключення до бази даних і обернути у функцію, яка передається йому при створенні, налаштування програми:

// server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const app = express();
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
require('./app/routes')(app, database);
app.слухати(port, () => {
console.log('We are live on' + port);
}); 
})

На цьому підготовка інфраструктури закінчена. З цього моменту будемо займатися виключно шляхами.

Додавання записів у базу даних
MongoDB зберігає дані в колекціях (collections), які повністю виправдовують свою назву. У нашому випадку замітки будуть зберігатися в колекції, яка, як нескладно здогадатися, буде називатися
notes
.

В ході налаштування клієнта, йому була передана рядок підключення до бази даних, аргумент
db
. У коді маршрутів доступ до бази можна отримати так:

db.collection('notes')

Створення нотатки в базі рівносильно викликом команди
insert
для колекції
notes
:

const note = { text: req.body.body, title: req.body.title}
db.collection('notes').insert(note, (err, results) => {
}

Після успішного завершення команди (або після того, як вона, з якоїсь причини, не зможе здійснитися), потрібно відправити у відповідь тільки що створений об'єкт замітки, або повідомлення про помилку. Ось код note_routes.js, доповнений з урахуванням цих міркувань:

// note_routes.js
module.exports = function(app, db) {
app.post('/notes', (req, res) => {
const note = { text: req.body.body, title: req.body.title };
db.collection('notes').insert(note, (err, result) => {
if (err) { 
res.send({ 'error': 'An error has occurred' }); 
} else {
res.send(result.ops[0]);
}
});
});
};

Випробуйте те, що вийшло. Надіслати з Postman POST-запит з прапором x-www-form-urlencoded ), задавши на вкладці Body значення полів title та body.

Відповідь повинен виглядати приблизно так:


Успішне додавання запису до бази

Якщо тепер подивитися на базу, авторизувавшись на mLab, у ній можна буде знайти тільки що створену замітку.

Читання заміток: маршрут READ
Інфраструктура, яку ми підготували вище, підходить для всіх маршрутів, тож тепер справа піде швидше.

Отже, ми збираємося запросити тільки що створену замітку, пройшовши по дорозі localhost:8000/notes/{id нотатку}. У нашому випадку шлях буде виглядати так: localhost:8000/notes/585182bd42ac5b07a9755ea3.

Якщо ID однієї з вже створених нотаток у вас немає, можете заглянути в базу на mLab і знайти його там, або створити нову замітку і скопіювати її ідентифікатор.

Ось як це виглядає в note_route.js:

// note_routes.js
module.exports = function(app, db) {
app.get('/notes/:id', (req, res) => {

});
app.post('/notes', (req, res) => {
const note = { text: req.body.body, title: req.body.title };
db.collection('notes').insert(note, (err, result) => {
if (err) { 
res.send({ 'error': 'An error has occurred' }); 
} else {
res.send(result.ops[0]);
}
});
});
};

Так само, як і раніше, ми збираємося викликати якусь команду для колекції бази даних нотаток. Застосуємо для цього метод
findOne
.

// note_routes.js
module.exports = function(app, db) {
app.get('/notes/:id', (req, res) => {
const details = { '_id': <ТУТ БУДЕ ID> };
db.collection('notes').findOne(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
}
});
});
app.post('/notes', (req, res) => {
const note = { text: req.body.body, title: req.body.title };
db.collection('notes').insert(note, (err, result) => {
if (err) { 
res.send({ 'error': 'An error has occurred' }); 
} else {
res.send(result.ops[0]);
}
});
});
};

Ідентифікатор параметрів URL можна витягнути за допомогою конструкції
req.params.id
. Проте якщо просто вставити рядок замість <<>> з коду вище, працювати не буде.

MongoDB потрібно ID не у вигляді рядка, а у вигляді спеціального об'єкта. Він називається ObjectID.

Ось що, після невеликих змін, у нас вийшло:

// note_routes.js
var ObjectID = require('mongodb').ObjectID;
module.exports = function(app, db) {
app.get('/notes/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
db.collection('notes').findOne(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
} 
});
});
app.post('/notes', (req, res) => {
const note = { text: req.body.body, title: req.body.title };
db.collection('notes').insert(note, (err, result) => {
if (err) { 
res.send({ 'error': 'An error has occurred' }); 
} else {
res.send(result.ops[0]);
}
});
});
};

Випробуйте це з одним із ідентифікаторів нотаток, наявних в базі даних. Відповідь Postman повинен виглядати так:


Успішний запит нотатки з бази

Видалення заміток: маршрут DELETE
Видалення об'єктів – це практично те ж саме, що їх пошук у базі. Тільки замість функції
findOne
використовується функція
remove
. Ось повний код відповідного шляху. Тут виділено те, що відрізняється від коду вже існуючого методу, який обробляє запит GET.

// note_routes.js
// ...
app.delete('/notes/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
db.collection('notes').remove(details, (err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send('Note' + id + ' deleted!');
} 
});
});
// ...

Оновлення нотаток: маршрут UPDATE
А ось і останній маршрут. Обробка запиту PUT – це, по суті, гібрид операцій READ і CREATE. Спочатку треба знайти об'єкт, потім – оновити його відповідно надійшли в запиті даними. Зараз, якщо ви, відчуваючи попередній фрагмент коду, видалили свою єдину замітку, створіть ще одну.

Ось код маршруту оновлення нотаток:

// note_routes.js
// ...
app.put</b>('/notes/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
const note = { text: req.body.body, title: req.body.title };
db.collection('notes').update(details, note, (err, result) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(note);
} 
});
});
// ...

Тепер будь-яку замітку можна редагувати. Ось як це виглядає:


Успішне оновлення нотатки

Зверніть увагу на недолік нашого прикладу. Якщо в PUT-запиті не буде тіла або заголовка нотатки, відповідні поля в базі будуть просто очищені.

Я не навантажував приклад додатковими перевірками. Якщо хочете, можете самі доопрацювати операцію оновлення нотаток, вносячи в базу нові дані тільки в тому разі, якщо запит сформовано вірно.

Підсумки
Тепер у вас є робоче Node API, яке підтримує чотири основні операції CRUD. Серверна частина програми вміє, реагуючи на HTTP-запити клієнта, створювати в базі даних замітки, знаходити їх, видаляти і редагувати.

Основна мета моєї розповіді – познайомити всіх бажаючих з зв'язкою Node + Express + MongoDB і з методикою розробки серверних додатків. Звичайно, якщо сьогодні відбулося ваше перше знайомство з цими інструментами, для того, щоб у все краще вникнути, вам знадобиться почитати документацію. Однак, розуміння того, що відбувається дозволить вам швидко заповнити прогалини в знаннях і приступити до роботи над власними проектами, використавши в якості відправної точки, додаток, яким ми з вами тут займалися.

Якщо у вас є досвід роботи з Node.js, Express і MongoDB в реальних проектах, може бути, ви порадите що-небудь корисне новачкам? А якщо ви тільки що все це вперше спробували – чекаємо ваших вражень.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.