Node.js é meu mais novo framework gratuito favorito para criar meus empreendimentos digitais. Não apenas porque eu já tenho alguns anos com experiência em Javascript e poder utilizar ele no frontend e no backend é bem tentador. Não apenas porque ele roda sobre Linux o que reduz o meu custo com licenciamento de servidor para zero. Não apenas porque ele é muito mais leve do que .NET, a plataforma que comumente eu utilizava para todos os meus projetos, permitindo o dobro de usuários com os mesmos recursos.
É por tudo isso e muito mais.
Não me entenda mal, eu adoro .NET e especificamente o C#. Ainda uso bastante C# para construção de sistemas. Mas coisas como reduzir custos de servidor de U$120 para U$15 me chama muito a atenção nessa nova plataforma…
Nesse post vou lhe ensinar como criar uma API usando Node.js, MongoDB, Express e Mongoose. Não sou nenhum especialista no assunto, mas tenho a experiência de alguns projetos rodando com essa configuração como o BuildIn e o SóFamosos. Em breve o Busca Acelerada deve ser migrado para esta plataforma também. Se preferir, tenho um outro que ensina como fazer uma API Node.js com MySQL.
Se você não conhece nada de Node.js, recomendo dar uma olhada nesse post primeiro, que vai lhe ajudar com os passos iniciais, que eu não serei tão didático aqui com a primeira parte. Se não conhece o protocolo HTTP, essencial para desenvolvimento web, leia este post. Já se você não gosta de ORMs e quiser aprender a mexer com o MongoDB nativamente, use este post primeiro.
Neste tutorial veremos:
- Configurando o projeto
- Configurando o banco
- Configurando o Mongoose
- GET de um cliente (consulta)
- POST de um cliente (cadastro)
- PUT de um cliente (edição)
- DELETE de um cliente (exclusão)
Querendo algo mais “pesado” de MongoDB, sugiro este post aqui, focado nesta tecnologia de banco de dados.
Parte 1: Configurando o projeto
Primeiro, caso ainda não tenha feito, baixe e instale o NodeJS no site oficial.
Segundo, crie uma pasta para seus projetos node. Abra o prompt de comando e acesse o diretório dos seus projetos Nodes, criado logo antes. Rode o comando abaixo para criar um projeto Express dentro dela, com o nome de apitest:
1 2 3 |
C:node>express -e --git apitest |
Isso irá criar toda a estrutura básica de uma aplicação Express, incluindo rotas default, o arquivo app.js com o coração da aplicação, usando a view engine EJS e com suporte a Git (opcional).
Terceiro, rode o comando abaixo para adicionar algumas dependências novas no package.json:
1 2 3 |
C:nodeapitest> npm install -S mongodb mongoose |
Agora que suas dependências estão prontas, rode o comando abaixo para instalar todas elas no seu projeto:
1 2 3 |
C:nodeapitest>npm install |
Quarto passo, sua aplicação Express deve estar funcionando agora, rode o comando abaixo para executá-la e se certificar que fez tudo correto até aqui.
1 2 3 |
C:nodeapitest>npm start |
Acesse localhost:3000 no navegador e verá se a index aparece corretamente.
Voilá, funcionando!
Parte 2: Configurando o banco
Caso você já tenha um banco MongoDB pronto, ignore essa parte e pule para a parte 3.
Por que MongoDB? Porque é um dos bancos com maior fit com o NodeJS. Dependendo da sua demanda, outros NoSQL como Influx, Rethink e Redis podem lhe ser bem úteis. Outras alternativas são os bancos relacionais mais tradicionais, como o MySQL.
Vou usar linha de comando aqui. Caso prefira uma ferramenta visual, sugiro o MongoDB Compass que são gratuitos e eu uso bastante e mostro no vídeo abaixo.
Primeiro passo, se ainda não tem o MongoDB na sua máquina, baixe-o no site oficial. Instale/extraia os arquivos para seu C:MongoDB ou qualquer caminho que preferir. Mostro o passo a passo no vídeo abaixo.
Segundo passo, primeiro crie uma pasta data dentro da pasta da sua api. Depois abra o prompt de comando e navegue até a pasta do seu MongoDB, dentro dela terá uma pasta server/versão/bin e dentro dela você executará a seguinte linha de comando, que iniciará o servidor Mongo dentro da API:
1 2 3 |
mongod --dbpath c:nodeapitestdata |
Se aparecer a mensagem de “waiting connections”, está tudo ok.
Terceiro passo, vamos adicionar alguns registros em nosso banco para não iniciar com ele zerado. Neste exemplo irei inserir alguns clientes no banco, que sempre é um bom exemplo comercial. Para isso, abra outro prompt de comando e navegue até a pasta do seu MongoDB, dentro dela terá uma pasta server/versão/bin e dentro dela você executará a seguinte linha de comando, que iniciará o cliente Mongo:
1 2 3 |
mongo |
Uma vez com o cliente aberto, digite o seguinte comando para se conectar no nosso banco da API:
1 2 3 |
use apitest |
E na sequência vamos inserir um array de registros de clientes:
1 2 3 4 |
custArray = [{ "name" : "customer1", "email" : "[email protected]" }, { "name" : "customer2", "email" : "[email protected]" }] db.customers.insert(custArray); |
Se quiser testar pra ver se funcionou, basta dar um db.customers.find().pretty() e serão listados todos os clientes cadastrados no banco.
Com isso temos todo o ambiente pronto e configurado, agora vamos programar!
Parte 3: Configurando o Mongoose
Alguns tutoriais na Internet ensinam usando o ORM Monk. O Monk é legal, super fácil de utilizar e tenho grande apreço por ele, mas…ele é uma m**** quando o assunto é desempenho (50% a menos de desempenho nos testes que fiz). Por isso, vamos usar o Mongoose aqui, que é a solução de ORM mais profissional para usar MongoDB com NodeJ.js.
Primeiro passo, crie um arquivo db.js na raiz do seu projeto apitest. Dentro dele, cole o seguinte código, que explicarei na sequência:
1 2 3 4 5 6 7 8 9 10 11 12 |
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/apitest'); var customerSchema = new mongoose.Schema({ name: String, email: String }, { collection: 'customers' } ); module.exports = { Mongoose: mongoose, CustomerSchema: customerSchema } |
Aqui eu criei um objeto Mongoose, fiz a conexão com nosso banco que está rodando local e defini o schema da coleção de clientes no banco de dados, usando o mesmo nome que já tinha usado na parte 2 (customers). Por fim, exportei um objeto contendo o Mongoose e o schema, para uso posterior.
Segundo passo, vamos testar. Para fazer isso, vamos fazer o método mais elementar e básico da API: o GET por todos os clientes da base. Mais pra frente implementaremos elementos importantes como paginação, mas por ora, vamos retornar todos. Abra seu arquivo index.js dentro da pasta routes do seu projeto. Coloque o seguinte código logo acima do module.exports:
1 2 3 4 5 6 7 8 9 10 11 |
/* GET all customers. */ router.get('/customers', function (req, res, next) { var db = require('../db'); var Customer = db.Mongoose.model('customers', db.CustomerSchema, 'customers'); Customer.find({}).lean().exec(function(e,docs){ res.json(docs); res.end(); }); }); |
Esse código basicamente pega todas requisições GET para a URL localhost:3000/customers e retorna todos os clientes cadastrados no banco no formato JSON, diretamente no corpo da resposta, que é o esperado para uma API. Salve esse arquivo e reinicie o servidor NodeJS, para que as alterações surtam efeito.
Terceiro passo, vamos pro navegador testar: acesse localhost:3000/customers e você deve ver o seguinte resultado:
Aqui eu cortei parte da resposta, mas é apenas um array JSON com os elementos existentes no banco de dados. Isso mostra que tudo está funcionando por enquanto.
Com esse conhecimento você já é capaz de entender como tudo funcionará daqui pra frente, adicionando verbos HTTP ao endpoint customers e executando as consultas e comandos apropriados no banco de dados e retornando JSON.
Caso já queira colocar isso que você fez em um servidor Windows, sugiro ler este post aqui. Se quiser hospedar na Umbler (você vai ter de criar o banco MongoDB lá também), pode fazê-lo via Git com instruções dentro do painel da Umbler, após criar o site Node.
Parte 4: GET de um cliente
Agora vamos alterar nosso código para retornar apenas um cliente, algo bem comum em APIs, uma vez que nem sempre você vai querer retornar todos os elementos de uma coleção do banco.
Para isso, basta criarmos uma nova rota /customers para também aceitar um ID logo após seu path, algo como /customers/123-abc-456-def (lembrando que no MongoDB usamos guids alfanuméricos). O código abaixo mostra como seria essa nova rota:
1 2 3 4 5 6 7 8 9 10 11 |
/* GET ONE customers. */ router.get('/customers/:id', function (req, res, next) { var db = require('../db'); var Customer = db.Mongoose.model('customers', db.CustomerSchema, 'customers'); Customer.find({ _id: req.params.id }).lean().exec(function (e, docs) { res.json(docs); res.end(); }); }); |
Note o uso de :id indicando que o parâmetro logo após /customers será o ID do mesmo. Note também o uso de um filtro em nosso find, baseado no _id do documento.
Para conseguir testar, primeiro você terá de descobrir o _id de um cliente que já esteja no banco. Para isso, use o Studio 3T (antigo Mongo Chef) ou a própria linha de comando “mongo”, que fica dentro da pasta bin do MongoDB, executando um db.customers.find().pretty() e copiando o _id de qualquer um. Caso você não saiba como copiar texto do console, basta clicar com o botão direito, escolher Mark, selecionar o texto que quer e clicar com o botão direito do mouse sobre ele. Pronto está copiado, basta colar onde quiser.
Salve o arquivo, reinicie seu servidor Node e o resultado de uma pesquisa com ID é o abaixo:
Nem perca tempo querendo copiar o ID do meu cliente, ele nunca se repete.
Parte 5: POST de um cliente
O próximo passo é criarmos uma rota que permita adicionar novos clientes em nossa coleção. Segundo a especificação HTTP, o verbo POST é usado quando queremos adicionar novos elementos, então é isso que faremos, uma rota que manipule o POST em nossa API, como no exemplo abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* POST ONE customer. */ router.post('/customers/', function (req, res, next) { var db = require('../db'); var Customer = db.Mongoose.model('customers', db.CustomerSchema, 'customers'); var newcustomer = new Customer({ name: req.body.name, email: req.body.email }); newcustomer.save(function (err) { if (err) { res.status(500).json({ error: err.message }); res.end(); return; } res.json(newcustomer); res.end(); }); }); |
Note que este código já começa diferente dos demais, com router.post ao invés de router.get, indicando que está rota tratará POSTs no endpoint /customers. Na sequência, carregamos o objeto db para pegar o model customers do Mongoose, criarmos um novo customer com o name e o email que vieram no body da requisição (incluindo aqui requisições com body em JSON) e depois mandamos ele salvar no banco.
Nosso save também está um pouco diferente do exemplo do post de introdução, pois aqui estamos tratando o caso de dar algum erro. Neste caso enviamos um código HTTP 500 ao consumidor da API, com um JSON contendo a mensagem de erro. De outra maneira, em caso de sucesso, devolvemos o objeto customer que acabou de ser criado, incluindo o _id que recebeu.
Salve seu arquivo e reinicie o servidor Node. Agora vamos testar!
Mas como testar um POST se no navegador só conseguimos fazer GETs?
Usando uma ferramenta muito bacana chamada POSTMAN, que usaremos não apenas para esta parte do tutorial, mas para outras também. Baixe e instale o POSTMAN no seu Google Chrome e execute-o, ele é apenas um forjador de requisições bem útil (outra opção é o Fiddler). Você verá a tela abaixo:
Note que no primeiro select eu escolhi o verbo POST e na caixa de endereço digitei localhost:3000/customers, que é o endpoint da nossa API. Depois temos as abas Authorization (para adicionarmos autenticação futuramente), Headers e Body.
Em Headers, adicione a chave-valor Content-type (chave) com “application/json” (sem aspas, valor), indicando que vamos POSTar JSON na API. Já em Body, marque a opção raw (crú) e digite um objeto JSON que represente os dados de um customer no campo logo abaixo. Segue um exemplo para você copiar e colar:
1 2 3 |
Agora sim podemos testar!
Considerando que você já esteja com seu servidor atualizado e rodando, clique no enorme botão Send do POSTMAN e sua requisição será enviada. Quando isso acontece, o POSTMAN exibe em uma área logo abaixo da requisição, a resposta (response) da requisição, como abaixo:
Note que é mostrado o corpo da resposta (nesse caso o customer que enviamos, incluindo o _id) e o status da mesma (nesse caso um 200 OK).
Mas será que funcionou mesmo?
Basta digitarmos localhost:3000/customers em nosso navegador (ou construir uma requisição GET no POSTMAN) e veremos que nosso novo customer está lá!
Parte 6: PUT de um cliente
Agora que já podemos consultar todos clientes, consultar um cliente e salvar um novo cliente, é hora de permitirmos que os usuários de nossa API atualizem os dados dos clientes.
A especificação para APIs HTTP REST define que nosso endpoint deve esperar receber o id do objeto que queremos atualizar na URL, no formato /customers/id, enquanto que no corpo da requisição devemos enviar o novo objeto que vai substituir o original na coleção.
Sendo assim, vamos configurar mais uma rota em nosso arquivo /routes/index.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* PUT ONE customer. */ router.put('/customers/:id', function (req, res, next) { var db = require('../db'); var Customer = db.Mongoose.model('customers', db.CustomerSchema, 'customers'); Customer.findOneAndUpdate({ _id: req.params.id }, req.body, { upsert: true }, function (err, doc) { if (err) { res.status(500).json({ error: err.message }); res.end(); return; } res.json(req.body); res.end(); }); }); |
Neste exemplo estamos configurando uma rota PUT para /customers/id. O processo inicial do algoritmo não é muito diferente dos demais, onde carregamos o objeto db, o model Customer e com ele chamamos o método findOneAndUpdate que, baseado na query {_id: req.params.id} irá alterar o objeto com o req.body passado (que é o JSON que enviaremos na requisição PUT com os valores atualizados do customer).
O próximo parâmetro, upsert:true, indica que se o customer não existir, ele será criado. Fica a seu critério manter esta configuração assim ou não. Já a nossa função de callback é a de sempre: retornará o erro em caso de falha ou o objeto JSON que foi salvo se tudo der certo.
Salve e reinicie o seu servidor Node para podermos testar.
Para testar, primeiro pegue o _id de algum customer que irá ser atualizado. Você pode fazer isso fazendo um GET em todos eles no navegador, no POSTMAN ou mesmo via linha de comando “mongo” e executando uma query db.customers.find().pretty(). Você que escolhe.
Com o _id em mãos, abra o POSTMAN novamente (ou melhor, não feche-o) e construa a seguinte configuração:
- verbo HTTP: PUT
- URL: localhost:3000/customers/{seu_id} (troque seu_id pelo ID do seu objeto que irá atualizar)
- Headers: adicione “Content-Type” e “application/json” na primeira linha
- Body: selecione “raw” e cole o seu objeto JSON, sem o _id, com os dados que serão atualizados, como abaixo, onde mudei o nome do customer:
1 2 3 |
A sua configuração do POSTMAN deve se parecer com a abaixo:
Note que não adianta copiar o ID que eu coloquei na URL pois ele não se repete!
Clique no botão Send do POSTMAN e ele enviará esse PUT para nossa API, recebendo como retorno (se tudo der certo) o mesmo JSON que enviamos, que nem vale a pena colocar o print aqui.
Para ver se deu certo, basta fazermos um GET no navegador ou POSTMAN (ou ainda via linha de comando) para ver que o nome do customer de exemplo, que era luiztools, agora está mostrando “luiz fernando”.
Update: check!
Parte 7: DELETE de um cliente
E para finalizar o CRUD da nossa API, vamos criar uma rota que espere um verbo DELETE com o id do customer a ser excluído na URL. Fácil, fácil!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* DELETE ONE customer. */ router.delete('/customers/:id', function (req, res, next) { var db = require('../db'); var Customer = db.Mongoose.model('customers', db.CustomerSchema, 'customers'); Customer.find({ _id: req.params.id }).remove(function (err) { if (err) { res.status(500).json({ error: err.message }); res.end(); return; } res.json({success: true}); res.end(); }); }); |
Essa nossa nova e última rota trata requisições com o verbo HTTP DELETE no endpoint /customers/id. Para excluir o documento correspondente da nossa coleção basta darmos um find por _id usando o id que veio na URL e chamando a função remove logo em seguida, que removerá o documento que for retornado pelo find. O conteúdo do callback do remove dispensa comentários.
Salve o arquivo e reinicie o servidor Node para podermos testar usando o POSTMAN.
Mas antes, como é de praxe, você deve obter o ID de algum customer que você deseje excluir. Vai lá, eu espero.
Voltando ao POSTMAN, mude o verbo HTTP para DELETE, a URL para localhost:3000/customers/{seu_id} (troque pelo id do customer que quer excluir) e limpe qualquer coisa que tenha no seu body.
Clique em Send e se tudo der certo você verá um success: true como resposta no POSTMAN. Para se certificar que funcionou, faça um GET no endpoint raiz /customers e você verá que um customer não existe mais. No meu caso, o customer luiztools/luiz fernando:
E com isso finalizamos a nossa API em NodeJS!
Quando quiser fazer deploy dessa API em produção, dê uma olhada nesse outro post se estiver usando Windows ou use a Umbler, provedor que uso para minhas aplicações Node e PHP.
Se quiser colocar autenticação na sua API, leia este sobre JWT, vai lhe ajudar.
Se quiser tornar sua API mais escalável, o vídeo abaixo te ensina como subir um cluster da sua API.
Além disso, existe uma série de artigos super bacana aqui no blog que ensina como estruturar uma arquitetura de microserviços, toda baseada em webapis, pode ser o seu próximo passo com Node e Mongo.
Qualquer coisa que precisar, chama aí nos comentários.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Valeu, Luiz ! Exatamente o que eu estava procurando. E não baixei os fontes. Acompanhei o seu post passo a passo, copiando apenas as funcionalidades uma a uma, estudando, entendendo perfeitamente (muito bem explicado por voce) e testando uma a uma. Perfeito o seu código. Uma ajuda a quem tiver o mesmo problema que eu. Uso o Linux direto e, ao usar o Postman, tive problema. Ele não inicializava de jeito nenhum. Como a minha finalidade principal era o seu post, deixei de lado o Postman e instalei o HttpRequester no Firefox . Fácil, fácil e tranquilo. Ótima contribuição a sua, para a comunidade. O B R I G A D O !!!
Que bom que curtiu Paul. Outra ferramenta free que pode substituir o Postman é o Fiddler: http://www.telerik.com/fiddler
Olá Luiz blz? Muito bom o tutorial, fiz aqui todos os passos e funcionou muito bem, porém fiquei com duas dúvidas 🙂
1. O que é o “__v”: 0 que aparece em cada registro? Seria algum tipo de campo de versão que o POSTMAN gera?
2. Para outra pessoa consumir esta API, como faço para renderizar ela numa página HTML? Tem algum tutorial sobre isso?
Valeu mano.. Abraços
Boa tarde Douglas. Que bom que curtiu o tutorial.
Sobre o __v, aqui tem a resposta (é coisa do Mongoose): https://stackoverflow.com/questions/12495891/what-is-the-v-field-in-mongodb
Sobre consumir a API, basta fazer chamadas HTTP nos endpoints da mesma, o que pode ser feito em qualquer linguagem de programação. O único exemplo que tenho aqui no blog é fazendo com Android: http://184.73.67.74/post/acessando-banco-de-dados-remoto-com-android/
Cara não sei se é um erro mas a “função” db.customers.fint().pretty() não seria um find em vez de fint? eu tentei aqui com fint e nao foi ai coloquei find() e foi… =D bom vou continuar lendo que ta bom rs
Sério que cometi este erro? o.O
Valeu pelo toque, vou corrigir, é ‘find’ mesmo, de ‘buscar’.
Apenas um lembrete para quem estiver iniciando: Tem que instalar o Express na pasta do projeto (npm install express –save)
Excelente tutorial Luiz parabéns, me tira uma dúvida você tem algum tutorial sobre a autenticação da API.
Obrigado pelo feedback Roberto. A autenticação é por requisição, e pode ser feita do mesmo jeito que mostro neste tutorial aqui: http://184.73.67.74/post/autenticacao-em-node-js-com-passport/
[…] com as adaptações certas, você pode usar esse módulo em uma web API Express ou mesmo com algum outro framework do Node, para compor uma solução de […]
[…] com as adaptações certas, você pode usar esse módulo em uma web API Express ou mesmo com algum outro framework do Node, para compor uma solução de […]