В этом руководстве я продемонстрирую, как создать простое веб-приложение для управления контактами с использованием Node.js , Express.js , Vue.js в сочетании с объектно-реляционным преобразователем (ORM) sequelize.js, поддерживаемым базой данных SQLite.
Однако основное внимание в этой статье будет уделено тому, как использовать библиотеку sequelize.js в сочетании с SQLite, которая расширяет мою предыдущую статью A SQLite Tutorial with Node.js. Вы можете найти готовый код этого руководства в моейучетной записи GitHub .
Установка и установка
Для начала я инициализирую новый проект, используя старый добрый
npm init
и нажимая Enter, чтобы принять все значения по умолчанию, за
исключением использования точки входа server.js вместо index.js. Кстати,
я использую Node.js версии 8.10.
$ mkdir node-vue-sqlite-sequelize && cd node-vue-sqlite-sequelize
$ npm init
Далее я установлю зависимости, которые мне понадобятся, а именно: express, body-parser, sequelize, sequelize-cli, sqlite3 и, возможно, nodemon, чтобы мне не приходилось постоянно перезапускать Node.js.
$ npm install --save express body-parser sequelize sequelize-cli sqlite3 nodemon
Создание пользовательского интерфейса управления контактами
Сначала я создаю статический каталог в том же каталоге, что и файл package.json:
$ mkdir static
Внутри нового статического каталога я создам HTML-файл с именем index.html. Для создания многофункционального и интерактивного веб-интерфейса (или даже настольных приложений с Electron ) я полагаюсь на Vue.js из- за его элегантности и простоты, поэтому снова в этом руководстве я буду использовать Vue.js в файле index.html.
Внутри index.html я начну с простой части шаблона HTML5 и исходного кода Vue.js вместе с Bulma и Font-Awesome, чтобы сделать вещи немного красивее, вместе с axios.js, который я буду использовать для вызовов AJAX позже. .
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Contact</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17-beta.0/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
.section-container {
max-width: 800px;
margin: auto;
}
</style>
</head>
<body>
</body>
</html>
Хорошо, прежде чем я смогу углубиться в создание пользовательского интерфейса, мне нужно обсудить функциональность и подумать о различных способах, которыми мне нужно будет передавать данные туда и обратно с помощью серверной части этого управляемого данными приложения.
Поскольку приложение управляет контактами, мне, очевидно, понадобится некоторое представление человека, которое я буду называть объектом «Контакт». Что касается поведения, мне, вероятно, понадобится следующее:
- Посмотреть список всех контактов
- Добавить контакты
- Просмотр сведений о контакте
- Обновить информацию о контакте
- Удалить контакт
Довольно простая функциональность CRUD, не так ли?
Кроме того, как я уже упоминал ранее, я буду взаимодействовать с серверной частью через AJAX REST API, поэтому позвольте мне также разметить конечные точки API, которые, как мне кажется, мне понадобятся.
Маршрут Методика Функциональность
/api/contacts
ПОЛУЧАТЬ Получить все контакты
/api/contacts
ПОЧТА Создать контакт
/api/contacts/:id
СТАВИТЬ Обновить информацию о контакте
/api/contacts/:id
УДАЛИТЬ Удалить контакт
Теперь, когда я определил, какие функции необходимы, я могу начать собирать вместе компоненты пользовательского интерфейса и реализовывать желаемое поведение JavaScript.
Для начала я создам компонент с именем AddUpdateContact
который я могу
использовать для добавления новых контактов в приложение или обновления
существующих. Итак, внизу body
HTML я добавляю script
и следующий
код JavaScript на основе Vue.js:
<script>
const AddUpdateContact = {
props: ['contact', 'title'],
data () {
return {
id: this.contact ? this.contact.id : null,
firstName: this.contact ? this.contact.firstName : '',
lastName: this.contact ? this.contact.lastName : '',
phone: this.contact ? this.contact.phone : ''
}
},
methods: {
save() {
this.$emit('save-contact', { id: this.id, firstName: this.firstName, lastName: this.lastName, phone: this.phone })
if (!this.id) {
this.firstName = ''
this.lastName = ''
this.phone = ''
}
}
},
template: `
<form class="form" @submit.prevent="save">
<h3 class='subtitle'>{{ title }}</h3>
<div class="field">
<label>First Name</label>
<div class="control">
<input class="input" type="text" v-model="firstName">
</div>
</div>
<div class="field">
<label>Last Name</label>
<div class="control">
<input class="input" type="text" v-model="lastName">
</div>
</div>
<div class="field">
<label>Phone</label>
<div class="control">
<input class="input" type="text" v-model="phone">
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-success">Save</button>
</div>
</div>
</form>
`
}
</script>
Компоненту AddUpdateContact
можно присвоить два свойства: (1)
заголовок и (2) в необязательном случае, когда он будет использоваться
для обновления существующего контакта контактного объекта. Члены data
инициализируются в зависимости от того, contact
объект как опора или
нет. Раздел template
содержит форму и поля ввода для добавления новой
контактной информации или изменения существующей. Затем раздел методов
содержит единственный save
который передает информацию о контакте до
родительского компонента, что согласуется с философией односторонней
привязки данных
Vue.js.
Далее я создам компонент « Contact
script
, который будет выглядеть
так:
<script>
// omitting the AddUpdateContact component ...
const Contact = {
props: ['contact'],
components: { 'add-update-contact': AddUpdateContact },
data () {
return {
showDetail: false
}
},
methods: {
onAddOrUpdateContact(contact) {
this.$emit('save-contact', contact)
},
deleteContact (contact) {
this.$emit('delete-contact', contact)
}
},
template: `
<div class="card">
<header class="card-header">
<p @click="showDetail = !showDetail" class="card-header-title">
{{ contact.firstName }} {{ contact.lastName }}
</p>
<a class="card-header-icon" @click.stop="deleteContact(contact)">
<span class="icon">
<i class="fa fa-trash"></i>
</span>
</a>
</header>
<div v-show="showDetail" class="card-content">
<add-update-contact title="Details" :contact="contact" @save-contact="onAddOrUpdateContact" />
</div>
</div>
`
}
</script>
Компонент Contact
получит contact
как опору, которая содержит
данные, которые будут отображаться в его шаблоне. Компонент Contact
также будет использовать AddUpdateContact
чтобы отображать подробности
контакта, а также давать пользователю возможность обновлять его.
Функциональность обновления возможна за счет использования метода
обработчика событий onAddOrUpdateContact
который прослушивает событие
save-contact
AddUpdateContact
, который затем распространяет событие
вверх по цепочке на основной экземпляр Vue.js, который будет обсуждаться
в ближайшее время. Кроме того, существует также deleteContact
компонента Contact, который также передает сообщение об удалении до
родительского экземпляра Vue.js.
Хорошо, чтобы завершить внешний вид JavaScript-кода, мне нужно создать
экземпляр корневого объекта Vue.js и указать ему, чтобы он привязался к
элементу div
id
"app" следующим образом:
<script>
// omitting AddUpdateContant and Contact components ...
new Vue({
el: '#app',
components: { contact: Contact, 'add-update-contact': AddUpdateContact },
data: {
contacts: [],
apiURL: 'http://localhost:3000/api/contacts'
},
methods: {
onAddOrUpdateContact (contact) {
if (contact.id) {
this.updateContact(contact)
} else {
this.addContact(contact)
}
},
addContact (contact) {
return axios.post(this.apiURL, contact)
.then((response) => {
const copy = this.contacts.slice()
copy.push(response.data)
this.contacts = copy
})
},
updateContact (contact) {
return axios.put(`${this.apiURL}/${contact.id}`, contact)
.then((response) => {
const copy = this.contacts.slice()
const idx = copy.findIndex((c) => c.id === response.data.id)
copy[idx] = response.data
this.contacts = copy
})
},
deleteContact (contact) {
console.log('deleting', contact)
return axios.delete(`${this.apiURL}/${contact.id}`)
.then((response) => {
let copy = this.contacts.slice()
const idx = copy.findIndex((c) => c.id === response.data.id)
copy.splice(idx, 1)
this.contacts = copy
})
}
},
beforeMount () {
axios.get(this.apiURL)
.then((response) => {
this.contacts = response.data
})
}
})
</script>
Корневой объект Vue.js регистрирует ранее описанные компоненты и определяет набор методов, которые будут взаимодействовать с REST API через вызовы AJAX, выполняемые на сервере приложений Node.js / Express.js, который скоро будет построен.
Последняя часть пользовательского интерфейса, о которой нужно позаботиться, - это HTML, необходимый для рендеринга компонентов Vue.js, который показан ниже полностью в файле index.html.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Contacts</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17-beta.0/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
.section-container {
max-width: 800px;
margin-right: auto;
margin-left: auto;
}
</style>
</head>
<body>
<div id="app" class="container">
<section class="section section-container" style="padding-top: 24px; padding-bottom: 5px;">
<h2 class="title">Contacts</h2>
<contact v-for="contact in contacts"
:key="contact.name"
:contact="contact"
@save-contact="onAddOrUpdateContact"
@delete-contact="deleteContact" />
</section>
<section class="section section-container" style="padding-bottom: 10px;">
<div class="box">
<add-update-contact title="Add Contact" @save-contact="onAddOrUpdateContact" />
</div>
</section>
</div>
<script>
const AddUpdateContact = {
props: ['contact', 'title'],
data () {
return {
id: this.contact ? this.contact.id : null,
firstName: this.contact ? this.contact.firstName : '',
lastName: this.contact ? this.contact.lastName : '',
phone: this.contact ? this.contact.phone : ''
}
},
methods: {
save() {
this.$emit('save-contact', { id: this.id, firstName: this.firstName, lastName: this.lastName, phone: this.phone })
if (!this.id) {
this.firstName = ''
this.lastName = ''
this.phone = ''
}
}
},
template: `
<form class="form" @submit.prevent="save">
<h3 class='subtitle'>{{ title }}</h3>
<div class="field">
<label>First Name</label>
<div class="control">
<input class="input" type="text" v-model="firstName">
</div>
</div>
<div class="field">
<label>Last Name</label>
<div class="control">
<input class="input" type="text" v-model="lastName">
</div>
</div>
<div class="field">
<label>Phone</label>
<div class="control">
<input class="input" type="text" v-model="phone">
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-success">Save</button>
</div>
</div>
</form>
`
}
const Contact = {
props: ['contact'],
components: { 'add-update-contact': AddUpdateContact },
data () {
return {
showDetail: false
}
},
methods: {
onAddOrUpdateContact(contact) {
this.$emit('save-contact', contact)
},
deleteContact (contact) {
this.$emit('delete-contact', contact)
}
},
template: `
<div class="card">
<header class="card-header">
<p @click="showDetail = !showDetail" class="card-header-title">
{{ contact.firstName }} {{ contact.lastName }}
</p>
<a class="card-header-icon" @click.stop="deleteContact(contact)">
<span class="icon">
<i class="fa fa-trash"></i>
</span>
</a>
</header>
<div v-show="showDetail" class="card-content">
<add-update-contact title="Details" :contact="contact" @save-contact="onAddOrUpdateContact" />
</div>
</div>
`
}
new Vue({
el: '#app',
components: { contact: Contact, 'add-update-contact': AddUpdateContact },
data: {
contacts: [],
apiURL: 'http://localhost:3000/api/contacts'
},
methods: {
onAddOrUpdateContact (contact) {
if (contact.id) {
this.updateContact(contact)
} else {
this.addContact(contact)
}
},
addContact (contact) {
return axios.post(this.apiURL, contact)
.then((response) => {
const copy = this.contacts.slice()
copy.push(response.data)
this.contacts = copy
})
},
updateContact (contact) {
return axios.put(`${this.apiURL}/${contact.id}`, contact)
.then((response) => {
const copy = this.contacts.slice()
const idx = copy.findIndex((c) => c.id === response.data.id)
copy[idx] = response.data
this.contacts = copy
})
},
deleteContact (contact) {
console.log('deleting', contact)
return axios.delete(`${this.apiURL}/${contact.id}`)
.then((response) => {
let copy = this.contacts.slice()
const idx = copy.findIndex((c) => c.id === response.data.id)
copy.splice(idx, 1)
this.contacts = copy
})
}
},
beforeMount () {
axios.get(this.apiURL)
.then((response) => {
this.contacts = response.data
})
}
})
</script>
</body>
</html>
Создание каркаса для REST API
Поскольку я использую Express.js для этого приложения, мне нужно будет создать файл JavaScript с именем server.js, который будет обслуживать приложение в том же каталоге, что и файл package.json, а затем открыть его в моем любимом текстовом редакторе разработки (VS Код).
В верхней части файла я извлекаю фреймворк Express.js через require
а
затем создаю экземпляр приложения. Затем я говорю приложению
использовать статическое промежуточное ПО Express для обслуживания
статического контента (HTML, JS, CSS и т. Д.) Из «статического»
каталога. В самом низу сценария server.js я говорю серверу приложений
привязать (прослушивать) к порту 3000 и запросам сервера.
Чтобы вывести конечные точки REST API, ранее определенные в таблице в предыдущем разделе, мне требуется body-parser, и я указываю экспресс-приложению, что он будет использоваться для синтаксического анализа содержимого тела HTTP. Затем я отключаю конечные точки API, которые обслуживают запрошенный контент.
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(express.static(__dirname + '/static'));
app.get('/api/contacts', (req, res) => {
// TODO: retreive contacts and send to requester
});
app.post('/api/contacts', (req, res) => {
const { firstName, lastName, phone } = req.body
// TODO: create contact
});
app.delete('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
// TODO: find and delete contact by id
});
app.put('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
const { firstName, lastName, phone } = req.body
// TODO: find and update contact by id
});
app.listen(3000, () => {
console.log('Server is up on port 3000');
});
Как только будут определены модели данных и последующая структура базы данных SQLite, я вернусь и предоставлю функциональные возможности для обратных вызовов маршрутов API.
Sequelize ORM и интерфейс командной строки Sequelize
Sequelize.js - это популярный ORM для Node.js версии 4 и выше, который можно использовать для многих различных систем управления базами данных (СУБД), таких как MySQL, Postgres, SQLite и других. Существует дополнительная служебная библиотека под названием sequelize-cli, которая помогает автоматизировать некоторые повседневные, а также несколько нетривиальные части программирования баз данных.
В этом руководстве я буду использовать sequelize-cli для создания языка определения данных (DDL) для создания таблиц базы данных, а также для создания моделей данных, которые создают и выполняют язык манипулирования данными (DML) для запросов к базе данных, и даже система миграции для помощи в управлении версиями схемы базы данных.
Для начала я воспользуюсь sequelize-cli для инициализации уровня доступа к данным проекта следующим образом:
$ node_modules/.bin/sequelize init
Эта команда создаст конфигурацию каталогов, миграции, модели и сеялки, в результате чего моя структура проекта будет выглядеть так:
.
├── config
│  └── config.json
├── migrations
├── models
│  └── index.js
├── package-lock.json
├── package.json
├── seeders
├── server.js
└── static
└── index.html
Назначение каталогов следующее:
- config / index.js - определяет параметры подключения и sql dialet
- migrations - содержит сценарии миграции для управления версиями схемы.
- models - содержит модели данных, которые вы используете для взаимодействия с базой данных в коде вашего приложения.
- сидеры - содержат скрипты для заполнения вашей базы данных исходными данными
Во-первых, мне нужно отредактировать файл config / config.json, чтобы sequelize знал, что я собираюсь работать с базой данных SQLite. Так что изменю файл из этого ...
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
к этому ...
{
"development": {
"dialect": "sqlite",
"storage": "./database.sqlite3"
},
"test": {
"dialect": "sqlite",
"storage": ":memory"
},
"production": {
"dialect": "sqlite",
"storage": "./database.sqlite3"
}
}
который создаст и будет использовать файл базы данных SQLite с именем database.sqlite3 в корне проекта.
Теперь я буду следовать этой команде с другой, но на этот раз я буду
использовать model:generate
для определения моей модели контакта и ее
атрибутов, как показано ниже:
$ node_modules/.bin/sequelize model:generate --name Contact --attributes firstName:string,lastName:string,phone:string,email:string
Параметр --name
очевидно, является именем модели, которая должна быть
сгенерирована, а за --attibutes
следуют поля объекта, которые
определяют его вместе с их типами данных. Результатом этой команды
являются два новых файла:
- models / contact.js - модель данных, которая будет использоваться в логическом коде приложения Node.js
- migrations / yyyymmddHHMMSS-create-contact.js - скрипт миграции, который выдаст DDL SQL для создания таблицы контактов в базе данных.
В дополнение к атрибутам, указанным в model:generate
команда
sequelize-cli также сгенерирует автоматически увеличивающееся
целочисленное id
а также поля createdAt
и updatedAt
date-link.
Следующее, что нужно сделать, это запустить миграцию, чтобы база данных SQLite содержала следующую таблицу контактов:
$ node_modules/.bin/sequelize db:migrate
Эта команда укажет, что миграция прошла успешно. Теперь я могу открыть свой недавно созданный файл database.sqlite3 и просмотреть схему следующим образом:
$ sqlite3 database.sqlite3
SQLite version 3.20.1 2017-08-24 16:21:36
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE `SequelizeMeta` (`name` VARCHAR(255) NOT NULL UNIQUE PRIMARY KEY);
CREATE TABLE `Contacts` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `firstName` VARCHAR(255), `lastName` VARCHAR(255), `phone` VARCHAR(255), `email` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
CREATE TABLE sqlite_sequence(name,seq);
Обратите внимание, что там есть еще одна таблица с именем SequelizeMeta. Это таблица, которую Sequelize.js использует для сохранения порядка, в котором выполняются миграции. Например, выполнение этой команды покажет имя только что запущенного сценария миграции.
sqlite> .headers ON
sqlite> select * from SequelizeMeta;
name
20180726180039-create-contact.js
Теперь, когда у меня есть модель Contact, сопоставленная с таблицей базы данных, я хотел бы сгенерировать сценарий сидера, чтобы предварительно заполнить мою базу данных некоторыми данными, чтобы продемонстрировать, как взаимодействовать с ORM в моем коде приложения. Создание сценария сидера очень похоже на предыдущие команды.
$ node_modules/.bin/sequelize seed:generate --name seed-contact
Результатом является новый сценарий в каталоге сидеров соглашения об именах yyyymmddHHMMSS-seed-contact.js. Изначально это просто каркас интерфейса сидера вроде этого:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkInsert('Person', [{
name: 'John Doe',
isBetaMember: false
}], {});
*/
},
down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkDelete('Person', null, {});
*/
}
};
Я отредактирую его следующим образом, чтобы добавить несколько контактов.
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkInsert('Person', [{
name: 'John Doe',
isBetaMember: false
}], {});
*/
return queryInterface.bulkInsert('Contacts', [{
firstName: 'Snoop',
lastName: 'Dog',
phone: '111-222-3333',
email: ' [email protected] ',
createdAt: new Date().toDateString(),
updatedAt: new Date().toDateString()
}, {
firstName: 'Scooby',
lastName: 'Doo',
phone: '444-555-6666',
email: ' [email protected] ',
createdAt: new Date().toDateString(),
updatedAt: new Date().toDateString()
}, {
firstName: 'Herbie',
lastName: 'Husker',
phone: '402-437-0001',
email: ' [email protected] ',
createdAt: new Date().toDateString(),
updatedAt: new Date().toDateString()
}], {});
},
down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkDelete('Person', null, {});
*/
return queryInterface.bulkDelete('Contacts', null, {});
}
};
Наконец, мне нужно запустить сеялку, чтобы заполнить базу данных этими начальными контактами.
$ node_modules/.bin/sequelize db:seed:all
Это дает мне вывод в консоли, сообщающий мне, что таблица базы данных была успешно заполнена данными.
Итак, настройка наконец-то завершена. Теперь я могу перейти к более интересной части реального взаимодействия с моей моделью контактов, поддерживаемой базой данных, и предоставить тем, которые ранее были заглушены конечными точками API, их требуемые функции.
Однако, прежде чем я смогу использовать свою модель контакта, мне нужно
сообщить приложению Express.js, что оно существует. Я делаю это, добавив
require
в server.js и присвоения его const
переменной называется
db
, как так:
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const db = require('./models'); // new require for db object
Эта db
будет содержать мою модель контакта, доступную через
db.Contact
.
Я начинаю с простейшей конечной точки API - конечной точки
GET /api/contacts
. Этой конечной точке просто необходимо, чтобы все
контакты были возвращены из базы данных и сериализованы в ответ
вызывающей стороне. Я могу назвать findAll
метод Contact
объекта и
когда thenable обещания вернулось я могу стрелять контакты прочь к
клиенту , используя знакомый res.send(...)
, представленный в рамках
Express.js.
// server.js
// ommitting everything above ...
app.get('/api/contacts', (req, res) => {
return db.Contact.findAll()
.then((contacts) => res.send(contacts))
.catch((err) => {
console.log('There was an error querying contacts', JSON.stringify(err))
return res.send(err)
});
});
// omitting everything below...
Теперь я могу запустить свой сервер Express, указать в браузере localhost: 3000 / index.html, и меня встретит пользовательский интерфейс контактов.
$ nodemon
... или, если не используется nodemon:
$ npm start
{.ezlazyload .img-responsive}
Двигаясь дальше, я реализую функцию создания для конечной точки
POST /api/contacts
Создать новые экземпляры контактов очень просто.
Просто вызовите метод create(...)
db.Contact
, дождитесь разрешения
обещания путем связывания с then(...)
а затем я верну вновь созданный
контакт клиенту, например:
// server.js
// ommitting everything above ...
app.post('/api/contacts', (req, res) => {
const { firstName, lastName, phone } = req.body
return db.Contact.create({ firstName, lastName, phone })
.then((contact) => res.send(contact))
.catch((err) => {
console.log('***There was an error creating a contact', JSON.stringify(contact))
return res.status(400).send(err)
})
});
// omitting everything below ...
Теперь, если я введу другой контакт, скажем, Чудо-женщину, но оставлю номер телефона пустым (она сказала, что позвонит мне вместо того, чтобы давать свой номер), в форме «Добавить контакт» пользовательского интерфейса и нажмите «Сохранить», мой список контактов будет теперь у нас четыре участника: Snoop Dog, Скуби Ду, Херби Хаскер и Чудо-женщина.
Двигаясь дальше, теперь я могу добавить функциональность для реализации
поведения обновления для конечной точки PUT /api/contacts/:id
С точки
зрения программирования баз данных, это взаимодействие состоит из двух
частей.
Сначала я начинаю с поиска контакта, соответствующего идентификатору,
указанному в URL- db.Contact.findById(...)
, затем изменяю поля новыми
данными и заканчиваю использованием метода update(...)
contact
отправляющего обновленную модель обратно в базу данных, где эти
изменения будут сохранены.
// server.js
// ommitting everything above ...
app.put('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
return db.Contact.findById(id)
.then((contact) => {
const { firstName, lastName, phone } = req.body
return contact.update({ firstName, lastName, phone })
.then(() => res.send(contact))
.catch((err) => {
console.log('***Error updating contact', JSON.stringify(err))
res.status(400).send(err)
})
})
});
// omitting everything below...
Теперь я могу обновить номер телефона Чудо-женщины, потому что мой хороший приятель Снуп Дог любезно дал его мне. Для этого я нажимаю имя Чудо-женщины в списке контактов пользовательского интерфейса, чтобы развернуть форму сведений / обновления. После ввода ее номера 111-888-1213 и нажатия кнопки «Сохранить мою Чудо-женщину» контакт обновляется.
Последнее, что нужно сделать, это добавить возможность удаления контакта
через конечную точку DELETE /api/contacts/:id
Удаление очень похоже на
функциональность обновления в том, что экземпляр модели, который вы
хотите удалить, сначала нужно получить из базы данных, а затем вы просто
вызываете метод destroy()
экземпляра модели.
// server.js
// ommitting everything above ...
app.delete('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
return db.Contact.findById(id)
.then((contact) => contact.destroy())
.then(() => res.send({ id }))
.catch((err) => {
console.log('***Error deleting contact', JSON.stringify(err))
res.status(400).send(err)
})
});
// omitting everything below ...
Оказывается, хорошо, что контакты в моем списке контактов можно удалить, потому что Чудо-женщина оказалась настоящей королевой драмы и дралась с моей женой, а не с крутой Чудо-женщиной. Вы собираетесь быть удаленными.
В пользовательском интерфейсе я могу удалить Чудо-женщину, щелкнув мусорное ведро справа от ее имени.
Полный сценарий server.js показан ниже для полноты картины.
// server.js
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const db = require('./models');
const app = express();
app.use(bodyParser.json());
app.use(express.static(__dirname + '/static'));
app.get('/api/contacts', (req, res) => {
return db.Contact.findAll()
.then((contacts) => res.send(contacts))
.catch((err) => {
console.log('There was an error querying contacts', JSON.stringify(err))
return res.send(err)
});
});
app.post('/api/contacts', (req, res) => {
const { firstName, lastName, phone } = req.body
return db.Contact.create({ firstName, lastName, phone })
.then((contact) => res.send(contact))
.catch((err) => {
console.log('***There was an error creating a contact', JSON.stringify(contact))
return res.status(400).send(err)
})
});
app.delete('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
return db.Contact.findById(id)
.then((contact) => contact.destroy({ force: true }))
.then(() => res.send({ id }))
.catch((err) => {
console.log('***Error deleting contact', JSON.stringify(err))
res.status(400).send(err)
})
});
app.put('/api/contacts/:id', (req, res) => {
const id = parseInt(req.params.id)
return db.Contact.findById(id)
.then((contact) => {
const { firstName, lastName, phone } = req.body
return contact.update({ firstName, lastName, phone })
.then(() => res.send(contact))
.catch((err) => {
console.log('***Error updating contact', JSON.stringify(err))
res.status(400).send(err)
})
})
});
app.listen(3000, () => {
console.log('Server is up on port 3000');
});
Заключение
В этом руководстве я продемонстрировал, как использовать ORM Sequelize.js вместе с его CLI Sequelize для обработки программирования баз данных в сочетании с SQLite. При этом я предоставил глупое приложение со списком контактов, которое использует Node.js / Express.js для сервера приложений вместе с пользовательским интерфейсом на основе Vue.js. Sequelize.js - это довольно полезный ORM, который сокращает многие детали, необходимые для программирования баз данных в приложениях на основе Node.js, и довольно популярен среди разработчиков Node.js.
Как всегда, я благодарю вас за чтение и приветствую комментарии и критику ниже.