Criando uma API com Node.js, Express e MongoDB

Esse artigo é baseado no tutorial https://woliveiras.com.br/posts/construindo-uma-api-com-node-js-parte-1-criando-e-listando-dados/

O GitHub deste projeto https://github.com/totemarcal/apiFormik

1- Crie uma pasta chamada apiFormik

2- Acesse a pasta apiFormik e rode:

npm init

3- Vamos instalar o Express e o Nodemon. O Express é nosso framework web e o Nodemon nos ajuda na hora do desenvolvimento para não ser necessário ficar reiniciando o servidor a cada alteração de código fonte.

npm install --save express debug && npm install --save-dev nodemon

4- Abra o Visual Code executando:

code .

5- Edite o arquivo package.json para adicionar o seguinte script:

"scripts": {
    "dev": "node node_modules/nodemon/bin/nodemon bin/server"
}

6- Crie a pasta bin e o arquivo server.js dentro dela (pode ser dentro do Visual Code). Neste arquivo, adicione o seguinte conteúdo:

const app = require('../src/app');
const http = require('http');
const debug = require('debug')('nodestr:server');

// PORT // based on express-generator
function normalizePort(val) {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    return val;
  }

  if (port >= 0) {
    return port;
  }

  return false;
}

const port = normalizePort(process.env.PORT || 3000);
app.set('port', port);

// error handler
function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }
  
  const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;

  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);

    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);

    default:
      throw error;
  }
}

// listener handler
function onListening() {
  const addr = server.address();
  const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

// levanta o server
const server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
console.log(`API is alive on ${port}!`);

7- Crie um pasta chamada src e dentro da pasta crie o arquivo app.js e adicione o seguinte código:

const express = require('express');

// App
const app = express();

// Load routes
const indexRoutes = require('./routes/index-routes');
// Chama a primeira rota '/'
app.use('/', indexRoutes);

module.exports = app;

8- Crie a pasta routes dentro de src e dentro da pasta crie o arquivo index-routes.js e adicione o seguinte código:

const express = require('express');
const router = express.Router();

router.get('/', (req, res, next) => {
  res.status(200).send({
    title: 'userFormik',
    version: '1.0.0'
  });
});

module.exports = router;

9- Start o servidor com o comando abaixo:

npm run dev

Acesse o endereço  localhost:3000

MongoDB


10- Acesse o site mongodb.com/cloud  e crie um Cluster

Clique em DataBase Acess e depois em add new user.

Depois volte para a tela de Cluster, clique em Connect e selecione a aba Choose a connection method e copie a Connection String. Configura a liberação de endereços de IP em Network Acess e depois Allow Access from Anywhere.


Configurando o MongoDB

11- Para fazer a conexão com o MongoDB vamos utilizar variáveis de ambiente, que vamos colocar em um arquivo de configuração, e o Mongoose, uma lib que facilita a utilização do MongoDB. Para isso execute o seguinte comando:

npm install --save mongoose dotenv

12- Crie o arquivo .env na raiz do nosso projeto (fora da pasta src) e cole a Connection String copiada anteriormente alterando o usuário e senha.


Salvar e Consultar

13- Crie a pasta models dentro da pasta src e depois crie o arquivo userFormik.js com o seguinte conteúdo:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const schema = new Schema({
  name: {
    type: String,
    required: true,
    trim: true
  },
  username: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  }
});

module.exports = mongoose.model('Mentions', schema);

14-  Crie uma pasta controllers dentro de src e crie o arquivo apiFormik-controller.js e adicione o seguinte conteúdo:

const repository = require('../repositories/userFormik-repository');


// list
exports.listUserFormik= async (req, res) => {
  try {
    const data = await repository.listUserFormik();
    res.status(200).send(data);
  } catch (e) {
    res.status(500).send({message: 'Falha ao carregar userFormik.'});
  }
};

// create
exports.createUserFormik= async (req, res) => {
  try {
    await repository.createUserFormik({
      name: req.body.name,
      username: req.body.username,
      password: req.body.password
    });

    res.status(201).send({message: 'userFormik cadastrada com sucesso!'});
  } catch (e) {
    
    res.status(500).send({message: 'Falha ao cadastrar userFormik.'});
  }
};

15- Dentro da pasta src crie uma pasta chamada repositories, dentro dessa pasta crie o arquivo mentions-repository.js e adicione o seguinte código.

// importando o mongoose
const mongoose = require('mongoose');
const userFormik = mongoose.model('userFormik');

exports.listUserFormik= async () => {
      const res = await userFormik.find({}, 'name username -_id');
      return res;
  };
  
  // create
  exports.createUserFormik= async data => {
      const user= new userFormik(data);  
      await user.save();
  };

15- Navegue até a pasta src e depois routes, crie o arquivo userFomik-routes.js e adicione o seguinte conteúdo:

const express = require('express');
const router = express.Router();
const userFormikController = require('../controllers/apiFormik-controller');

router.get('/', userFormikController.listUserFormik);
router.post('/', userFormikController.createUserFormik);

module.exports = router;

16- Altere o arquivo app.js com o código seguinte. Com isso temos a nossa aplicação Express conectada com o MongoDB em um cluster na nuvem (cloud).

const express = require('express');
//
const mongoose = require('mongoose');
require('dotenv').config();

// App
const app = express();

app.use(express.json());
app.use(express.urlencoded({extended: true}));

// usamos o mongoose para criar uma conexão com a 
// connection string do banco de dados que passamos no arqvuio .env
mongoose.connect(process.env.DATABASE_CONNECTION_STRING, {
    useUnifiedTopology: true,
    useFindAndModify: true,
    useNewUrlParser: true,
    useCreateIndex: true 
});

const db = mongoose.connection;
  
db.on('connected', () => {
    console.log('Mongoose default connection is open');
});

db.on('error', err => {
    console.log(`Mongoose default connection has occured \n${err}`);
});

db.on('disconnected', () => {
    console.log('Mongoose default connection is disconnected');
});
// se o usuário matar o processo
process.on('SIGINT', () => {
    db.close(() => {
        console.log(
        'Mongoose default connection is disconnected due to application termination'
        );
        process.exit(0);
    });
});

// Load models
const userFormik = require('./models/userFormik');

// Load routes
const indexRoutes = require('./routes/index-routes');
// Chama a primeira rota '/'
app.use('/', indexRoutes);

const userFomikRoutes = require('./routes/userFomik-routes');
app.use('/userFomik', userFomikRoutes);

module.exports = app;

17- Teste no Postman


Validação

18- Para fazer a validação instale a seguinte lib:

npm install --save express-validator

19- Agora vamos importar o check do express-validator, no nosso userFormik-routes.js e adicionar suas validações na hora do POST check(nome do campo do body). O arquivo final ficará assim:

const express = require('express');
const router = express.Router();
const userFormikController = require('../controllers/userFormik-controller');
const { check } = require('express-validator');


router.get('/', userFormikController.listUserFormik);
router.post('/', [
    check('name').isLength({ min: 7 }).withMessage("O nome precisa ter no mínimo 7 caracteres."),
    check('username').isLength({ min: 20, max: 280 }).withMessage("A menção precisa ter no mínimo 20 caracteres e no máximo 280.")
],userFormikController.createUserFormik);

module.exports = router;

20- No nosso userFormik-controller.js vamos importar o validationResult e no createUserFormik no arquivo userFormik-controller vamos utilizar essa função para retornar um erro, caso o usuário tenha cometido um engano. O arquivo final ficará assim:

const repository = require('../repositories/userFormik-repository');
const { validationResult } = require('express-validator');


// list
exports.listUserFormik= async (req, res) => {
  try {
    const data = await repository.listUserFormik();
    res.status(200).send(data);
  } catch (e) {
    res.status(500).send({message: 'Falha ao carregar userFormik.'});
  }
};

// create
exports.createUserFormik= async (req, res) => {
  try {
    const {errors} = validationResult(req);

    if(errors.length > 0) {
      return res.status(400).send({message: errors})
    }
    
    await repository.createUserFormik({
      name: req.body.name,
      username: req.body.username,
      password: req.body.password
    });

    res.status(201).send({message: 'userFormik cadastrada com sucesso!'});
  } catch (e) {
    
    res.status(500).send({message: 'Falha ao cadastrar userFormik.'});
  }
};

Atualizando

21- Na pasta repositories, no arquivo userFormik-repository.js adicione a seguinte função:

// update
  exports.updateMention = async (id, data) => {
    await Mentions.findByIdAndUpdate(id, {
      $set: data
    });
  };

22- Agora vamos adicionar a rota para atualização. Para isso na pasta routes edite o arquivo userFormik-routes.js. Perceba que já implementamos as validações.

router.put('/:id', [
    check('name').isLength({ min: 7 }).withMessage("O nome precisa ter no mínimo 7 caracteres."),
    check('username').isLength({ min: 20, max: 280 }).withMessage("A menção precisa ter no mínimo 20 caracteres e no máximo 280.")
], userFormikController.updateUserFormik);

23- Na pasta controller edite o arquivo userFormik-controller.js e adicione o seguinte código:

//update
exports.updateUserFormik = async (req, res) => {
  try {
    const {errors} = validationResult(req);

    if(errors.length > 0) {
      return res.status(400).send({message: errors})
    }
    
    await repository.updateUserFormik(req.params.id, req.body);
    res.status(200).send({
      message: 'Usuário atualizado com sucesso!'
    });
  } catch (e) {
    res.status(500).send({message: 'Falha ao atualizar a usuário.'});
  }
};

24- Teste no Postman e altere o verbo para PUT.


Apagando

25- Na pasta repositories, no arquivo userFormik-repository.js adicione a seguinte função:

//delete
  exports.deleteUserFormik = async id => {
    await userFormik.findByIdAndDelete(id);
  };

26- Agora vamos adicionar a rota para atualização. Para isso na pasta routes edite o arquivo userFormik-routes.js.

router.delete('/:id', userFormikController.deleteUserFormik);

27- Na pasta controller edite o arquivo userFormik-controller.js e adicione o seguinte código:

// delete
exports.deleteUserFormik = async (req, res) => {
  try {
    await repository.deleteUserFormik(req.params.id);
    res.status(200).send({
      message: 'Usuário removido com sucesso!'
    });
  } catch (e) {
    res.status(500).send({message: 'Falha ao remover o usuário.'});
  }
};

28- Teste no Postman e altere o verbo para DELETE.