Основные понятия

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);

Что можно делать в обработчике пути?

Очень простое задание точек приложения (путей, ендпойтнов). Есть методы 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
$ node index

Создание первого пути:

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
$ npm i -D nodemon

Создание скриптов запуска приложения:

1
2
3
4
5
// package.json
"scripts":{
  "start": "node index",
  "dev": "nodemon index"
}

Запуск приложения по скрипту:

1
$ npm run dev

Отправка файла

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
$ npm i moment

Использование дат в логировании:

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
$ npm i uuid
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')
  });
};

Postman

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);
});

Postman

Подключение к базе данных

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]);
      }
    });
  });
};

Postman

Чтение заметок: маршрут 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);
      }
    });
  });
  // ...
};

Postman

Удаление заметок: маршрут 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);
      } 
    });
  });
// ...

Postman

Основы Express.js

Основы Express.js