Validando endpoints com NodeJS
Tags: node, javascript, express
Published: 2021-06-18 15:44:45

---
title: Validando endpoints com NodeJS
published: true
date: 2021-04-27 00:00:01 UTC
tags: nodejs, javascript, express
canonical_url: https://programadev.com.br/express-celebrate-joi/
---

## Introdução[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#Introducao)

No último [artigo](https://dev.to/guilhermegarcia86/express-e-mongodb-2173-temp-slug-4847113) vimos como integrar a uma aplicação **Express** com o banco de dados não relacional **MongoDB**. Porém detectamos que estávamos expondo mais informações do que queríamos na resposta das operações, ex:

```json
{
    "id": 1,
    "name": "Kelly",
    "telephone": "118888888",
    "address": "Rua dos Bobos n 1",
    "_id": "607d805caa972341ebd3e568"
}
```

A exposição do campo `_id` não é uma boa pratica pois pode expor detalhes internos que não queremos compartilhar com o mundo externo.

Além disso a API ainda não efetua nenhuma validação sobre os dados que recebe e como queremos manter a consistência dos dados que serão inseridos iremos adicionar ao projeto a biblioteca de validações **Joi** e o middleware **Celebrate** para tratamento das respostas da API.

## Transformando o modelo[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#Transformando-o-modelo)

No primeiro [artigo](https://programadev.com.br/express-rest-I/) criamos a classe **Contact** mas não chegamos a usá-la, agora iremos revê-la e começaremos a usar o modelo na aplicação.

É muito importante ser definido um domínio para a sua aplicação, um domínio bem estruturado e planejado impede que aplicações que começaram simples cresçam sem controle, agregando mais responsabilidades do que deveriam lidar. Esse é um problema antigo mas com o advento dos _microservices_ ficou explícito o quanto é importante haver um domínio bem estruturado.

Nessa aplicação o domínio é óbvio, contatos, porém nem sempre isso é tão claro e vale a pena investir um bom tempo separando os domínios de uma aplicação e como eles se relacionam e trocam informações uns com os outros.

Olhando para o arquivo na pasta **model** temos o seguinte:

```js
const Contact = {

    id: 0,
    name: "",
    telephone: "",
    address: ""

}

module.exports = Object.create(Contact)
```

Um ponto importante sobre domínios de aplicação é que eles não devem guardar informações que não sejam referentes ao seu domínio. Parece óbvio essa ideia porém como visto no exemplo acima temos um objeto **Contato** porém existe nele um campo chamado `id` que poderia ser usado por um banco de dados contudo não agrega valor ao domínio **Contato** , a primeira coisa seria excluí-lo. A segunda coisa que podemos usar para nos ajudar a manipular os dados do nosso domínio é ter uma forma de criarmos o objeto, uma função criadora que receba os dados de um contato e devolva uma instância pronta. Há algumas formas de fazer porém aqui iremos usar um **construtor** , uma função que toda classe já possui no **JavaScript** e já é responsável por criar o objeto.

Para podermos usar um **construtor** é necessário algumas alterações no objeto **Contato** :

```js
class Contact {

    constructor(name, telephone, address){
        this.name = name
        this.telephone = telephone
        this.address = address
    }

}

module.exports = Contact
```

Agora tiramos o campo `id` do domínio e alteramos o objeto **Contato** para que ele seja uma classe, com um construtor e três variáveis que são informadas no momento da criação.

Vamos usar a classe **Contato** , porém onde seria o lugar mais apropriado para isso?

Podemos delegar para a camada da nossa aplicação que é responsável por executar a lógica, a camada **Service**. Faz sentido pois nessa camada executamos as regras que são pertinentes e salvamos no banco de dados, então bem que podemos fazer esse tratamento lá.

```js
const Contact = require('../model')

class Service{

    constructor(repository){
        this.repository = repository
    }

    create(body){
        const contact = new Contact(body.name, body.telephone, body.address)
        this.repository.insert(contact)
    }
    
    async getById(name){
        const contact = await this.repository.selectById(name)

        return new Contact(contact.name, contact.telephone, contact.address)
    }
    
    async getAll(){
        const contactList = await this.repository.selectAll()

        const result = contactList.map((contact) => {
            return new Contact(contact.name, contact.telephone, contact.address)
        })

        return result
    }
    
    async put(name, body){
        const contact = await this.repository.update(name, new Contact(body.name, body.telephone, body.address))

        const result = contact.value

        return new Contact(result.name, result.telephone, result.address)
    }

    async patch(name, body){
        return this.repository.patch(name, body);
    }
    
    remove(name){
        this.repository.remove(name)
    }

}

module.exports = Service
```

No código acima para cada ponto que precisamos inserir ou recuperar os dados da aplicação iremos usar a classe **Contato** para mantermos o padrão de domínio.

Para ficar mais claro iremos fazer as requisições para a API e ver as respostas.

```bash
curl --location --request POST 'http://localhost:3000/api' \
--header 'Content-Type: application/json' \
--data-raw '{
    
    "name": "Kelly",
    "telephone": "1199999999",
    "address": "Rua dos Bobos n 2"

}' | json_pp
```

E temos como resposta o seguinte:

```json
{
    "name": "Kelly",
    "telephone": "1199999999",
    "address": "Rua dos Bobos n 2"
}
```

Podemos notar que não existe mais um campo `id`, apesar de internamente existir e ser importante para o banco de dados para o nosso domínio e para a nossa resposta ao usuário que fez essa chamada não é.

## Adicionando validação[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#Adicionando-validacao)

A nossa aplicação ainda não é capaz de validar a entrada de dados. Validar entrada de dados é algo que pode começar simples e logo se torna muito complicado dependendo das regras, para nos ajudar com isso existe uma biblioteca especializada nisso, o **JOI** , onde conseguimos definir regras a serem seguidas. Validar entrada de dados é algo que pode ser muito complicado e o **JOI** possui uma vasta [documentação](https://joi.dev/api/?v=17.4.0) para nos ajudar com regras, desde as mais simples até as mais complexas usando **Regex**.

Em conjunto com o **JOI** , que faz a validação dos dados que estão entrando na aplicação precisamos devolver uma resposta clara e padronizada ao usuário sobre o erro ocorrido. Para isso usaremos o _middleware_ **Celebrate** que integra muito bem ao **JOI**.

Precisamos adicionar somente o **Celebrate** ao projeto, pois o **JOI** é usado internamente pelo **Celebrate** :

```bash
npm install --save celebrate
```

E vamos começar criando a parte da nossa aplicação que será responsável por fazer as validações, vamos criar um pasta chamada **validation** e nela criamos o arquivo `index.js`:

```js
const { Joi } = require('celebrate')

const bodySchema = Joi.object().keys({
    name: Joi.string().min(3).required(),
    telephone: Joi.string().required(),  
    address: Joi.string().required()
})

const pathSchema = { name: Joi.string().min(3).required() } 

module.exports = {
    bodySchema,
    pathSchema
}
```

Definimos as seguintes validações:

- Quando for passado um _body_ são obrigatórios todos os campos e o campo `name` deve ter pelo menos 3 caracteres.
- Quando for passado um _pathParam_ de nome `name`, ele é obrigatório e deve ter pelo menos 3 caracteres.

Agora precisamos adicionar essas validações nas rotas, mas antes disso para que o **Celebrate** possa lidar com as mensagens de erro é necessário adicioná-lo como _middleware_ para o **Express** :

```js
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const router = require('./router')
//Declaração do middleware de erros do Celebrate
const { errors } = require('celebrate')

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

app.use((req, resp, next) => {
    resp.set('Access-Control-Allow-Origin', '*')
    next()
})

app.use('/api', router)

//Inclusão do middleware
app.use(errors())

const server = app.listen(3000, () => console.log('A API está funcionando!'))

module.exports = server
```

E após podemos adicionar as validações nas rotas:

```js
const { celebrate, Segments } = require('celebrate')
const validation = require('../validation')

router.post('/', celebrate({[Segments.BODY]: validation.bodySchema }), async (req, res) => {
    const contact = req.body

    service.create(contact)

    res.status(201).json(contact)
})

router.get('/:name', celebrate({[Segments.PARAMS]: validation.pathSchema }), async (req, res) => {

    const id = req.name_from_param

    const result = await service.getById(id)
    if(result !== undefined){
        res.status(200).json(result)
        return
    }
    
    res.sendStatus(204)
    
})
```

As funções de rotas do **Express** podem receber um argumento que é um **handler** e no nosso caso será o **Celebrate** juntamente com a validação do **JOI**.

Na função _celebrate_ é passado um objeto onde a chave é o segmento, **Segments** , que deve ser validado, no nosso exemplo validamos tanto o _body_ com o **Segments.BODY** e o _pathParam_ com o **Segments.PARAMS** ; e o valor é a validação que deve ser aplicada.

Com isso podemos testar a API, primeiramente com **POST** :

```bash
curl --location --request POST 'http://localhost:3000/api' \
--header 'Content-Type: application/json' \
--data-raw '{
    
    "name": "Kelly",
    "address": "Rua dos Bobos n 2"

}' | json_pp
```

E teremos a seguinte validação:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "celebrate request validation failed",
    "validation": {
        "body": {
            "source": "body",
            "keys": [
                "telephone"
            ],
            "message": "\"telephone\" is required"
        }
    }
}
```

E agora com um **GET** :

```bash
curl --location --request GET 'http://localhost:3000/api/k'
```

E como resposta teremos:

```json
{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "celebrate request validation failed",
    "validation": {
        "params": {
            "source": "params",
            "keys": [
                "name"
            ],
            "message": "\"name\" length must be at least 3 characters long"
        }
    }
}
```

## Conclusão[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#Conclusao)

Nesse artigo foi apresentado como podemos deixar a nossa aplicação um pouco mais coerente através de uma especificação de domínio e inserir validações que nos ajudam a proteger a aplicação de inserção de dados errados ou consultas desnecessárias.

## Código do projeto[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#Codigo-do-projeto)

O código desse artigo está no [GitHub](https://github.com/guilhermegarcia86/phonebook/tree/feature/validation).