Express.js & MongoDB

Третья лекция

План

 

  1. MongoDB
  2. Mongoose.js
  3. Express.js
  4. RESTful JSON API
  5. Задача

0

MongoDB

1

MongoDB?

MongoDB (от англ. humongous — огромный) — документоориентированная система управления базами данных (СУБД) с открытым исходным кодом, не требующая описания схемы таблиц. Написана на языке C++.

Why MongoDB?

Ура! Долой проектирование

Subdocuments

Or not?

Нормализация

vs

Денормализация

Нормализация

Денормализация

JOINS?

 

JS everywhere!

ObjectID

 

  • a 4-byte value representing the seconds since the Unix epoch,
  • a 3-byte machine identifier,
  • a 2-byte process id, and
  • a 3-byte counter, starting with a random value.

507f191e810c19729de860ea

Приходится создавать дополнительные id

_id, id, id2, articleId

 

Для чего такие сложности?

Совет

Бекапить все и почаще

Mongoose.js

2

Schema

2

const schema = new Schema({
  name:    String,
  binary:  Buffer,
  living:  Boolean,
  updated: { type: Date, default: Date.now },
  age:     { type: Number, min: 18, max: 65 },
  mixed:   Schema.Types.Mixed,
  _someId: Schema.Types.ObjectId,
  array:      [],
  ofString:   [String],
  ofNumber:   [Number],
  ofDates:    [Date],
  ofBuffer:   [Buffer],
  ofBoolean:  [Boolean],
  ofMixed:    [Schema.Types.Mixed],
  ofObjectId: [Schema.Types.ObjectId],
  nested: {
    stuff: { type: String, lowercase: true, trim: true }
  }
})

Query

2

Person.
  find({
    occupation: /host/,
    'name.last': 'Ghost',
    age: { $gt: 17, $lt: 66 },
    likes: { $in: ['vaporizing', 'talking'] }
  }).
  limit(10).
  sort({ occupation: -1 }).
  select({ name: 1, occupation: 1 });

Cursor

2

const cursor = Pet
  .find({ type: 'cat' })
  .cursor();

cursor.on('data', function(doc) {
  // Called once for every document
});
cursor.on('close', function() {
  // Called when done
});

Hooks

2

schema.pre('save', function(next) {
  if (!this.isModified('password')) return next();
  this.password = hashPassword(this.password);

  next();
});


schema.pre('validation', function(next) {
  // do stuff
  next();
});

Populate

2

Story
.find(...)
.populate({
  path: 'fans',
  match: { age: { $gte: 21 }},
  select: 'name',
  options: { limit: 5 }
})

Express.js

3

Hello World

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

3

Hello World

var express = require('express');
var app = express();

app.post('/', function (req, res) {
  res.send('Hello Post World!');
});

app.all('/', function (req, res) {
  res.send('Hello All World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

3

Request

req.app
req.baseUrl
req.body
req.cookies
req.fresh
req.hostname
req.ip
req.ips
req.method
req.originalUrl

3

req.params
req.path
req.protocol
req.query
req.route
req.secure
req.signedCookies
req.stale
req.subdomains
req.xhr

Response


res.download()	Prompt a file to be downloaded.
res.end()	End the response process.
res.json()	Send a JSON response.
res.jsonp()	Send a JSON response with JSONP support.
res.redirect()	Redirect a request.
res.render()	Render a view template.
res.send()	Send a response of various types.
res.sendFile()	Send a file as an octet stream.
res.sendStatus() Set the response status code and send its string representation as the response body.

3

Middlewares

const myLogger = (req, res, next) => {
  console.log('Log something');
  next();
};

const myBadLogger = (req, res, next) => {
  console.log('Do domething');
};

3

Middlewares


const requestTime = (req, res, next) => {
  req.requestTime = Date.now();
  next();
};

const isAdmin = (req, res, next) => {
  if (req.user.role !== 'admin') {
  	next('no access');
  }
  console.log('Do domething');
};

3

Catch-middleware

const isAdmin = (req, res, next) => {
  if (req.user.role !== 'admin') {
    return next('no access');
  }
  console.log('Do domething');
};

const catchErrors = (err, req, res, next) => {
  res.json({err});
};

3

How to use

app.use(requestTime);
app.use(myLogger);

app.get('/', function (req, res) {
  ...
  
  responseText += 
       'Generate time: ' + 
       (Date.now() - req.requestTime) + 'ms';

  res.send(responseText);
});

app.listen(3000);

3

Static


var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use('/assets', express.static('public', options));

3

Cookies

$ npm install cookie-parser

var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

3

Nested


app.use('/some1', mw1);

app.use('/some2', mw1, mw2, mw3);

app.use('/some3', [mw1, mw2, mw3]);

app.use('/some4', [mw1, 
                   [mw21, mw22, mw22], 
                   mw3], mw4);

3

Routes

 

app.get('/hello', function (req, res) {
  res.send('hello world')
})

app.get('/ab?cd', ... );

app.get('/ab+cd', ... );

app.get('/ab(cd)?e', ... );

app.get(/.*fly$/, ...);

app.get('/users/:userId/books/:bookId', function (req, res) {
  res.send(req.params)
})

3

Router

// birdRoutes.js
const router = express.Router();

// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});

router.get('/', function(req, res) {
  res.send('Birds home page');
});


// index.js
import birdRoutes from './birdRoutes'
...
app.use('/birds', birdRoutes);

3

Sub apps

var express = require('express');

var app = express(); // the main app
var admin = express(); // the sub app

admin.get('/', function (req, res) {
  console.log(admin.mountpath); // /admin
  res.send('Admin Homepage');
});

app.use('/admin', admin); // mount the sub app

3

NPM

  • express
  • express-async-router
  • express-graphql
  • express-jwt
  • express-ws
  • express-xml-bodyparser
  • express-winston

3

RESTful JSON API

 

4

REST

4

REST ( Representational State Transfer ) — архитектурный стиль взаимодействия компонентов распределённого приложения в сети.

 

Web приложение, не нарушающее накладываемые ограничения REST называют  «RESTful».

REST представляет собой согласованный набор ограничений, учитываемых при проектировании распределённой гипермедиа-системы.

 

Ограничения

4

  • Отсутствие состояния
  • Единообразие интерфейса

CRUD Routes

GET /users
GET /users/:id
POST /users
PUT /users/:id
DELETE /users/:id

4

Routes

GET /users/:id/skills

GET /users/:id/pets
POST /users/:id/pets
DELETE /users/:id/pets

4

Types

GET /users
GET /users.xml
GET /users.json
GET /users.yml

4

Status Codes

4

200 OK («хорошо»)
201 Created («создано»)
202 Accepted («принято»)
400 Bad Request («плохой, неверный запрос»)
401 Unauthorized («не авторизован»)
402 Payment Required («необходима оплата»)
403 Forbidden («запрещено»)
404 Not Found («не найдено»)
301 Moved Permanently («перемещено навсегда»)
302 Moved Temporarily («перемещено временно»)
302 Found («найдено»)
500 Internal Server Error («внутренняя ошибка сервера»)
501 Not Implemented («не реализовано»)
502 Bad Gateway («плохой, ошибочный шлюз»)
503 Service Unavailable («сервис недоступен»)
504 Gateway Timeout («шлюз не отвечает»)

Задача

5

  • Подключиться к базе данных
  • Создать модель User, Pet
  • Связать модели Pet -> User  
  • Разработать методы для вывода всех User, Pet. Pet  должны выводиться вместе со своими хозяйнами
  • Разработать метод для очистки базы данных
  • Закрыть роут очистки только для админа

статьте лайки, подписывайтесь на канал

Спасибо за внимание

Игорь Суворов

telegram.me/isuvorov

vk.com/igor.suvorov

github.com/isuvorov

 

Вопросы?

NaN

Made with Slides.com