Académique Documents
Professionnel Documents
Culture Documents
MEAN - Node.js
Tabela de contedos
Introduo
Teoria
V8
1.1
Single thread
1.2
Event Loop
1.3
I/O Async
1.4
Libuv
1.5
Event Driven
1.6
API
1.7
Instalao
HTTP
createServer
3.1
Methods
3.2
Status Codes
3.3
Rotas
3.4
get
3.5
request
3.6
post
3.7
put
3.8
Callbacks
FileSystem
mkdir
5.1
open
5.2
readdir
5.3
readFile
5.4
rename
5.5
writeFile
5.6
Be MEAN - Node.js
npm
6
init
6.1
install --global ou -g
6.2
install --save
6.3
install --save-dev
6.4
install --optional
6.5
run
6.6
Globals
CommonJS
7.1
Process
Mongoose
Create
9.1
Retrieve
9.2
Update
9.3
Delete
9.4
Eventos
Mongoose
Promises
Mongoose
Mongoose
10
10.1
11
11.1
12
Arquitetura Atmica
12.1
Validate
12.2
Populate
12.3
Plugins
12.4
TDD
13
assert
13.1
chai
13.2
Teste Atmicos
14
Express
15
Model
15.1
Be MEAN - Node.js
Controller
15.2
Routes
15.3
Express
16
Events
16.1
Promises
16.2
Middlewares
16.3
Express
17
TDD
17.1
Socket.io
18
TDD
18.1
REPL
19
UDP
20
TCP
21
HTTPS
22
23
pokemons
23.1
github
23.2
redtube
23.3
23.4
Autenticao
24
Session
24.1
Express
24.2
Mongoose/MongoDB
24.3
Autenticao
Token
25
25.1
Be MEAN - Node.js
Node.js
Nesse curso abordarei a verso 5 do Node.js.
O MongoDb um banco e dados NoSQL open-source e orientado a documentos
JSON. Ele foi criado para ser escalado horizontalmente, conceito que veremos
mais a frente.
ndice
Teoria
V8
Single thread
Event Loop
I/O Async
Libuv
Event Driven
API
Instalao
HTTP
createServer
Methods
Status Codes
Rotas
get
request
post
put
Callbacks
FileSystem
mkdir
open
readdir
readFile
Introduo
Be MEAN - Node.js
rename
writeFile
npm
init
install --global ou -g
install --save
install --save-dev
install --optional
run
Globals
CommonJS
Process
Mongoose
Create
Retrieve
Update
Delete
Eventos
Mongoose
Promises
Mongoose
Mongoose
Arquitetura Atmica
Validate
Populate
Plugins
TDD
assert
chai
Teste Atmicos
Express
Model
Controller
Routes
Express
Events
Introduo
Be MEAN - Node.js
Promises
Middlewares
Express
TDD
Socket.io
TDD
REPL
UDP
TCP
HTTPS
Utilizando APIs externas
pokemons
github
redtube
instagram
Autenticao
Session
Express
Mongoose/MongoDB
Autenticao
Token
Code Style
Code Style
Types
References
Objects
Arrays
Destructuring
Strings
Functions
Arrow Functions
Constructors
Modules
Introduo
Be MEAN - Node.js
Iterators and Generators
Properties
Variables
Hoisting
Comparison Operators & Equality
Blocks
Comments
Whitespace
Commas
Semicolons
Type Casting & Coercion
Naming Conventions
Accessors
Events
jQuery
ECMAScript 5 Compatibility
ECMAScript 6 Styles
Testing
Performance
Resources
In the Wild
Translation
The JavaScript Style Guide Guide
Chat With Us About JavaScript
Contributors
License
Introduo
Be MEAN - Node.js
Teoria
Teoria
Be MEAN - Node.js
V8
Teoria
10
Be MEAN - Node.js
Single thread
Teoria
11
Be MEAN - Node.js
O Node.js trabalha apenas com uma thread, podendo ser criadas outras, com
isso economizando muita memria, diferentemente da forma que o Apache
trabalha e voc percebe claramente a diferena de utilizao de memria, j que
com apenas uma thread voc no precisa criar um processo novo para cada
usurio conectado, acarretando tambm em uma economia de CPU.
Mas como ele consegue gerenciar a porra toda apenas com uma thread?
Com uma coisinha linda fachamada Event Loop.
Event Loop
Teoria
12
Be MEAN - Node.js
O Event Loop nada mais que uma fila infinita que recebe todos os eventos
emitidos pelo Node.js, isso inclui as requisies que recebemos no servidor
HTTP.
Quando o evento chega para ser exeutado no Event Loop, caso ele seja
assncrono, ele ser enviado para onde deve ser executado, por exemplo:
filesystem, network, process, etc.
Como o processo assncrono ele ir executar e s aps sua finalizao que
ele dispara o trigger para seu callback, esse voltando para a fila que ir ser
executada pelo Event Loop.
Teoria
13
Be MEAN - Node.js
Libuv
Foi no Node 0.5 que ela entrou em cena e na verso 0.9 a libev foi removida.
Features
Full-featured event loop backed by epoll, kqueue, IOCP, event ports.
Asynchronous TCP and UDP sockets
Asynchronous DNS resolution
Asynchronous file and file system operations
File system events
ANSI escape code controlled TTY
IPC with socket sharing, using Unix domain sockets or named pipes
(Windows)
Teoria
14
Be MEAN - Node.js
Child processes
Thread pool
Signal handling
High resolution clock
Threading and synchronization primitives
Lista retirada da documentao
Caso voc queira se aprofundar mais indico esse material.
I/O Async
Qualquer funo do Node.js, por padro, assncrona por isso sempre
precisamos de uma funo que executar aps o final desse processamento,
essa que executa posteriormente chamada de callback, falaremos muito mais
sobre isso futuramente.
Mas ento o que quer dizer que o I/O assncrono?
Basicamente diz que qualquer leitura ou escrita de dados no espera seu
processo finalizar para continuar o script, nesse caso os processos ocorrem
paralelamente execuo.
Para termos uma ideia melhor de como o funcionamento assncrono, vamos
pensar um restaurante sendo sncrono.
Teoria
15
Be MEAN - Node.js
No restaurante sncrono quando uma mesa atendida ela precisa receber seu
pedido antes que o garom possa antender outra mesa!!!
Teoria
16
Be MEAN - Node.js
Quando um pedido finalizado no Restaurante Assncrono uma
campainha/evento emitido.
O mesmo acontece com nossos sistemas, quando voc envia uma requisio
assncrona voc no tem a certeza quando ela ir retornar, por isso usamos
Promises, mas isso um assunto posterior.
Isso me lembrou o Princpio da incerteza de Heisenberg na fsica, mais algum
pira nisso como eu? :p
Teoria
17
Be MEAN - Node.js
Thread Pool
O node.js trabalha uma tread-pool a partir de uma thread, a thread em que ele
instanciado, facilmente confundvel com o que diz respeito a multi-thread,
porm o node no dispe desse custoso mecanismo para fazer i/o assncrono, se
voc quiser dispor de todos os mecanismos de ncleos de um processador, voc
pode usar mecanismo de balanceamento e comunicao entre portas,
cluster(um mdulo nativo do node.js).
Definio: Uma thread-pool pode ser comparada com um array em que o
nmero de colunas representaria uma idle-thread, uma thread pr executada
que s espera um processo para trabalhar e o processo, o indice desse array. O
node.js trabalha o processo como um I/O que a aplicao em node.js faz. Ou seja
um I/O para uma idle-thread.
Event Driven
API
A api do node.js consiste de uma forte influncia de outras plataformas.
O unix, sistema operacional usado como base para osx e linux, uma forte
influncia para o node.js, no atoa que roda muito bem no unix, discartando o
mrito do nosso amiguinho ruindows* do tio Gates.
Por causa dessa influncia o node.js extensivamente modular, possuindo
mdulos para tudo, inclusive a sua aplicao ser tratada como mdulo para o
node.js. Ento muito importante ter um cdigo consistente, pouco dependente,
pouco aclopado, e por fim, modularizado :)
Teoria
18
Be MEAN - Node.js
Instalao
Voc pode baixar os instaladores em nodejs.org/en/download/
Ou utilizando o brew, para Mac, ou apt-get/yum para Linux.
apt-get
curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash sudo apt-get update
sudo apt-get install nodejs
Caso voc receba um erro que o comando node no exista, faa o seguinte
link:
sudo ln -s /usr/bin/nodejs /usr/bin/node
Instalao
19
Be MEAN - Node.js
Caso tenha vindo uma verso antiga, a atual a 5, voc pode fazer adicionar
um PPA (arquivo de pacotes pessoais) mantido pelo NodeSource.
Primeiro, voc precisa instalar o PPA de modo a obter acesso ao seu contedo:
curl -sL https://deb.nodesource.com/setup | sudo bash -
yum
sudo yum install nodejs npm --enablerepo=epel
Instalao
20
Be MEAN - Node.js
EPEL (Extra Packages for Enterprise Linux) is open source and free
community based repository project from Fedora team which provides 100%
high quality add-on software packages for Linux distribution including RHEL
(Red Hat Enterprise Linux), CentOS, and Scientific Linux. Epel project is not a
part of RHEL/Cent OS but it is designed for major Linux distributions by
providing lots of open source packages like networking, sys admin,
programming, monitoring and so on. Most of the epel packages are
maintained by Fedora repo.
Via http://www.tecmint.com/how-to-enable-epel-repository-for-rhel-centos-6-5/
brew
Tendo o brew instalado basta executar:
brew update
brew install node
E pronto.
Mas tudo isso apenas se voc no utilizar o instalador disponilizado aqui.
Instalao
21
Be MEAN - Node.js
HTTP
O mdulo http o principal mdulo da nossas aplicaes pois com ele que
criamos um servidor web para fornecer nossos sistemas.
Ele trabalha com diversas funcionalidades do protocolo HTTP, porm no iremos
abranger todas.
Esse um mdulo nativo, no necessitando que seja instalado anteriormente.
Como usar
Para utilizar esse mdulo basta import-lo para seu cdigo:
require('http')
Cada requisio enviada possui cabealhos que dizem o que essa requisio faz,
vamos ver um exemplo de uma requisio GET :
GET / HTTP/1.1
Host: webschool.io
Connection: close
User-Agent: Chrome/46.0.2490.86
Accept-Encoding: gzip
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
Cache-Control: no
Accept-Language: de,en;q=0.7,en-us;q=0.3
Referer:
HTTP
22
Be MEAN - Node.js
HTTP/1.1 200 OK
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html
Date: Sun, 06 Dec 2015 01:07:17 GMT
ETag: W/'55f9df1c-23f'
Last-Modified: Wed, 16 Sep 2015 21:29:00 GMT
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding
Methods
O protocolo HTTP possui um conjunto de mtodos/verbos que o cliente pode
invocar, veja abaixo a lista dos verbos mais usados:
GET: Requisita um representao do recurso especificado (O mesmo
recurso pode ter vrias representaes, ao exemplo de servios que
retornam XML e JSON). HEAD: Retorna os cabealhos de uma resposta
(sem o corpo contendo o recurso)
POST: Envia uma entidade e requisita que o servidor aceita-a como
subordinada do recurso identificado pela URI.
PUT: Requisita que um entidade seja armazenada embaixo da URI
fornecida. Se a URI se refere a um recurso que j existe, ele modificado; se
a URI no aponta para um recurso existente, ento o servidor pode criar o
recurso com essa URI.
DELETE: Apaga o recurso especificado.
TRACE: Ecoa de volta a requisio recebida para que o cliente veja se
houveram mudanas e adies feitas por servidores intermedirios.
HTTP
23
Be MEAN - Node.js
OPTIONS: Retorna os mtodos HTTP que o servidor suporta para a URL
especificada.
CONNECT: Converte a requisio de conexo para um tnel TCP/IP
transparente, usualmente para facilitar comunicao criptografada com SSL
(HTTPS) atravs de um proxy HTTP no criptografado.
PATCH: Usado para aplicar modificaes parciais a um recurso.
E so com 4 verbos diferentes que criamos um CRUD, que essencial em
qualquer sistema.
No CRUD precisamos ter 4 aes:
Create
Retrieve/Read
Update
Delete
Sabendo disso agora, quais so os 4 verbos que utilizamos para o CRUD?
ps: Faz parte do exerccio dessa aula.
Status Codes
Os cdigos de retorno HTTP so compostos por 3 dgitos que seguem um
formato padro dando melhor direcionamento para a identificao correta do
retorno.
Os cdigos de status so divididos em:
1XX Informacional
No h necessidade de se preocupar com este, serve apenas para informar que
a informao foi recebida e que o processo continua.
2XX Sucesso
Significa que o pedido foi recebido com sucesso. o que sempre acontece
quando suas pginas so carregadas
HTTP
24
Be MEAN - Node.js
200 OK. O pedido ao servidor foi atendido com sucesso. A pgina web existe e
ser enviada ao user-agent (navegador, rob de busca).
3XX Redirecionamento
Serve para avisar direto no cabealho HTTP uma mudana de pgina. Diferente
de um Meta Refresh ou usar javascript, ele permite um redirecionamento suave
e importante para SEO.
301 Movido Permanentemente. Muito til para redirecionar pginas. Serve para
redirecionar suas URLs que foram movidas permanentemente. Assim voc evita
pginas de cdigo 404 ou pode tornar URLs dinmicas com em URLs limpas.
302 Movido Temporariamente. Serve tambm para mover, mas com funo
temporria. A vantagem que voc pode reverter isto. Funciona bem para
manutenes ou alterao no definitiva. O rob de busca continua visitando o
endereo original.
25
Be MEAN - Node.js
503 Servio indisponvel. Pode ser um erro temporrio. Uma manuteno ou
uma grande quantidade de acessos pode derrubar o servidor.
Lista dos cdigos de status.
Cats API
createServer
Antes de iniciar a criao dos cdigos desse mdulo crie uma pasta para o
Workshop e dentro dela uma para o Node.js, por exemplo:
workshop-be-mean/nodejs/
HTTP
26
Be MEAN - Node.js
Para iniciarmos um servidor HTTP utilizaremos a funo createServer que
recebe uma funo com 2 parmetros:
request;
response.
// hello-world.js
var http = require('http');
http.createServer(function(request, response){
response.writeHead(200, {'Content-Type': 'text/plain'});
response.write('Be MEAN');
response.end();
}).listen(3000, function(){
console.log('Servidor rodando em localhost:3000');
});
Ou:
var http = require('http');
var server = http.createServer(function(request, response){
response.writeHead(200, {'Content-Type': 'text/plain'});
response.write('Be MEAN');
response.end();
});
server.listen(3000, function(){
console.log('Executando Servidor HTTP');
});
HTTP
27
Be MEAN - Node.js
function(request, response){
response.writeHead(200, {'Content-Type': 'text/plain'});
response.write('Be MEAN');
response.end();
}
HTTP
28
Be MEAN - Node.js
// hello-http.js
var http = require('http');
http.createServer(function(request, response){
response.writeHead(200, {'Content-Type': 'text/plain'});
response.write('<h1>Be MEAN</h1>');
response.end();
}).listen(3000, function(){
console.log('Servidor rodando em localhost:3000');
});
HTTP
29
Be MEAN - Node.js
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Be MEAN - Instagram</title>
</head>
<body>
<h1>Be MEAN - Instagram - html</h1>
</body>
</html>
Rotas
A primeira coisa que nosso sistema web precisa ter para ser acessado so rotas,
ento vamos criar nossa primeira rota /api/v1 que retornar informaes
sobre a nossa api.
HTTP
30
Be MEAN - Node.js
Primeiramente vamos criar o JSON de resposta:
const JSON = {
version: 1.0
, name: 'Be MEAN'
, created_at: Date.now()
};
HTTP
31
Be MEAN - Node.js
// server.js
'use strict';
var date = (new Date()).toJSON();
const http = require('http')
, SUCCESS = {
version: '1.0'
, name: 'Be MEAN'
, returned_at: date
}
, ERROR = {
message: 'DEU MERDA FI!!!!'
};
http.createServer(function(req, res){
if(req.url === '/api/v1') {
res.writeHead(200, {'Content-Type': 'application/json'});
res.write(JSON.stringify(SUCCESS));
}
else {
res.writeHead(400, {'Content-Type': 'application/json'});
res.write(JSON.stringify(ERROR));
}
res.end();
}).listen(3000, function(){
console.log('Servidor rodando em localhost:3000');
});
npm i -g nodemon
HTTP
32
Be MEAN - Node.js
Tambm instale o POSTMAN que uma extenso para o Chrome, para
testarmos nossas APIs, ser de grande utilizao durante o curso.
Depois de ter feito tudo isso levante seu servidor com o nodemon :
nodemon server.js
6 Dec 16:05:26 - [nodemon] v1.3.8
6 Dec 16:05:26 - [nodemon] to restart at any time, enter `rs`
6 Dec 16:05:26 - [nodemon] watching: *.*
6 Dec 16:05:26 - [nodemon] starting `node server.js`
Servidor rodando em localhost:3000
HTTP
33
Be MEAN - Node.js
Querystring
HTTP
34
Be MEAN - Node.js
'use strict';
// file: hello-querystring.js
let http = require('http')
, url = require('url');
http.createServer(function(request, response){
var result = url.parse(request.url, true);
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('<html><body>');
response.write('<h1>Query string</h1>');
response.write('<ul>');
for(var key in result.query){
response.write('<li>'+key+' : '+result.query[key]+'</li>');
}
response.write('</ul>');
response.write('</body></html>');
response.end();
}).listen(3000, function(){
console.log('Servidor rodando em localhost:3000');
});
get
Para dar continuidade no HTTP vamos ver um dos verbos mais usados, o GET .
Com ele iremos requisitar informaes na nossa ou em outras APIs e isso que
faremos agora, consultaremos a API dos Pokemons.
Usaremos a funo http.get seguindo o seguinte modelo:
HTTP
35
Be MEAN - Node.js
http.get({
hostname: 'localhost',
port: 80,
path: '/',
agent: false // criar um novo agente apenas para este pedido
}, function (res) {
// Faa algo com res
})
Agora criando a requisio para o nosso servidor que est rodando o helloquerystring.js :
// file: http-get-localhost-querystring.js
'use strict';
const http = require('http');
http.get({
hostname: 'localhost',
path: '/user?name=Suissa&teacher=true&age=31',
port: 3000,
agent: false
}, function (response) {
let body = '';
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + response.headers);
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
console.log('Resposta: ', body);
});
});
HTTP
36
Be MEAN - Node.js
Mas vamos fazer uma pequena modificao para vocs j se acostumarem com
[Arrow Functions] do [ES6]:
// file: http-get-localhost-querystring.js
'use strict';
const http = require('http');
http.get({
hostname: 'localhost',
path: '/user?name=Suissa&teacher=true&age=31',
port: 3000,
agent: false
}, (response) => {
let body = '';
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + response.headers);
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
console.log('Resposta: ', body);
});
});
Eu poderia ter omitido os () de (response) => , porm deixei para ficar mais
fcil a sua migrao.
Salve esse cdigo como http-get-localhost-querystring.js e execute
como visto abaixo:
node http-get-localhost-querystring.js
STATUS: 200
HEADERS: {"content-type":"text/html","date":"Sat, 12 Dec 2015 14:46:37 GMT"
Resposta: <html><body><h1>Be - MEAN</h1><h2>Query string</h2><ul><
HTTP
37
Be MEAN - Node.js
Agora vou explicar o que aconteceu no cdigo, primeiramente passamos o JSON
de configurao da requisio:
{
hostname: 'localhost',
path: '/user?name=Suissa&teacher=true&age=31',
port: 3000,
agent: false
}
38
Be MEAN - Node.js
Um objeto IncomingMessage criado por http.Server ou
http.ClientRequest e passado como o primeiro argumento para o
request e response , respectivamente. Ele pode ser usado para acessar
request
Primeira coisa que precisamos fazer criar o JSON de configurao da
requisio:
const options = {
host: 'api.redtube.com'
, path: '/?data=redtube.Videos.searchVideos&search=Sasha%20Gray'
};
HTTP
39
Be MEAN - Node.js
function callback(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Dados finalizados: ', data)
})
}
HTTP
40
Be MEAN - Node.js
// file: http-request.js
'use strict';
const http = require('http');
const options = {
host: 'api.redtube.com'
, path: '/?data=redtube.Videos.searchVideos&search=Sasha%20Gray'
};
function callback(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Dados finalizados: ', data)
})
}
const req = http.request(options, callback);
req.on('error', (e) => {
console.log('ERROOOO: ' + e.message);
});
req.end();
HTTP
41
Be MEAN - Node.js
node http-request.js
STATUS: 200
Create - POST
Para executarmos a ao de Create usaremos o verbo POST , normalmente, na
mesma url que usamos o GET , requisitaremos em uma API externa pois ainda
no estamos trabalhando com banco aqui e queremos um efeito real.
Essa API est em http://webschool-io.herokuapp.com/ .
// file: http-request-post.js
'use strict';
const http = require('http');
const querystring = require('querystring');
const postData = querystring.stringify({
name: 'Jean Nascimento'
, type: 'professor'
});
const options = {
host: 'webschool-io.herokuapp.com'
, path: '/api/pokemons'
, method: 'POST'
, headers: {
'Content-Type': 'application/x-www-form-urlencoded'
, 'Content-Length': postData.length
}
};
function callback(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
HTTP
42
Be MEAN - Node.js
let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Dados finalizados: ', data)
})
}
const req = http.request(options, callback);
req.on('error', (e) => {
console.log('ERROOOO: ' + e.message);
});
req.write(postData);
req.end();
name=Jean%20Nascimento&type=professor
HTTP
43
Be MEAN - Node.js
const options = {
host: 'webschool-io.herokuapp.com'
, path: '/api/pokemons'
, headers: {
'Content-Type': 'application/x-www-form-urlencoded'
, 'Content-Length': postData.length
}
};
FIM
Dica: https://github.com/floatdrop/debug-http
HTTP
44
Be MEAN - Node.js
Callback
Olha s que legal, callback um termo bastante usado nas nossas aulas e
deveremos entender a sua razo de existencia. O node.js trabalha fortemente
com programao assncrona, I/O assncrono s code-nome.
Vamos mostrar isso em cdigo e explicar linha a linha.
/**
* Olha s
*/
function sayName (name, cb) {
if (typeof name === 'undefined') {
var error = new Error("Voc deve passar um parmetro como string para
/*** aqui a gente s retorna o erro da nossa funo */
return cb(err, null);
} else {
/*** agora definimos o nosso erro como null e retornamos o valor passad
return cb(null, name);
}
}
Callbacks
45
Be MEAN - Node.js
Confesso para os senhores que um pouco verboso usar callback, mas s assim
conseguimos garantir a continuao da execuo de uma determinada funo.
O legal ter uma forma extendida de nossa funo.
Esse estilo de programao herdada do paradigma funcional que o javascript
sofre influncia, facilmente confudvel com o conceito de first-class function,
porm o que se adequa mais ao conceito de continuao de uma funo o
high-order-functions.
Essa continuao um pattern de programao funcional chamado de
continuation-passing-style. bom conhecermos esses nomes por que se a gente
quiser se aprofundar mais nos conceitos a gente sabe para onde ir :).
Nem tudo mar de flores, nem tudo to bonito assim.
Callback-hell ou boomerang-effect:
E se agente quiser tratar callbacks como um certa ordem? Vejamos o seguinte
exemplo com mais de uma funo definida
Callbacks
46
Be MEAN - Node.js
/*** agora definimos o nosso erro como null e retornamos o valor pass
return cb(null, name);
}
}, 100);
}
function saySurname (name, cb) {
// dessa vez a gente vai usar um primito assincrono
// para garantir que essa funo seja assincrona de qualquer forma
setTimeout(function () {
if (typeof name === 'undefined') {
/*** agora definimos o nosso erro como null e retornamos o valor pass
return cb(null, name);
}
}, 105);
}
Callbacks
47
Be MEAN - Node.js
Aff... a tendendncia desse cdigo ficar cada vez mais feia devido a
complexidade das funes que criarmos, ento para evitar isso ns temos varias
solues que ns iremos tratar posteriormente. :)
Beijo no corao.
Callbacks
48
Be MEAN - Node.js
FileSystem
FileSystem
49
Be MEAN - Node.js
NPM
O npm nada mais que o gerenciador de pacotes do Node.js.
Uns dizem que npm significa Node Package Manager, outros dizem que no,
ento FODA-SE. Ele nosso gerenciador dos mdulos que iremos instalar.
Se voc ver pelo site do npm, ele o gerenciador de pacotes do JavaScript.
npm
50
Be MEAN - Node.js
tima pergunta gafanhoto, por isso que esse array de dependncia to
importante, pois nele que voc ir especificar cada mdulo e sua verso.
Fazendo com que a listagem de todas as dependncias estejam no
package.json , quando algum clonar o projeto receber apenas os cdigos e
o package.json .
Com o package.json em mos, basta executar npm install para que o
npm instale todas aquelas dependncias listadas.
Simples no?
Por isso SEMPRE ADICIONE node_module no seu .gitignore!
n
Para instalar o n bem fcil, basta clonar esse repositrio do TJ e depois
executar o make install :
npm
51
Be MEAN - Node.js
######################################################################## 100
...
installed : v5.2.0
n git:(master) node -v
v5.2.0
n git:(master) npm -v
3.3.12
nvm
Sobre o nvm eu j escrevi aqui no meu blog - Node.js - O que nvm e como
gerenciar verses do Node?.
Aconselho a leitura.
Agora vamos voltar ao npm.
npm init
npm
52
Be MEAN - Node.js
Quando iniciarmos qualquer projeto utilizando o Node.js, usaremos esse
comando npm init para inicializar o projeto.
Ento vamos iniciar nosso primeiro projeto utilizando os dados dos Pokemons,
para isso crie uma pasta chamada pokemons-api dentro da sua pasta do
Node.js do nosso workshop.
mkdir pokemons-api
cd pokemons-api
npm init
Depois da licena ele pergunta se voc confirma aqueles dados, basta apertar
Enter ou digitar sua licena, no meu caso a WTFPL.
npm
53
Be MEAN - Node.js
{
"name": "pokemons-api",
"version": "1.0.0",
"description": "Api dos pokemons",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"api",
"pokemons",
"node.js",
"mean"
],
"author": "Suissa",
"license": "WTFPL"
}
package.json
name
As coisas mais importantes no seu package.json so o nome e a verso, pois
eles so obrigatrios e seu package no instalar sem eles.
O nome e a verso, juntos, formam um identificador que para ser nico, no
esquea de mudar a verso quando soltar algum release novo.
Algumas regras:
o nome precisa ser menor que 214 caracteres;
o nome no pode comear com um . ou _ ;
novos packages no podem ter letras maisculas no nome;
o nome no pode conter quaisquer caracteres non-URL-safe.
Algumas dicas:
no use o mesmo nome de um mdulo do core do Node;
no coloque "js" ou "node" no nome;
npm
54
Be MEAN - Node.js
o nome provavelmente ser passado como um argumento para require(), por
isso deve ser algo curto, mas tambm razoavelmente descritivo;
voc pode querer verificar o registro do npm para ver se h algo com esse
nome j, antes que voc fique muito apegado a ele. https://www.npmjs.com/
npm install
O comando npm install serve para instalar algum mdulo/pacote ou as
dependncias listadas no package.json
Vamos conhecer um pouco mais sobre as opes do npm install .
Erro: EACCES
npm
55
Be MEAN - Node.js
Voc pode receber um erro EACCES quando voc tentar instalar um pacote
global. Isso indica que voc no tem permisso para gravar os diretrios que o
NPM usa para armazenar pacotes globais e comandos.
Voc pode corrigir esse problema usando uma das duas opes:
Alterar a permisso do diretrio padro do NPM.
Alterar o diretrio padro do npm para outro diretrio.
Opo 1: Mudar a permisso do diretrio padro do NPM
Primeiramente voc precisa saber qual o diretrio padro do NPM, para isso
execute npm config get prefix :
npm config get prefix
/usr/local
Depois para mudar a permisso desse diretrio para que seu usurio seja o dono
dele basta executar sudo chown -R whoami diretorio
Caso voc no queira mudar a permisso de todo o diretrio, voc pode mudar
apenas as permisses dos sub-diretrios:
bin
share
lib/node_modules
Opo 2: Mudar o diretrio padro do NPM para outro
H momentos em que voc no deseja alterar a propriedade do diretrio padro
que o NPM usa; por exemplo, se voc estiver compartilhando uma mquina com
outros usurios.
Neste caso, voc pode configurar npm para usar um diretrio diferente.
Crie um diretrio para instalaes globais:
npm
56
Be MEAN - Node.js
mkdir ~/npm-global
npm
57
Be MEAN - Node.js
npm
58
Be MEAN - Node.js
59
Be MEAN - Node.js
Ir adicionar sua dependncia em optionalDependencies .
So dependncias opcionais que no devem interferir na execuo do projeto.
npm run
Esse um tpico deveras interessante, pois voc pode executar scripts para
automatizar suas tarefas.
Para demonstrar isso primeiro crie uma pasta chamada npm e dentro dela
execute o npm init , depois crie o script.js com esse pequeno cdigo:
console.log("Rodei!");
Alm de executar seus scripts ele tambm possui os seguintes scripts nativos:
prepublish: Roda ANTES do mdulo ser publicado. (Also run on local npm
npm
60
Be MEAN - Node.js
install without any arguments.);
publish: publica um mdulo no npm;
postpublish: Roda DEPOIS do mdulo ser publicado;
preinstall: Roda ANTES do mdulo ser instalado;
install: Instala todas as dependncias localmente;
postinstall: Roda DEPOIS do mdulo ser instalado;
preuninstall, uninstall: Roda ANTES the package ser desinstalado;
postuninstall: Roda DEPOIS the package is desinstalado;
preversion: Roda ANTES do comando version;
version: Execute para modificar sua verso;
postversion: Roda DEPOIS de rodar o version;
pretest: Roda ANTES do test;
test: Roda o comando que executa os testes;
posttest: Roda DEPOIS de executar os testes;
prestop: Roda ANTES do comando stop;
stop: Roda o script caso definido;
poststop: Roda DEPOIS do comando stop;
prestart: Roda ANTES do comando start;
start: Executa o comando definido, normalmente utilizado para levantar o
servidor;
poststart: Roda DEPOIS do comando start;
prerestart: Roda ANTES do comando restart;
restart: Reinicia o script;
postrestart: Roda DEPOIS do comando restart.
O restart na verdade executa o stop e o start tambm, e seus scripts
pre e post, na ordem abaixo:
prerestart
prestop
stop
poststop
restart
prestart
start
poststart
postrestart
npm
61
Be MEAN - Node.js
ps: Com os scripts nativos no necessrio npm run script, apenas npm script.
npm
62
Be MEAN - Node.js
Mongoose
O Mongoose um dos projetos mais utilizados quando trabalhamos com o
MongoDb pois ele nos d uma funcionalidade que o MongoDb no d
nativamente, voc sabe qual?
Mongoose
63
Be MEAN - Node.js
Schema
Tudo no Mongoose comea com o Schema, ele o esqueleto da nossa coleo,
o objeto onde definimos cada campo da coleo com seu tipo, atributos e
validao.
Percebeu como importante?
Bom j que voc percebeu ento vamos ver como criar nosso primeiro Schema,
obviamente voc precisa importar o mdulo do mongoose antes:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/be-mean-instagram');
const Schema = mongoose.Schema;
// Criao do Schema
const pokemonSchema = new Schema({
name: String,
description: String,
type: String,
attack: Number,
defense: Number,
height: Number,
});
// apenas para verificar a criao
console.log(pokemonSchema);
connect
Mongoose
64
Be MEAN - Node.js
Obviamente precisamos conectar no MongoDb antes de fazermos qualquer coisa
com o Mongoose e para isso tambm contamos com eventos para nos ajudar a
gerenciar essa conexo.
Primeiramente passamos a string de conexo para a funo connect :
const dbURI = 'mongodb://localhost/be-mean-instagram';
mongoose.connect(dbURI);
E para colocar a cereja no bolo, vamos fechar a conexo com o MongoDb caso o
processo do Node.js seja finalizado:
process.on('SIGINT', function() {
mongoose.connection.close(function () {
Mongoose
65
Be MEAN - Node.js
Agora juntando tudo isso temos um arquivo de configurao/conexo com o
MongoDb que podde ser re-usado.
const dbURI = 'mongodb://localhost/be-mean-instagram';
mongoose.connect(dbURI);
mongoose.connection.on('connected', function () {
console.log('Mongoose default connection open to ' + dbURI);
});
mongoose.connection.on('error',function (err) {
console.log('Mongoose default connection error: ' + err);
});
mongoose.connection.on('disconnected', function () {
console.log('Mongoose default connection disconnected');
});
mongoose.connection.on('open', function () {
console.log('Mongoose default connection is open');
});
process.on('SIGINT', function() {
mongoose.connection.close(function () {
Schema - continuao
No exemplo anterior criamos o Schema para nossa coleo de Pokemons que
criamos no mdulo de MongoDB, mas podemos melhorar ele deixando o JSON
de configurao do Schema separado da criao.
Mongoose
66
Be MEAN - Node.js
J ficou bem melhor n? Mas e se quisermos ter algum valor por padro?
Para isso usaremos o default !
default
Vamos adicionar o campo created_at no nosso esquema com um valor
padro, mas voc s ver ele em ao quando inserir um objeto novo.
Mongoose
67
Be MEAN - Node.js
Nesse caso quando um objeto for inserido ele colocar o valor da data atual no
campo created_at , o MongoDB ir persistir essa data no formato ISODate.
Calma que j veremos isso em ao antes precisamos aprender mais
algumas coisinhas.
Voc deve ter percebido que o campo created_at um objeto diferente dos
outros, mas por qu?
Basicamente porque quando passamos apenas o nome do tipo estamos
usando apenas o atributo type do objeto de configurao do campo, para
conhecermos mais sobre isso precisamos conhecer primeiramente quais os tipos
suportados pelo Schema.
Tipos
O Mongoose aceita vrios tipos diferentes, que so:
String
Number
Date
Mongoose
68
Be MEAN - Node.js
Buffer
Boolean
Mixed
Objectid
Array
Vamos falar brevemente sobre cada um, os que no tem link porque no tem
na documentao oficial.
Se voc entrar nos links ver que no tem nenhum tipo de explicao adicional
sobre cada tipo, apenas suas funcionalidades, ento vamos ver se eu posso
ajudar nisso.
String
Obviamente o tipo que aceita Strings, nesse caso ele ir converter o tipo
Number para uma String, porm olhe o que acontece quando voc tenta inserir
um valor de um objeto:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/be-mean-instagram');
const Schema = mongoose.Schema;
const _schema = {
name: String
}
// Criao do Schema
const pokemonSchema = new Schema(_schema);
const data = {name: {teste: "Suissa"}}
const Model = mongoose.model('testepokemon', pokemonSchema);
const poke = new Model(data);
poke.save(function (err, data) {
if (err) return console.log('ERRO: ', err);
console.log('Inseriu: ', data)
})
Mongoose
69
Be MEAN - Node.js
Number
O tipo Number aceita tanto nmeros negativos como positivos e tanto interos
como decimais e tambm aceita fraes!
Mongoose
70
Be MEAN - Node.js
Mongoose
71
Be MEAN - Node.js
Date
Armazena datas no formato ISODate, vamos utilizar o cdigo j feito
anteriormente.
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/be-mean-instagram');
const Schema = mongoose.Schema;
const _schema = {
name: String,
description: String,
type: String,
attack: Number,
defense: Number,
height: Number,
created_at: { type: Date, default: Date.now }
}
// Criao do Schema
const pokemonSchema = new Schema(_schema);
// apenas para verificar a criao
console.log(pokemonSchema);
Mongoose
72
Be MEAN - Node.js
Buffer
O tipo Buffer muito para salconst arquivos e retorn-los da forma que
conhecemos no Node.js, porm o MongoDB converte para Binary.
Dica: caso for graconst uma imagem, converta-a para base64.
const imageSchema = new Schema({
mime: String,
bin: Buffer
});
Boolean
O tipo Boolean todo mundo sabe como , correto?
Claro que sabe, se no souber da uma conferida nesse material ultra bsico que
criamos para o JS4Girls.
Ou seja, ele basicamente aceita apenas valores booleanos que podem ser:
true ou 1
Mongoose
73
Be MEAN - Node.js
false ou 0
Por exemplo nesse cdigo:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/be-mean-instagram');
const Schema = mongoose.Schema;
const _schema = {
name: Boolean
}
// Criao do Schema
const pokemonSchema = new Schema(_schema);
const data = {name: 1}
const Model = mongoose.model('testepokemon', pokemonSchema);
const poke = new Model(data);
poke.save(function (err, data) {
if (err) return console.log('ERRO: ', err);
console.log('Inseriu: ', data)
})
Mongoose
74
Be MEAN - Node.js
Mixed
Mongoose
75
Be MEAN - Node.js
Mongoose
76
Be MEAN - Node.js
Mongoose
77
Be MEAN - Node.js
ObjectId
Mongoose
78
Be MEAN - Node.js
E isso ser muito importante por causa de uma coisa chamada: populate.
Mongoose
79
Be MEAN - Node.js
O populate ser o responsvel por fazer a busca pelos _ids especificados no
campo com Schema.Types.ObjectId e como voc deve ter percebido tambm
usamos mais um atributo:
ref: 'testepokemons'
Pois com o valor de ref , que o nome da coleo que possui aquele
documento, que o Mongoose ir fazer a busca nessa coleo, retornando o
resultado j adicionado no objeto de resposta, iremos ver melhor sobre isso
futuramente.
Mas lembre-se:
muito importante!
Array
Obviamente o tipo que aceita apenas array correto?
Mongoose
80
Be MEAN - Node.js
Mongoose
81
Be MEAN - Node.js
Mongoose
82
Be MEAN - Node.js
Executando...
Inseriu: { pokemons: [ 'Pikachu', 'Squirtle' ],
_id: 5691ea660fc87d1317e5d91f,
__v: 0 }
Percebeu ento que apenas mudei para pokemons: [String] pois nesse caso
o campo pokemons ir receber um array de Strings como seria o procedimento
mais natural.
Ento agora voc sabe que o tipo Schema.Types.Array cria um array para
cada elemento contido no campo.
Ento eu aconselho a voc usar a segunda forma que utilizando o Array do
JavaScript mesmo, alis dificilmente voc encontrar cdigos com
Schema.Types.Array mas eu tinha que explicar. :p
__v
Com certeza voc percebeu que quando inserimos algum documento o
Mongoose nos retorna o objeto com um atributo que no inserimos, o _v .
Tudo bem, mas para que serve essa biroska?
Mongoose
83
Be MEAN - Node.js
Esse campo adicionado automaticamente pelo Mongoose quando inserimos
algum documento novo, ele serve para o Mongoose gerenciar, internamente, a
verso de cada documento caso haja alguma alterao concorrente.
Caso necessrio voc pode modificar sua verso manualmente, se desejar
utilizar esse campo como versionador tambm.
Validation
Agora sim chegamos em algo de extrema importncia, a validao dos campos.
Validao padro
Como j vimos anteriormente o Mongoose possui validaes padro para alguns
tipos de campos, alm disso todos os tipos tambm possui a validao de
required .
Mongoose
84
Be MEAN - Node.js
Porm alguns tipos possuem validadores mais especficos como:
Number: possui os validadores de max e min
String: possui os validadores de enum , match , maxlength e
minlength
Acima podermos ver qual foi o erro, porm no fazemos nada com essa
informao, por nossa sorte ela vem separadinha logo abaixo:
{ [ValidationError: testepokemon validation failed]
message: 'testepokemon validation failed',
name: 'ValidationError'
...
}
Mongoose
85
Be MEAN - Node.js
Ento podemos perceber que a mensagem de erro contida em message
composta pelo nome do Model que deu a merda, testepokemon , mais
validation failed e no campo name o nome da validao que deu
Dentro do objeto errors existe um atributo com o nome do campo, que gerou
o erro, e atrelado a ele o seu objeto do erro.
{ name:
{ [CastError: Cast to String failed for value "[object Object]" at path
message: 'Cast to String failed for value "[object Object]" at path "nam
name: 'CastError',
kind: 'String',
value: [Object],
path: 'name',
reason: undefined
}
}
Mongoose
86
Be MEAN - Node.js
{ message: 'Cast to String failed for value "[object Object]" at path "name"
name: 'CastError',
kind: 'String',
value: [Object],
path: 'name',
reason: undefined
}
Mongoose
87
Be MEAN - Node.js
Validao customizada
Para criar uma validao customizada bem simples, basta passar um objeto
para o atributo validate do seu campo, no Schema:
Mongoose
88
Be MEAN - Node.js
age: {
type: Number,
validate: {
validator: function(v) {
return v >= 18;
},
message: 'Sua idade({VALUE}) no permitida!'
}
}
Validadores sempre recebem o valor para validar como seu primeiro argumento e
devem retornar um valor booleano. Retornando false significa que a
validao falhou.
Vamos testar a validao:
const User = mongoose.model('user', userSchema);
const u = new User();
u.age = 24;
console.log(u.validateSync());
u.age = 6;
console.log(u.validateSync().toString());
u.age = 2;
console.log(u.validateSync());
Mongoose
89
Be MEAN - Node.js
Executando essa validao temos:
undefined
ValidationError: Sua idade(6) no permitida!
{ [ValidationError: user validation failed]
message: 'user validation failed',
name: 'ValidationError',
errors:
{ age:
{ [ValidatorError: Sua idade(2) no permitida!]
properties: [Object],
message: 'Sua idade(2) no permitida!',
name: 'ValidatorError',
kind: 'user defined',
path: 'age',
value: 2 } } }
conhecemos.
Agora vamos tentar validateSync().toString() com um valor maior que 18:
u.age = 69;
console.log(u.validateSync().toString());
Mongoose
90
Be MEAN - Node.js
console.log(u.validateSync().toString());
^
TypeError: Cannot read property 'toString' of undefined
at Object.<anonymous> (/Users/jeancarlonascimento/www/projetos/webschool
at Module._compile (module.js:399:26)
at Object.Module._extensions..js (module.js:406:10)
at Module.load (module.js:345:32)
at Function.Module._load (module.js:302:12)
at Function.Module.runMain (module.js:431:10)
at startup (node.js:141:18)
at node.js:977:3
Por isso cuidado ao usar essa funo, tenha certeza que esteja executando
em um erro!
Vamos criar uma validao um pouco mais complexa agora:
Mongoose
91
Be MEAN - Node.js
Alm do match :
Mongoose
92
Be MEAN - Node.js
para o validate :
const RequisitosSchema = new Schema({
name: String
});
const Requisitos = mongoose.model('Requisitos', RequisitosSchema);
Requisitos.schema.path('name').validate(function (value) {
return /js|html|css|angular|node|mongodb/i.test(value);
}, 'Requisito({VALUE}) invlido!');
const req = new Requisitos({ name: 'php'});
console.log(req.validateSync());
Model
O Model a implementao do Schema, sendo o objeto com o qual trabalhamos.
const Model = mongoose.model('Model', schema);
Mongoose
93
Be MEAN - Node.js
const _schema = {
name: String
}
const pokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('Pokemon', pokemonSchema);
const Suissamon = new PokemonModel({ name: 'Suissamon' });
Suissamon.save(function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Inseriu:', data);
});
// ou
Suissamon.create({ name: 'Suissamon' }, function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Inseriu:', data);
});
Create - save()
J vimos em outros exemplos como criar um documento novo, ento agora
vamos padronizar seu uso.
const _schema = {
name: String
}
const pokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('Pokemon', pokemonSchema);
const dataModel = { name: 'Suissamon' };
const Suissamon = new PokemonModel(dataModel);
Suissamon.save(function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Inseriu:', data);
});
Mongoose
94
Be MEAN - Node.js
Iremos sempre separar o JSON com os dados do Model( dataModel ) da sua
criao new PokemonModel(dataModel) para depois executar a funo
save , passando como parmetro uma funo de callback que ir sempre
Retrieve - find()
Existem 2 formas diferentes de executar uma busca com o Mongoose:
via callback com JSON
encadeando funes
Vamos aprender das 2 formas, ento vamos buscar por exemplo o Pikachu
que foi inserido em exerccios anteriores, buscando pelo name e se o attack
for maior que 90 :
const pokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('Pokemon', pokemonSchema);
const query = {name: 'Pikachu', attack: {'$gt': 90}};
PokemonModel.find(query, function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Buscou:', data);
});
Mongoose
95
Be MEAN - Node.js
Caso voc queira limitar quais campos devem ser retornados basta passar como
JSON no segundo parmetro, assim:
const pokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('Pokemon', pokemonSchema);
const query = {name: 'Pikachu', attack: {'$gt': 90}};
const fields = {name: 1};
PokemonModel.find(query, fields, function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Buscou:', data);
});
Para voc fazer uma busca independente de maiscula ou minscula voc deve
usar de RegEx para isso:
Mongoose
96
Be MEAN - Node.js
Voc pode ver todas as funes dessa segunda forma aqui na documentao
oficial.
findOne
Como voc deve lembrar que alm do find tambm temos o findOne que
serve para qu?
findById
O findById equivalente ao findOne({_id: id}) , com valor
findById(undefined) ele converte para findById({ _id: null }) .
Mongoose
97
Be MEAN - Node.js
Update - update()
Para alterarmos um documento iremos seguir a mesma lgica do update do
cliente do MongoDb, porm no Mongoose podemos omitir o operador $set
pois ele no ir sobrescrever todo seu objeto caso no o use, assim como no
cliente.
Lembra-se?
Mongoose
98
Be MEAN - Node.js
findAndModify
const pokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('Pokemon', pokemonSchema);
const query = {name: /pikachu/i};
const mod = {attack: 666};
PokemonModel.findAndModify(query, mod, function (err, data) {
if (err) return console.log('ERRO: ', err);
return console.log('Alterou:', data);
})
findOneAndUpdate
upsert
multi
Delete - remove()
findOneAndRemove
Mongoose
99
Be MEAN - Node.js
Indexes
Quando o aplicativo iniciado, Mongoose chama automaticamente
ensureIndex para cada ndice definido no seu Schema. Mongoose chamar
ensureIndex sequencialmente para cada ndice, e emitem um evento index
Mas ele s impacta quando voc levanta seu sistema, sabendo disso voc no
precisa seguir essa recomendao de desligar o autoIndex , pois ele ir
garantir certa integridade dos seus dados.
Mongoose
100
Be MEAN - Node.js
Mongoose
101
Be MEAN - Node.js
test> db.system.indexes.find()
{
"v": 1,
"key": {
"_id": 1
},
"name": "_id_",
"ns": "test.users"
}
{
"v": 1,
"unique": true,
"key": {
"email": 1
},
"name": "email_1",
"ns": "test.users",
"background": true
}
{
"v": 1,
"key": {
"date_created": 1
},
"name": "date_created_1",
"ns": "test.users",
"background": true
}
Para criar um ndice composto basta faz-lo passando o JSON para a funo
index do Schema:
Mongoose
102
Be MEAN - Node.js
Methods e Statics
No Mongoose podemos definir mtodos especficos para um Schema, como
tambm mtodos "estticos".
Methods
Para se definir um mtodo muito simples, basta criarmos ele no objeto
Schema.methods :
Mongoose
103
Be MEAN - Node.js
const _schema = {
name: String
, description: String
, type: String
, attack: Number
, defense: Number
, height: Number
};
const PokemonSchema = new Schema(_schema);
PokemonSchema.methods.findSimilarType = function findSimilarType (cb
return this.model('Pokemon').find({ type: this.type }, cb);
};
const Pokemon = mongoose.model('Pokemon', PokemonSchema);
const poke = new Pokemon({ name: 'Teste', type: 'inseto' });
poke.findSimilarType(function (err, data) {
if (err) return console.log('Erro:', err);
return data.forEach((pokemon) => console.log('pokemon: ', pokemon));
})
poke
.findSimilarType()
.where('defense').gt(50)
.limit(2)
.exec(function (err, data) {
if (err) return console.log('Erro:', err);
return data.forEach((pokemon) => console.log('pokemon: ', pokemon));
});
Statics
Mongoose
104
Be MEAN - Node.js
Alm dos mtodos normais tambm podemos criar os mtodos estticos, os
quais sempre estaro acessveis no seu Model.
const _schema = {
name: String
, description: String
, type: String
, attack: Number
, defense: Number
, height: Number
};
const PokemonSchema = new Schema(_schema);
PokemonSchema.statics.search = function (name, cb) {
return this.where('name', new RegExp(name, 'i')).exec(cb);
};
const Pokemon = mongoose.model('Pokemon', PokemonSchema);
Pokemon.search('caterpie', function (err, data) {
if (err) return console.log('Erro:', err);
return data.forEach((pokemon) => console.log('pokemon: ', pokemon));
});
Getters e Setters
Getters e setters ajudam a mudar a forma como voc obtm e/ou define os
atributos do documento.
Setters
Mongoose
105
Be MEAN - Node.js
Setters permitem que voc transforme os dados originais antes que cheguem ao
documento.
Suponha que voc est implementando o registro do usurio para um site.
Usurio fornecer um e-mail e senha, que fica guardado no MongoDB. O e-mail
uma seqncia de caracteres que voc vai querer normalizar para minsculas.
Voc pode configurar a normalizao do e-mail para minsculas facilmente
atravs de um setter.
function toLower (v) {
return v.toLowerCase();
}
const UserSchema = new Schema({
email: { type: String, set: toLower }
});
const User = mongoose.model('User', UserSchema);
const user = new User({email: 'SUISSERA@webschool.io'});
console.log(user.email); // 'suissera@webschool.io'
Getters
Getters permitem que voc transforme a representao dos dados, uma vez que
transformado a partir do documento para o valor que voc v.
Suponha que voc queira retornar o ttulo do post todo em maiscula.
Voc pode faz-lo atravs da definio de um getter.
Mongoose
106
Be MEAN - Node.js
Virtuals
O Mongoose suporta atributos virtuais, que so convenientes em alguns
momentos, mas no so armazenados no MongoDB.
Pense no seguinte Schema:
const PersonSchema = new Schema({
name: {
first: String
, last: String
}
});
Mongoose
107
Be MEAN - Node.js
Se voc deseja mostrar os valores dos virtuals no cliente deve setar {
virtuals: true } para toObject e toJSON no Schema, como mostrado
abaixo:
const PersonSchema = new Schema({
name: {
first: String
, last: String
}
}, {
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
});
Mongoose
108
Be MEAN - Node.js
Para fazer isso basta passar 'name.full' para a funo virtual do
Schema:
PersonSchema
.virtual('name.full')
.get(function () {
return this.name.first + ' ' + this.name.last;
});
Retornando:
Nome completo: Jean Suissa
Mongoose
109
Be MEAN - Node.js
PersonSchema
.virtual('name.initials')
.get(function () {
return this.name.first[0] + this.name.last[0];
});
const Person = mongoose.model('Person', PersonSchema);
Person.findById('569e513f7672012c28da89f1', (err, data) => {
if (err) return console.log('Erro:', err);
return console.log('Iniciais: ', data.name.initials);
});
E nossa resposta :
Iniciais: JS
Embedded Documents
Esse tpico muito interessante pois diversas vezes iremos colocar um
documento dentro de outro(embedded). Documentos incorporados desfrutam dos
mesmos recursos que os Models. Sempre que ocorrer um erro ele ir para o
callback do save() .
Adicionando
Vamos iniciar com um exemplo clssico, de Blog:
Mongoose
110
Be MEAN - Node.js
Mongoose
111
Be MEAN - Node.js
Sucesso: { comments:
[ { _id: 569e2ef6e17e3736266c9cd7,
date: Tue Jan 19 2016 10:41:26 GMT-0200 (BRST),
body: 'T comentando meu fiiiii!',
title: 'Comentei aqui' } ],
_id: 569e2ef6e17e3736266c9cd6,
__v: 0 }
Mongoose
112
Be MEAN - Node.js
Sucesso: { comments:
[ { _id: 569e300ad1455a8326c9aa92,
date: Tue Jan 19 2016 10:46:02 GMT-0200 (BRST),
body: 'T comentando meu fiiiii!',
title: 'Outro comentrio' } ],
_id: 569e300ad1455a8326c9aa91,
body: 'Post inicial do blog UEBAAA',
title: 'Primeiro POST',
__v: 0 }
Removendo
Para remover um documento incorporado precisamos primeiramente buscar o
documento "pai", pelo _id de preferncia, para depois selecionar qual
documento interno deve ser removido e depois salconst o documento modificado.
const BlogPostModel = mongoose.model('BlogPost', BlogPostSchema);
const BlogPost = new BlogPostModel(post);
const id = '569e300ad1455a8326c9aa91';
BlogPostModel.findById(id, function (err, post) {
if (err) return console.log('Erro:', err);
console.log('post.comments', post.comments)
post.comments[0].remove();
post.save(function (err, data) {
if (err) return console.log('Erro interno:', err);
return console.log('Sucesso:', data);
});
});
Mongoose
113
Be MEAN - Node.js
Executando, recebemos:
Achou esse comentrio: { title: 'Outro comentrio',
body: 'T comentando meu fiiiii!',
date: Tue Jan 19 2016 11:14:26 GMT-0200 (BRST),
_id: 569e36b2d6a928b526db9136 }
Juntando tudo
Mongoose
114
Be MEAN - Node.js
Vamos agora criar um Schema mais completo e atmico com o conhecimento
adquirido.
J conhecemos o conceito de Arquitetura Atmica para o Mongoose, ento
vamos criar um Schema de usurio, primeiramente da forma simples:
const userSchema = new Schema({
name: String
, password: String
, email: String
, type: String
, created_at: { type: Date, default: Date.now }
});
Antes de tudo vamos criar um projeto novo chamado mongoose-user via npm
init , depois instalando localmente o mongoose vamos copiar a pasta
fields do projeto mongoose-atomic e colar na pasta do projeto mongooseuser , para podermos reaproveitar o cdigo criado anteriormente.
Mongoose
115
Be MEAN - Node.js
Pronto agora atomizamos nossos fields ento est na hora de trabalhar em cada
campo para definir suas peculiaridades, vamos comear pelo name
( fields/field-name ):
module.exports = { type: String }
Mongoose
116
Be MEAN - Node.js
Mongoose
117
Be MEAN - Node.js
Mongoose
118
Be MEAN - Node.js
Tendo retirado o cdigo anterior de app.js logicamente precisamos importar
esse Controller para o app , ficando assim:
'use strict';
const http = require('http');
const Controller = require('./controller');
http.createServer((req, res) => {
let msg = '';
switch(req.url){
case '/api/users/create':
msg = 'USUARIO CADASTRADO';
Controller.create(req, res);
break;
default:
msg = 'ROTA NAO ENCONTRADA';
break;
}
res.end(msg);
}).listen(3000, () => {
console.log('Servidor rodando em localhost:3000');
});
Mongoose
119
Be MEAN - Node.js
Mongoose
120
Be MEAN - Node.js
Se liga s!
Para recebermos dados no nosso servidor ns escutaremos o evento data do
objeto Request( req ), pois pense que voc pode estar enviando um vdeo, logo
no tem como o Node.js ter uma funo para receber o vdeo inteiro, em vez
disso precisamos apenas escutar o evento data at o Request emitir o evento
end .
Sei que no aula sobre http , mas o objeto de Request uma instncia de
http.IncomingMessage. que implementa a interface de Readable Stream e uma
interface de Readable stream uma abstrao para uma fonte de dados que
voc esteja lendo, em outras palavras os dados so lidos em um fluxo legvel,
palavras da prpria documentao. :p
Ou seja, (quase)sempre que voc for ler dados com o Node.js poder utilizar
essa interface para leitura de dados.
Mongoose
121
Be MEAN - Node.js
ps: O Node.js no verifica se o Content-Length e o comprimento do corpo que
tenham sido enviados so iguais ou no.
Vamos voltar para o cdigo, agora que ja o entendemos:
create: (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const obj = querystring.parse(queryData);
User.create(obj, (err, data) => {
if (err) return console.log('Erro:', err);
return console.log('Inserido:', data);
});
});
}
Ento entendemos que, enquanto nosso servidor recebe os dados ele vai
adicionando em queryData para depois esse objeto ser parseado, por
querystring.parse , de string para objeto quando executar o evento end de
Request.
Para depois inserirmos com User.create e PIMBA!
Mongoose
122
Be MEAN - Node.js
Como estamos criando uma API vamos retornar nossa resposta em forma de
JSON, por isso esse trecho:
Mongoose
123
Be MEAN - Node.js
Mongoose
124
Be MEAN - Node.js
Agora vamos fazer a listagem dos usurios, ento seguimos o mesmo padro do
Create, tanto em app.js como em controller.js , logo vamos refatorar o
Model.
retrieve: (req, res) => {
const query = {};
User.find(query, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data));
});
}
No iremos perder mais tempo com isso pois usaremos o Express futuramente.
Ento vamos fazer mais uma funo do CRUD, o Update, para isso iniciamos
adicionando sua rota no app.js :
Mongoose
125
Be MEAN - Node.js
'use strict';
const http = require('http');
const url = require('url');
const Controller = require('./controller');
http.createServer((req, res)=> {
var url_parts = url.parse(req.url);
let msg = '';
switch(url_parts.pathname){
case '/api/users/create':
Controller.create(req, res);
break;
case '/api/users':
Controller.retrieve(req, res);
break;
case '/api/users/update':
Controller.update(req, res);
break;
default:
res.end('ROTA NAO ENCONTRADA');
break;
}
}).listen(3000, ()=> {
console.log('Servidor rodando em localhost:3000');
});
Mongoose
126
Be MEAN - Node.js
Mongoose
127
Be MEAN - Node.js
Mongoose
128
Be MEAN - Node.js
Agora para finalizar o CRUD faremos a funo Delete, iniciando por adicionar sua
rota em app.js :
Mongoose
129
Be MEAN - Node.js
'use strict';
const http = require('http');
const url = require('url');
const Controller = require('./controller');
http.createServer((req, res)=> {
var url_parts = url.parse(req.url);
let msg = '';
switch(url_parts.pathname){
case '/api/users/create':
Controller.create(req, res);
break;
case '/api/users':
Controller.retrieve(req, res);
break;
case '/api/users/update':
Controller.update(req, res);
break;
case '/api/users/delete':
Controller.delete(req, res);
break;
default:
msg = 'ROTA NAO ENCONTRADA';
break;
}
}).listen(3000, () => {
console.log('Servidor rodando em localhost:3000');
});
Mongoose
130
Be MEAN - Node.js
Mongoose
131
Be MEAN - Node.js
Agora para fazer a funo get usando User.findOne ficou bem fcil:
get: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.findOne(query, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data));
});
}
Ento usaremos a funo retrieve para listagem dos usurios e get para
consultar 1 usurio, no esquea de adicionar a rota em app.js e a funo
controller.js .
Mongoose
132
Be MEAN - Node.js
Mongoose
133
Be MEAN - Node.js
const query = querystring.parse(url_parts.query);
User.find(query, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data));
});
}
, get: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.findOne(query, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data));
});
}
, update: (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const mod = querystring.parse(queryData);
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.update(query, mod, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
});
});
}
Mongoose
134
Be MEAN - Node.js
, delete: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.remove(query, (err, data) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
});
}
};
module.exports = CRUD;
Mongoose
135
Be MEAN - Node.js
const url = require('url');
const querystring = require('querystring');
const mongoose = require('mongoose');
const Schema = require('./schema');
const User = mongoose.model('User', Schema);
const callback = (err, data, res) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
};
const CRUD = {
create: (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const obj = querystring.parse(queryData);
User.create(obj, (err, data) => callback(err, data, res));
});
}
, retrieve: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.find(query, (err, data) => callback(err, data, res));
}
, get: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.findOne(query, (err, data) => callback(err, data, res));
}
, update: (req, res) => {
let queryData = '';
Mongoose
136
Be MEAN - Node.js
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const mod = querystring.parse(queryData);
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.update(query, mod, (err, data) => callback(err, data, res));
});
}
, delete: (req, res) => {
const url_parts = url.parse(req.url);
const query = querystring.parse(url_parts.query);
User.remove(query, (err, data) => callback(err, data, res));
}
};
module.exports = CRUD;
Pois para isso nosso callback deveria ter os mesmo parmetros e como nele
que estamos devolvendo a resposta com res.end(JSON.stringify(data))
precisamos ento fazer a chamada da funo para passar o Request como ltimo
parmetro:
(err, data) => callback(err, data, res)
Mongoose
137
Be MEAN - Node.js
Mongoose
138
Be MEAN - Node.js
const obj = querystring.parse(queryData);
User.create(obj, (err, data) => callback(err, data, res));
});
}
, retrieve: (req, res) => {
const query = getQuery(req);
User.find(query, (err, data) => callback(err, data, res));
}
, get: (req, res) => {
const query = getQuery(req);
User.findOne(query, (err, data) => callback(err, data, res));
}
, update: (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const query = getQuery(req);
const mod = querystring.parse(queryData);
User.update(query, mod, (err, data) => callback(err, data, res));
});
}
, delete: (req, res) => {
const query = getQuery(req);
User.remove(query, (err, data) => callback(err, data, res));
}
};
module.exports = CRUD;
Mongoose
139
Be MEAN - Node.js
Mais refatorao
Dessa vez iremos refatorar algo muito importante em uma API REST, a
aceitao de diferentes verbos do HTTP na mesma rota, ento vamos comear
refatorando a rota /api/users para receber os verbos:
GET
POST
PUT
DELETE
Mongoose
140
Be MEAN - Node.js
Em app.js :
Mongoose
141
Be MEAN - Node.js
'use strict';
const http = require('http');
const url = require('url');
const Controller = require('./controller-teste');
http.createServer((req, res) => {
var url_parts = url.parse(req.url);
switch (url_parts.pathname) {
case '/api/users':
switch (req.method.toLowerCase()) {
case 'get':
Controller.find(req, res);
break;
case 'post':
Controller.create(req, res);
break;
case 'put':
Controller.update(req, res);
break;
case 'delete':
Controller.remove(req, res);
break;
}
break;
case '/api/users/get':
Controller.findOne(req, res);
break;
default:
res.end('ROTA NAO ENCONTRADA');
break;
}
}).listen(3000, () => {
console.log('Servidor rodando em localhost:3000');
});
Em controller.js :
Mongoose
142
Be MEAN - Node.js
Mongoose
143
Be MEAN - Node.js
const obj = querystring.parse(queryData);
User.create(obj, (err, data) => callback(err, data, res));
});
};
const find = (req, res) => {
const query = getQuery(req);
User.find(query, (err, data) => callback(err, data, res));
};
const findOne = (req, res) => {
const query = getQuery(req);
User.findOne(query, (err, data) => callback(err, data, res));
};
const update = (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const query = getQuery(req);
const mod = querystring.parse(queryData);
User.update(query, mod, (err, data) => callback(err, data, res));
});
};
const remove = (req, res) => {
const query = getQuery(req);
User.remove(query, (err, data) => callback(err, data, res));
};
const CRUD = {
create
Mongoose
144
Be MEAN - Node.js
, find
, findOne
, update
, remove
};
module.exports = CRUD;
Atomic Design
Essa estrutura que eu utilizo baseada no Atomic Design que utilizo no front-end,
porm eu modifiquei um pouco essa metodologia para adicionar a parte de
Comportamento para que eu pudesse extender ela com novas funcionalidades.
Essa palestra est gravada aqui na InfoQ.
Nesse caso cada tomo possuir um comportamento padro que pode ser
sobrescrito quando adicionado em uma molcula, tambm podendo mudar
quando adicionado em um organismo.
Ento vamos entender quais so suas partes.
tomo
O tomo a menor parte indivisvel do Mongoose.
Sabe qual ?
Mongoose
145
Be MEAN - Node.js
Quarks
Mongoose
146
Be MEAN - Node.js
Levando isso em considerao podemos dizer que as partes que formam nosso
tomo so os quarks:
type
get
set
validate
required
index
Vamos refatorar o cdigo, para reorganizar e refatorar o validate para objeto:
// quarks
const quark_get = (v) => v.toUpperCase();
const quark_set = (v) => v.toLowerCase();
const quark_validate = {
validator: (v) => v >= 3
, message: 'Nome {VALUE} precisa ser maior que 3 caracteres'
};
const Atom = {
type: String
, get: quark_get
, set: quark_set
, validate: quark_validate
, required: true
, index: true
}
module.exports = Atom;
Mongoose
147
Be MEAN - Node.js
// quark-toLower.js
module.exports = (v) => v.toLowerCase();
// quark-validate-string-lengthGTE3
module.exports = {
validator: (v) => v >= 18
, message: 'Nome {VALUE} precisa ser maior que 3 caracteres'
};
Molcula
Sabendo que o Field o tomo logicamente a Molcula ser o Schema, ento
vamos utilizar o seguinte Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Molecule = {
name: require('./fields/field-name')
}
module.exports = new Schema(Molecule);
Mongoose
148
Be MEAN - Node.js
Organismo
Para finalizar esse conceito o o Organismo ser o Model:
const url = require('url');
const querystring = require('querystring');
const Schema = require('./schema');
const User = mongoose.model('User', Schema);
const callback = (err, data, res) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
};
const getQuery = (_url) => {
const url_parts = url.parse(_url);
return querystring.parse(url_parts.query);
};
const create = (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const obj = querystring.parse(queryData);
User.create(obj, (err, data) => callback(err, data, res));
});
};
const find = (req, res) => {
const query = getQuery(req.url);
User.find(query, (err, data) => callback(err, data, res));
};
const findOne = (req, res) => {
Mongoose
149
Be MEAN - Node.js
const query = getQuery(req.url);
User.findOne(query, (err, data) => callback(err, data, res));
};
const update = (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const mod = querystring.parse(queryData);
const query = getQuery(req.url);
User.update(query, mod, (err, data) => callback(err, data, res));
});
};
const remove = (req, res) => {
const query = getQuery(req.url);
User.remove(query, (err, data) => callback(err, data, res));
};
const CRUD = {
create
, find
, findOne
, update
, remove
};
module.exports = CRUD;
Mongoose
150
Be MEAN - Node.js
'use strict';
const mongoose = require('mongoose');
const url = require('url');
const querystring = require('querystring');
const Schema = require('./schema');
const User = mongoose.model('User', Schema);
const callback = (err, data, res) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
};
const getQuery = (_url) => {
const url_parts = url.parse(_url);
return querystring.parse(url_parts.query);
};
const create = require('./actions/action-create');
const find = require('./actions/action-find');
const findOne = require('./actions/action-findOne');
const update = require('./actions/action-update');
const remove = require('./actions/action-remove');
const CRUD = {
create
, find
, findOne
, update
, remove
};
module.exports = CRUD;
Mongoose
151
Be MEAN - Node.js
// action-get-query-http.js
module.exports = (_url) => {
return require('querystring').parse(require('url').parse(_url).query);
};
// action-response-200-json.js
module.exports = (err, data, res) => {
if (err) return console.log('Erro:', err);
res.writeHead(200, {'Content-Type': 'application/json'});
return res.end(JSON.stringify(data));
};
Mongoose
152
Be MEAN - Node.js
// action-find.js
const callback = require('./action-response-200-json');
const getQuery = require('./action-get-query-http');
module.exports = (Model) => {
return (req, res) => {
const query = getQuery(req.url);
Model.find(query, (err, data) => callback(err, data, res));
};
};
// action-findOne
const callback = require('./action-response-200-json');
const getQuery = require('./action-get-query-http');
module.exports = (Model) => {
return (req, res) => {
const query = getQuery(req.url);
Model.findOne(query, (err, data) => callback(err, data, res));
};
};
Mongoose
153
Be MEAN - Node.js
// action-update
const callback = require('./action-response-200-json');
const getQuery = require('./action-get-query-http');
module.exports = (Model) => {
return (req, res) => {
let queryData = '';
req.on('data', (data) => {
queryData += data;
});
req.on('end', () => {
const mod = require('querystring').parse(queryData);
const query = getQuery(req.url);
Model.update(query, mod, (err, data) => callback(err, data, res));
});
};
};
// action-remove
const callback = require('./action-response-200-json');
const getQuery = require('./action-get-query-http');
module.exports = (Model) => {
return (req, res) => {
const query = getQuery(req.url);
User.remove(query, (err, data) => callback(err, data, res));
};
};
Mongoose
154
Be MEAN - Node.js
require('./db/config');
const mongoose = require('mongoose');
const Schema = require('./schema');
const Model = mongoose.model('User', Schema);
// Precisa passar o Model para as aes
const create = require('./actions/action-create')(Model);
const find = require('./actions/action-find')(Model);
const findOne = require('./actions/action-findOne')(Model);
const update = require('./actions/action-update')(Model);
const remove = require('./actions/action-remove')(Model);
const CRUD = {
create
, find
, findOne
, update
, remove
};
module.exports = CRUD;
Mongoose
155
Be MEAN - Node.js
'use strict';
const Doador = require('./../Organisms/doadorOrganism');
const expect = require('chai').expect;
describe('Setter Schema doadorSchema', () => {
// body...
describe('setter to uppercase', () => {
// body...
it('primeiro nome ONLY lower case save in mongo', () => {
const d = new Doador.Organism();
const fieldTestName = 'primeiroNome';
const fieldTestValue = 'ERNI';
d[fieldTestName] = fieldTestValue;
d.save((Doador) => {
expect(Doador[fieldTestName]).to.be.equal('erni');
});
});
});
});
Mongoose
156
Be MEAN - Node.js
Events
Muitos objetos do Node.js so capazes de emitir eventos, ou seja, eles so
Eventemitters. Um exemplo o net.Server, ele pode emitir eventos todas as
vezes em que recebe uma nova requisio ou quando termina o processo de
uma, o fs.Stream emite eventos toda vez que trabalha com um arquivo, todos
objetos que emitem eventos no Node.js so instncias de events.EventEmitter,
esse mdulo pode ser acessado atravs do mdulo require(events).
Traduo Livre from
(https://nodejs.org/docs/latest/api/events.html#events_events).
Eventos podem ser nominados de acordo com a necessidade de cada funo,
contudo eles seguem um padro para emisso (emit) e escuta (listener) de
eventos, quanto a transmiso de eventos chama-se a funo
obj.emit(event:name, action), para ouvir um evento chama-se a funo
obj.on(event:name, action). Esse padro se repete em vrios locais do mundo
javascript, inclusive em frameworks de frontend, como o angularJS.
Vamos ver o primeiro exemplo mais bsico de um EventEmiter, onde o primeiro
passo ser criar um arquivo.js e requerir o mdulo events. O Cdigo 01 um
mdulo simples que escuta dois eventos, o primeiro evento registrado o
time:event e o segundo mod:three.
Cdigo 01
Eventos
157
Be MEAN - Node.js
'use strict';
const events = require('events');
const em = new events.EventEmitter();
em.on("time:event", timeEvent);
em.on("mod:three", mod3Event);
function timeEvent(interval) {
console.log('timeEvent '+interval);
}
function mod3Event(mod3) {
console.log('3 mod %s === 0 ',mod3);
}
module.exports = em;
Cdigo 02
Eventos
158
Be MEAN - Node.js
'use strict';
const em = require('./events');
setInterval(( function() {
let i = 1;
return function () {
if(i % 3 === 0) {
em.emit('mod:three',i++);
} else {
em.emit('time:event', i++);
}
};
})(),1000);
Esse cdigo muito simples, feito apenas para fins demonstrativos, sobre a
criao bsica de um EventEmitter. O prximo exemplo um caso de uso mais
efetivo, onde ser extendido o comportamento do EventEmiter para um mdulo
criado pelo desenvolvedor, ou seja, assim como o prprio Node.js usa essa boa
prtica de emitir eventos em vrias classes internas, ns desenvolvedores
podemos extender esse comportamento, atravs de herana.
Ser usado o mdulo require(util), presente no core do Node.js, para nos ajudar
a fazer a herana, e transformar um simples mdulo chamado User, em um
EventEmiter. O cdigo 03 d incio ao mdulo.
Cdigo 03
Eventos
159
Be MEAN - Node.js
'use strict';
const EventEmitter = require('events').EventEmitter;
const util = require('util');
function User () {
EventEmitter.call(this);
}
util.inherits(User, EventEmitter);
module.exports = User;
Ok, essa uma forma bsica para criar um mdulo, que pode emitir e escutar
eventos, pois bem agora esse mdulo ter um comportamento simples, que
receber dados de um usurio, e ao chamar a sua funo save(), ele emitir um
evento(save:user, sendEmail) caso tudo corra bem, esse evento chamar em
seu callback uma funo que simula o envio de um email, caso contrrio emitir
um erro(error,err), que em seu callback chamar uma funo que levantar um
erro. Vejamos agora o cdigo 04.
Cdigo 04
use strict;
const EventEmitter = require('events').EventEmitter;
const util = require('util');
function User (data) {
this.data = data;
this.on('user:save', sendMail);
this.on('error', sendError);
EventEmitter.call(this);
}
User.prototype.save = function () {
Eventos
160
Be MEAN - Node.js
if(this.data.name){
this.emit('user:save',this.data);
}
else {
this.emit('error', new TypeError('User need an name'));
}
};
util.inherits(User, EventEmitter);
function sendMail(user) {
user.pass = Math.floor(Math.random() * (1000000 - 900000)) + 900000
util.log(`\n
\tOla ${user.name}!
\tbem vindo seu pass ${user.pass}
\tvoc tem 24 horas para muda-lo
\tou tera que pedir reenvio\n`
);
}
function sendError(err) {
throw err;
}
module.exports = User;
O mdulo User, possui algumas particularidades que devem ser explicadas, como
o uso do mdulo util, com ele pode-se fazer muitas coisas legais como herana,
inspeo de objetos, logs entre outros. Vale a pena verificar sua documentao
em: https://nodejs.org/api/util.html
Essa foi uma breve intruduo a eventos no nodeJS. Agora iremos aprender a
usar eventos nos models do mongoose, pois todo model do mongoose um
evento Emitter, ele possui alguns eventos padres, muito teis para melhorar a
arquitetura de um sistema.
Conhecendo o evento pre e post do mongoose, veja um model no cdigo 05.
Esse model pode executar tarefas antes e/ou quando for executar alguma funo,
como: save, create, find ou ou qualquer funo interna.
Eventos
161
Be MEAN - Node.js
Cdigo 05
'use strict';
const mongoose = require('mongoose');
const util = require('util');
function pokemonHandler () {
let Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
const schema = new Schema({
id : ObjectId,
name : {type : String, trin : true},
type : {type : String, trin : true},
attack : {type : Number},
defence : {type : Number},
height : {type : Number},
description : {type : String, trin : true}
});
schema.pre('find',function (next) {
this.start = Date.now();
util.log("finding ...");
next();
});
schema.post('find', function(result) {
setTimeout(function(){
console.log('finding end :P')
},1000);
});
return mongoose.model('Pokemon', schema);
}
module.exports = exports = pokemonHandler();
Eventos
162
Be MEAN - Node.js
seja executada como o esperado, ou seja, o pre KKKK ser chamado antes do
save, ok?
O evento post muito sugestivo, pois possui exatamente o comportamento de
execuo posterior, na sua chamada no existe necessidade de usar next()
lembre-se disso. No exemplo do model no cdigo 05, ele ser executado 1
segundo aps a execuo da funo find, ou seja, essas duas funes pre e
post so muito teis e devem ser usadas sempre que necessrio.
Eventos
163
Be MEAN - Node.js
Promise no Node.js
O que ? Promise??? uma abstrao para trabalhar com cdigo assncrono de
forma elegante, organizada e simplificada. Existem pessoas crentes que o
Node.js perdeu a chance de usar toda sua api baseada em Promises, contudo
por questes de performance, o callback foi mantido e no apenas isso, outras
abstraes podem ser usadas sobre os callbacks. Para saber do que se trata a
fundo uma Promise, leia a proposta com base de uma expecificao nesse link:
Promise/A+ https://promisesaplus.com/. Nele so abordados detalhes no
cobertos nessa apostila.
O Callback um grande problema no quesito de manuteno de cdigo, e eu
particularmente no gosto de mostrar para meus amigos cdigos com callbacks,
ainda mais se tiver muitos, com Promise no tenho essa preocupao.
Existe uma abstrao para trabalhar com cdigo assncrono em C++ e Java, que
a future, quem conhece essa abstrao entende Promise rapidamente.
Uma Promise composta por trs estados bsicos: pendente: quando ainda est
executando. realizada / fulfilled: quando ela termina e tem um resultado de
sucesso. rejeitada / reject: quando termina e tem algum erro;
O NodeJS desde a verso 0.11.x j possuia a possibilidade de trabalhar com
Promise, usando flag, contudo na verso 0.12.7 ela foi adicionada nativamente,
passando a ser usada diretamente e melhor ainda, como um objeto global na
plataforma, o global.Promise.
Vamos ver um exemplo? Primeiramente, vamos ver como ler um arquivo JSON
usando o mdulo de FS, com a funo readFile sem uso de Promise, no cdigo
01.
Cdigo 01
Promises
164
Be MEAN - Node.js
'use strict';
const fs = require('fs');
fs.readFile('./persons.json','utf-8',function(err, file){
if(!err) console.log(file);
});
Promises
165
Be MEAN - Node.js
'use strict';
const fs = require('fs');
//lendo primeiro arquivo
fs.readFile('./persons.json','utf-8', function(err, persons){
//array para juntar todos
let todos = [];
if(!err){
fs.readFile('./friends.json','utf-8', function(err, friends
if(!err){
//juntando arquivos
todos.push(JSON.parse(persons));
todos.push(JSON.parse(friends));
}
//fazendo uma operao com eles
sendFiles(todos);
});
}
});
function sendFiles(files){
//mapeando os arquivos
var arr = files.map(function(person) {
return person.concat(person)
});
//lendo o resultado
console.log(arr[0]);
}
Promises
166
Be MEAN - Node.js
ao callback hell, vamos criar uma funo genrica que recebe um
PATH/caminho de um arquivo e de forma assncrona nos retorna uma Promise, o
cdigo 03 faz exatamente isso.
Cdigo 03
'use strict';
const fs = require('fs');
function readFile (path) {
return new Promise(function(resolve, reject) {
fs.readFile(path,'utf8',function(err, res) {
err ? reject(err) : resolve(res);
});
});
}
module.exports = readFile;
Promises
167
Be MEAN - Node.js
'use strict';
const readFile = require('./fs-promise');
readFile('./persons.json')
.then(function(data) {
success(data);
})
.catch(function(err){
error(err);
});
readFile('./perso.json')
.then(success , error);
function success (data) {
console.log(data);
}
function error (err) {
console.error(err);
}
Promises
168
Be MEAN - Node.js
Promise.all([
readFile('./persons.json'),
readFile('./friends.json')
])
.then(function(result) {
console.log(result);
})
.catch(function(err){
console.log(err);
});
Promise Mongoose
Nos models do mongoose assim como temos Eventemitters por padro, podemos
tambm trabalhar com Promise, que ajuda muito a deixar o cdigo simples de
manter e testar. Vamos usar como base o model de pokemons criando na parte
de events, para entendermos como funciona. Em caso de dvida, a
documentao do mongoose possui exemplos bsicos de como usar Promise ao
invs de callbacks. http://mongoosejs.com/docs/promises.html
Cdigo 01
Promises
169
Be MEAN - Node.js
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/yourdb');
const Pokemon = require('./models/pokemon');
const pokemon = {
name : "Pompeu Limp",
type : "Fire",
attack : 81,
defence : 65,
height : 1.82,
description : "jiujitero"
};
Pokemon.create(pokemon).then(success , error);
function success(data) {
console.log(data);
}
function error (err) {
console.log(err);
}
Promises
170
Be MEAN - Node.js
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/pompeuapi');
const Pokemon = require('./models/pokemon');
let promise = Pokemon.find({}).exec();
promise.then(success, error);
Cdigo 03
'use strict';
'
```js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/pompeuapi');
const Pokemon = require('./models/pokemon');
let promise = Pokemon.findOne({ _id : '5666fd32ff4ea39e23e1528f' }).exec();
promise.then(success , error);
Agora que j sabemos como fazer find e findOne, percebam que nesses casos
foi chamada a funo exec() no final, para transformar minha busca em Promise,
que a nica diferena do create, agora verificaremos o update e delete nos
cdigos 04 e 05.
Cdigo 04
Promises
171
Be MEAN - Node.js
const pokemon = {
name : "Pompeu Limp",
type : "Fire",
attack : 99,
defence : 99,
height : 1.82,
description : "jiujitero"
};
let promise = Pokemon
.update({ _id : '5666ff2a9fa2a10c25d57ef7'},pokemon).exec();
promise.then(success , error);
Cdigo 05
Promises
172
Be MEAN - Node.js
Promise internamente. Em um futuro prximo (2016) o trabalho com cdigo
assncrono ser ridiculamente simples com a implementao da especificao do
novo ecma 2016.
Promises
173