Основные понятия
Express - быстрый минималистичный серверный веб-фреймворк, написанный на node.js.
Можно использовать для создания полноценный веб-приложений, или для создания API.
Может отдавать JSON или формировать HTML.
Самый популярный фреймворк для JavaScript.
Самый простой веб-сервер:
1
2
3
4
5
6
7
8
9
10
| const express = require('express');
const app = express();
// define endpoints
app.get('/', function(req, res){
res.send('Hello, world');
});
app.listen(5000);
|
Что можно делать в обработчике пути?
- получать данные из базы данных
- генерировать HTML-страницы
- возвращать JSON
- обрабатывать данные из запроса
Очень простое задание точек приложения (путей, ендпойтнов). Есть методы app.get(), app.post(), app.delete() и так далее.
Можно создавать связующие функции (middleware).
Рекомендуется использовать Postman для тестирования запросов к серверу.
Простейший сервер
Создание проекта express:
1
2
3
4
| $ mkdir express_app && cd express_app
$ npm init -y
$ npm i express
$ touch index.js
|
Создание пустого сервера в файле index.js:
1
2
3
4
5
6
7
8
9
| // index.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port
${PORT}.`));
|
Запуск сервера:
Создание первого пути:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('<h1>Hello, world!</h1>');
})
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port
${PORT}.`));
|
Установка живого сервера nodemon
Создание скриптов запуска приложения:
1
2
3
4
5
| // package.json
"scripts":{
"start": "node index",
"dev": "nodemon index"
}
|
Запуск приложения по скрипту:
Отправка файла
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // index.js
const express = require('express');
const path = require('path');
const app = express();
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
})
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port
${PORT}.`));
|
1
2
| $ mkdir ./public
$ touch ./public/index.html
|
1
2
3
4
5
6
7
8
9
10
11
| <!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Website</title>
</head>
<body>
<h1>My Website</h1>
</body>
</html>
|
Статический сервер
1
2
3
4
5
6
7
8
9
10
11
12
13
| // index.js
const express = require('express');
const path = require('path');
const app = express();
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port
${PORT}.`));
|
1
| $ touch ./public/about.html
|
1
2
3
4
5
6
7
8
9
10
11
| <!-- about.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>About</title>
</head>
<body>
<h1>About</h1>
</body>
</html>
|
Возврат JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // index.js
const express = require('express');
const path = require('path');
const members = require('./Members');
const app = express();
// Get all members
app.get('/api/members', (req, res) => {
res.json(members);
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // Members.js
const members = [
{
id: 1,
name: 'John Doe',
email: 'john@gmail.com',
status: 'active'
},
{
id: 2,
name: 'Bob Williams',
email: 'bob@gmail.com',
status: 'inactive'
},
{
id: 3,
name: 'Shannon Jackson',
email: 'shannon@gmail.com',
status: 'active'
}
];
module.exports = members;
|
Создание связующей функции
Создание связующей функции (middlware):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // index.js
const express = require('express');
const path = require('path');
const members = require('./Members');
const app = express();
const logger = (req, res, next) => {
console.log('Hello');
next();
};
// Init middleware
app.use(logger);
// Get all members
app.get('/api/members', (req, res) => {
res.json(members);
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Логирование обращения клиента
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // index.js
const express = require('express');
const path = require('path');
const members = require('./Members');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.protocol}://${req.get('host')}${req.originalUrl}`);
next();
};
// Init middleware
app.use(logger);
// Get all members
app.get('/api/members', (req, res) => {
res.json(members);
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Установка модуля работы с датами:
Использование дат в логировании:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| // index.js
const express = require('express');
const path = require('path');
const moment = require('moment');
const members = require('./Members');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.protocol}://${req.get('host')}${
req.originalUrl}: ${moment().format()}`);
next();
};
// Init middleware
app.use(logger);
// Get all members
app.get('/api/members', (req, res) => {
res.json(members);
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Пример вывода:
1
2
3
| Server started on port 5000.
http://localhost:5000/api/members:
2022-04-04T09:22:45+03:00
|
Создание RestAPI
Использование параметров
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // index.js
const express = require('express');
const path = require('path');
const logger = require('./middleware/logger');
const members = require('./Members');
const app = express();
// Init middleware
app.use(logger);
// Get all members
app.get('/api/members', (req, res) => res.json(members));
// Get single member
app.get('/api/members/:id', (req, res) => {
res.send(req.params.id);
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Отдача одного элемента
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // index.js
const express = require('express');
const path = require('path');
const logger = require('./middleware/logger');
const members = require('./Members');
const app = express();
// Init middleware
app.use(logger);
// Get all members
app.get('/api/members', (req, res) => res.json(members));
// Get single member
app.get('/api/members/:id', (req, res) => {
res.json(members.filter(member => member.id === parseInt(req.params.id)));
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Обработка пустого вывода:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // index.js
const express = require('express');
const path = require('path');
const members = require('./Members');
const app = express();
// Get all members
app.get('/api/members', (req, res) => res.json(members));
// Get single member
app.get('/api/members/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found){
res.json(members.filter(member => member.id === parseInt(req.params.id)));
}
else {
res.status(400).json({msg: `No member with id of ${req.params.id}`});
}
});
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
Использование роутера
1
2
3
| $ mkdir ./routes
$ mkdir ./routes/api
$ touch ./routes/api/members.js
|
Выделение путей в отдельный файл:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // ./routes/api/members.js
const express = require('express');
const router = express.Router();
const members = require('../../Members');
const app = express();
// Get all members
router.get('/api/members', (req, res) => res.json(members));
// Get single member
router.get('/api/members/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found){
res.json(members.filter(member => member.id === parseInt(req.params.id)));
}
else {
res.status(400).json({msg: `No member with id of ${req.params.id}`});
}
});
module.exports = router;
|
Рефакторинг основного файла:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // index.js
const express = require('express');
const path = require('path');
const app = express();
app.use('/api/members', require(./routes/api/members));
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port
${PORT}.`));
|
Удаление общей части пути:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // ./routes/api/members.js
const express = require('express');
const router = express.Router();
const members = require('../../Members');
const app = express();
// Get all members
router.get('/', (req, res) => res.json(members));
// Get single member
router.get('/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found){
res.json(members.filter(member => member.id === parseInt(req.params.id)));
}
else {
res.status(400).json({msg: `No member with id of ${req.params.id}`});
}
});
module.exports = router;
|
Создание объекта на сервере
Использование тела запроса:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // ./routes/api/members.js
const express = require('express');
const router = express.Router();
const members = require('../../Members');
const app = express();
// Get all members
router.get('/', (req, res) => res.json(members));
// Get single member
router.get('/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found){
res.json(members.filter(member => member.id === parseInt(req.params.id)));
}
else {
res.status(400).json({msg: `No member with id of ${req.params.id}`});
}
});
// Create member
router.post('/', (req, res) => {
res.send(req.body);
});
module.exports = router;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // index.js
const express = require('express');
const path = require('path');
const app = express();
// Body parser middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use('/api/members', require(./routes/api/members));
// Создание статической папки
app.use(express.static(path.join(__dirname, 'public')));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // ./routes/api/members.js
const express = require('express');
const uuid = require('uuid');
const router = express.Router();
const members = require('../../Members');
const app = express();
// ...
// Create member
router.post('/', (req, res) => {
const newMember = {
id: uuid.v4(),
name: req.body.name,
email: req.body.email,
status: 'active',
}
if (!newMember.name || !newMember.email) {
return res.status(400).json({ msg: 'Please include a name and email' });
}
members.push(newMember);
res.json(members);
});
module.exports = router;
|
Изменение объекта на сервере
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| // ./routes/api/members.js
const express = require('express');
const uuid = require('uuid');
const router = express.Router();
const members = require('../../Members');
const app = express();
// ...
// Update Member
router.put('/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
const updMember = req.body;
members.forEach((member, i) => {
if (member.id === parseInt(req.params.id)) {
member.name = updMember.name;
member.email = updMember.email;
}
});
}
else {
res.status(400).json({ msg: `No member with the id of ${req.params.id}` });
}
});
module.exports = router;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // ./routes/api/members.js
const express = require('express');
const uuid = require('uuid');
const router = express.Router();
const members = require('../../Members');
const app = express();
// ...
// Update Member
router.put('/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
const updMember = req.body;
members.forEach((member, i) => {
if (member.id === parseInt(req.params.id)) {
member.name = updMember.name ? updMember.name : member.name;
member.email = updMember.email ? updMember.email : member.email;
res.json({ msg: 'Member updated', updMember });
}
});
}
else {
res.status(400).json({ msg: `No member with the id of ${req.params.id}` });
}
});
module.exports = router;
|
Удаление объекта
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // ./routes/api/members.js
const express = require('express');
const uuid = require('uuid');
const router = express.Router();
const members = require('../../Members');
const app = express();
// ...
// Update Member
router.put('/:id', (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
res.json({
msg: 'Member deleted',
members: members.filter(member => !idFilter(req)(member))
});
}
else {
res.status(400).json({ msg: `No member with the id of ${req.params.id}` });
}
});
module.exports = router;
|
Использование шаблонизатора handlebars
1
2
3
4
5
| $ npm i express-handlebars
$ mkdir ./views/
$ mkdir ./views/layouts/
$ touch ./views/layouts/main.handlebars
$ touch ./views/index.handlebars
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // index.js
const express = require('express');
const path = require('path');
const exphbs = require('express-handlebars');
const app = express();
//Handlebars middleware
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// Body parser middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get('/', (req, res) => res.render('index'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!-- ./views/layouts/main.handlebars -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="bootstrap.min.css">
<title>Members app/title>
</head>
<body>
<div class="container mt-4">
{{{ body }}}
</div>
</body>
</html>
|
1
2
| <!-- ./views/index.handlebars -->
<h1 class="text-center mb-3">Member app</h1>
|
Использование полей в шаблоне
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // index.js
const express = require('express');
const path = require('path');
const exphbs = require('express-handlebars');
const app = express();
//Handlebars middleware
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// Body parser middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get('/', (req, res) => res.render('index', {
title: "Member app"
}));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
1
2
| <!-- ./views/index.handlebars -->
<h1 class="text-center mb-3"></h1>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // index.js
const express = require('express');
const path = require('path');
const exphbs = require('express-handlebars');
const members = require('./Members');
const app = express();
//Handlebars middleware
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// Body parser middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get('/', (req, res) => res.render('index', {
title: "Member app",
members
}));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}.`));
|
1
2
3
4
5
6
7
8
9
| <!-- ./views/index.handlebars -->
<h1 class="text-center mb-3"></h1>
<h4>Members</h4>
<ul class="list-group">
{{ #each members}}
<li class="list-group-item">: </li>
{{ /each }}
</ul>
|
Добавление записи из формы
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| <!-- ./views/index.handlebars -->
<h1 class="text-center mb-3"></h1>
<form action="/api/members" method="POST" class="mb-4">
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" class="form-control">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" class="form-control">
</div>
<input type="submit" value="Add Member" class="btn btn-primary btn-block">
</form>
<h4>Members</h4>
<ul class="list-group">
{{#each members}}
<li class="list-group-item">: </li>
{{/each}}
</ul>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // ./routes/api/members.js
const express = require('express');
const uuid = require('uuid');
const router = express.Router();
const members = require('../../Members');
const app = express();
// ...
// Create member
router.post('/', (req, res) => {
const newMember = {
id: uuid.v4(),
name: req.body.name,
email: req.body.email,
status: 'active',
}
if (!newMember.name || !newMember.email) {
return res.status(400).json({ msg: 'Please include a name and email' });
}
members.push(newMember);
// res.json(members);
res.redirect('/');
});
module.exports = router;
|
Работа с базой данных MongoDB
По мотивам статьи.
Создание базового приложения
1
2
3
4
5
6
7
8
9
10
| npm init
npm install --save express mongodb body-parser
npm install --save-dev nodemon
mkdir app
cd app
mkdir routes
cd routes
touch index.js
touch note_routes.js
|
1
2
3
4
5
6
7
8
9
| // server.js
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const app = express();
const port = 8000;
app.listen(port, () => {
console.log('We are live on ' + port);
});
|
1
2
3
| // routes/note_routes.js
module.exports = function(app, db) {
};
|
1
2
3
4
5
6
| // routes/index.js
const noteRoutes = require('./note_routes');
module.exports = function(app, db) {
noteRoutes(app, db);
// Тут, позже, будут и другие обработчики маршрутов
};
|
1
2
3
4
5
6
7
8
9
10
11
12
| // 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.listen(port, () => {
console.log('We are live on ' + port);
});
|
1
2
3
4
5
6
7
| // routes/note_routes.js
module.exports = function(app, db) {
app.post('/notes', (req, res) => {
// Здесь будем создавать заметку.
res.send('Hello')
});
};
|
1
2
3
4
5
6
7
| // note_routes.js
module.exports = function(app, db) {
app.post('/notes', (req, res) => {
console.log(req.body)
res.send('Hello')
});
};
|
1
2
3
4
5
6
7
| // note_routes.js
module.exports = function(app, db) {
app.post('/notes', (req, res) => {
console.log(req.body)
res.send('Hello')
});
};
|
1
2
3
4
5
6
7
8
9
| // server.js
app.use(bodyParser.urlencoded({ extended: true }));
require('./app/routes')(app, {});
app.listen(port, () => {
console.log('We are live on ' + port);
});
|
Подключение к базе данных
1
2
3
| mkdir config
cd config
touch db.js
|
1
2
3
| module.exports = {
url : здесь будет ваш URL
};
|
1
2
3
4
5
6
7
8
9
10
11
12
| // server.js
app.use(bodyParser.urlencoded({ extended: true }));
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
require('./app/routes')(app, database);
app.listen(port, () => {
console.log('We are live on ' + port);
});
})
|
Добавление записей в базу данных
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 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]);
}
});
});
};
|
Чтение заметок: маршрут READ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // note_routes.js
var ObjectID = require('mongodb').ObjectID;
module.exports = function(app, db) {
app.get('/notes/:id', (req, res) => {
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);
}
});
});
// ...
};
|
Удаление заметок: маршрут DELETE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // note_routes.js
// ...
app.put ('/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);
}
});
});
// ...
|