8
respostas

[Dúvida] Alguem pode me ajudar?

https://github.com/WesleySoeiro/ProjetoBD

Segue o link do meu projeto estável.

Estou tentando criar um middleware de paginação para juntar as informações deste curso com o anterior de API rest.

fiz algo nesse sentido:

Middleware

const NaoEncontrado = require("../erro/NaoEncontrado.js");

async function paginar(req, res, next) {
  try {
    let { limite = 2, pagina = 1, ordenacao = "_id:-1" } = req.query;
    let [campoOrdenacao, ordem] = ordenacao.split(":");
    ordem = ordem === "1" ? "ASC" : "DESC";

    if (isNaN(Number(limite)) || isNaN(Number(pagina))) {
      res.status(400).json({ mensagem: "Os valores devem ser um numéricos" });
    }

    if (limite < 1 || pagina < 1) {
      res.status(400).json({
        mensagem: "Os valores de limite e pagina devem ser maiores que 0",
      });
    }

    Object.entries(req.query).forEach(([chave, valor]) => {
      if (!chave || valor.trim() === "") {
        res.status(400).send("Parametros inválidos.");
      }
    });

    const resultado = req.resultado;

    const resultadoPaginado = await resultado.findAll({
      limit: limite,
      offset: (pagina - 1) * limite,
      order: [[campoOrdenacao, ordem]],
    });

    if (resultadoPaginado.length > 0) {
      return res.status(200).json(resultadoPaginado);
    } else {
      return next(new NaoEncontrado("Não há mais dados para listar."));
    }
  } catch (erro) {
    console.log(erro);

    next(erro);
  }
}

module.exports = paginar;

Services

const { where } = require("sequelize");
const dataSource = require("../database/models");


class Services {
  constructor(model) {
    this.model = model;
  }

  async getAll(req, res, next) {
    const resultado = dataSource[this.model].findAll();
    req.resultado = resultado;
    next();
  }
  
  ....

Controller

const { Op } = require("sequelize");
const CamposVazios = require("../erro/CamposVazios.js");
const ErroValidacao = require("../erro/ErroValidacao.js");
const NaoEncontrado = require("../erro/NaoEncontrado.js");
const filtro = require("../helpers/filtro.js");

class Controller {
  constructor(entidadeService) {
    this.entidadeService = entidadeService;
  }

  async getAllRegisters(req, res, next) {
    try {
      const listarRegistros = await this.entidadeService.getAll(req, res, next);
    } catch (erro) {
      console.log(erro);

      next(new NaoEncontrado());
    }
  }
  
  ...

Routes

const routes = require("express").Router();
const ProdutosController = require("../controllers/ProdutosController.js");
const paginar = require("../middlewares/paginar.js");

const produtosController = new ProdutosController();

routes
  .get(
    "/produtos",
    async (req, res, next) =>
      await produtosController.getAllRegisters(req, res, next),
    paginar
  )

Erro

Server is running on port 3000
TypeError: resultado.findAll is not a function
    at paginar (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\src\middlewares\paginar.js:27:47)
    at Layer.handle [as handle_request] (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\route.js:149:13)
    at ProdutosServices.getAll (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\src\services\Services.js:12:5)
    at ProdutosController.getAllRegisters (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\src\controllers\Controller.js:14:58)
    at C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\src\routes\produtosRoutes.js:11:32
    at Layer.handle [as handle_request] (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\route.js:149:13)
    at Route.dispatch (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\route.js:119:3)
    at Layer.handle [as handle_request] (C:\Users\wesle\OneDrive\Área de Trabalho\Projeto DB\Projeto\node_modules\express\lib\router\layer.js:95:5)

Eu percebi que na paginação resultados traz o objeto, como limitar o objeto que é recebido na area de paginação?

8 respostas

Olá Wesley! Tudo bem?

O erro que você está recebendo, "TypeError: resultado.findAll is not a function", indica que o resultado não é uma instância de um modelo Sequelize, o que é necessário para que o método findAll funcione.

Vamos analisar o que pode estar acontecendo:

  1. Definição do resultado: No seu serviço, você está definindo resultado como uma promessa ao chamar dataSource[this.model].findAll(). Isso significa que resultado não é o resultado da consulta, mas sim uma promessa que ainda não foi resolvida. Para corrigir isso, você precisa aguardar a resolução dessa promessa antes de atribuí-la ao req.resultado.

    Alteração sugerida no método getAll do seu serviço:

    async getAll(req, res, next) {
      try {
        const resultado = await dataSource[this.model].findAll();
        req.resultado = resultado;
        next();
      } catch (error) {
        next(error);
      }
    }
    
  2. Uso do resultado no middleware: Certifique-se de que o resultado que você está passando para o middleware paginar é uma lista de registros que o Sequelize retornou. Com a alteração acima, isso deve ser resolvido.

  3. Verificação de erros: Além disso, é sempre uma boa prática adicionar verificações de erro para garantir que o resultado seja um array antes de tentar usar métodos como findAll.

Com essas alterações, seu middleware de paginação deve funcionar corretamente, assumindo que o restante da configuração do Sequelize está correta.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

essa lista de registros, dever ser em objeto? ou um JSON?

e após receber esses resultados, como limitar a exibição? crio um array de objetos e acesso os valores totais e tiro via foreach os índices pelos limites expostos?

podem me dar uma dica?

eu consegui pensar nessa solução:

...

const resultado = req.resultado;

const results = resultado.map((resposta) => resposta.toJSON());

    const resultPginado = results.slice((pagina - 1) * limite, pagina * limite);
    
    res.status(200).json(resultPginado);

eu estava resolvendo a promise no middleware de paginação, mas deixei a resolução no modulo service, e quando transformei num JSON eu consegui fazer essa lógica, vou testar com mais rotas para validar.

Neste caso Wesley, a lista de registros retornada pelo método findAll é um array de objetos, onde cada objeto representa uma linha da tabela no banco de dados. Esse array pode ser enviado diretamente como resposta no formato JSON, que o Express já sabe manipular.

Para limitar a exibição, você não precisa criar manualmente um novo array. O Sequelize permite usar os parâmetros limit (quantidade de registros por página) e offset (posição inicial para a busca) diretamente na consulta. Assim, o banco de dados retorna apenas os registros necessários, você pode ver mais detalhes neste artigo, que está em inglês, mas, usando o recurso de tradução conseguirá compreender.

Obs: no seu código no Github não possui a função paginar, imagino que não esteja atualizado com a última versão que você fez. Se você me der mais detalhes de qual rota está gerando o erro ou como está testando o código , posso simular e te ajudar melhor.

Fico no aguardo e à disposição.

meu código no github é a vesão estável, o que tem de diferente é o código que eu pus na dúvida, a paginação, o service eu tirei apenas o get all do service pq se eu aprender 1 eu faço o resto.

com a solução do map em resultados eu recebo o valor, mas volta um erro tbm dizendo que resultado.map em results is not a function.

Wesley, obrigado pelo detalhamento. Consegui chegar a uma solução.

  • Passo 01: Altere o método getAll para retornar os registros diretamente em vez de armazená-los em req.resultado. A responsabilidade de buscar e paginar os dados deve ser tratada em uma função única. No arquivo service.js, mude a função getAll para o formato abaixo:

     async getAll(filtros = {}, paginacao = {}) {
        const { limite = 10, offset = 0, ordenacao = [["id", "DESC"]] } = paginacao;
    
        const resultado = await dataSource[this.model].findAndCountAll({
          where: filtros,
          limit: limite,
          offset: offset,
          order: ordenacao,
        });
    
        return resultado;
      }
    
  • Passo 02: Agora que mudamos a função getAll, também precisamos fazer com que o middleware de paginação configure as variáveis limite, offset, e ordenacao:

    const NaoEncontrado = require("../erro/NaoEncontrado.js");
    
    async function paginar(req, res, next) {
        try {
            let { limite = 2, pagina = 1, ordenacao = "id:-1" } = req.query;
            let [campoOrdenacao, ordem] = ordenacao.split(":");
            ordem = ordem === "1" ? "ASC" : "DESC";
    
            if (isNaN(Number(limite)) || isNaN(Number(pagina))) {
                return res
                    .status(400)
                    .json({ mensagem: "Os valores devem ser numéricos" });
            }
    
            if (limite < 1 || pagina < 1) {
                return res.status(400).json({
                    mensagem: "Os valores de limite e pagina devem ser maiores que 0",
                });
            }
    
            const paginacao = {
                limite: parseInt(limite),
                offset: (parseInt(pagina) - 1) * parseInt(limite),
                ordenacao: [[campoOrdenacao, ordem]],
            };
    
            req.paginacao = paginacao;
            next();
        } catch (erro) {
            next(erro);
        }
    }
    
    module.exports = paginar;
    
  • Passo 03: Agora o controlador pode delegar a responsabilidade de buscar os dados paginados para o serviço, utilizando req.paginacao configurado pelo middleware:

     async getAllRegisters(req, res, next) {
        try {
          const filtros = {}; 
          const paginacao = req.paginacao;
    
          const registros = await this.entidadeService.getAll(filtros, paginacao);
    
          if (registros.rows.length > 0) {
            return res.status(200).json({
              total: registros.count,
              paginas: Math.ceil(registros.count / paginacao.limite),
              registros: registros.rows,
            });
          } else {
            throw new NaoEncontrado("Não há registros disponíveis.");
          }
        } catch (erro) {
          console.error("Erro em getAllRegisters:", erro);
          next(erro);
        }
      }
    
  • Passo 04: Por fim, atualize as rotas:

    routes.get("/produtos", paginar, async (req, res, next) => {
      await produtosController.getAllRegisters(req, res, next);
    })
    

Em resumo, antes, o seu código estava misturando responsabilidades: a busca dos registros e a configuração da paginação estavam espalhadas entre o serviço, o middleware e o controlador. Inclusive, a busca de registros era feita no método getAll do serviço e os resultados eram atribuídos diretamente a req.resultado, mas isso gerava problemas de compatibilidade com o middleware, que não tinha controle sobre os parâmetros de paginação e ordenação, por isso os erros de método não encontrado.

Agora, o serviço (getAll) centraliza a lógica de busca no banco e retorna os dados paginados de forma estruturada, enquanto o middleware paginar configura todos os parâmetros de paginação (limite, offset, ordenacao) e valida os valores recebidos. Já o controlador (getAllRegisters) foca apenas em delegar a busca ao serviço e formatar a resposta para o cliente.

Resultado:

Exemplos de endpoint:

Fico à disposição.

CONSEGUI!!!

minha paginação esta funcionando agora.

https://github.com/WesleySoeiro/ProjetoBD

Atualizei o projeto.

Ainda estou testando, penso em refatorar os métodos de sort, jogar em uma função, mas por enquanto adorei o desafio de pensar em como transformar este problema em resultado.

no fim o segredo era lancar resultado como JSON e trabalhar com metodo de array.

Fico feliz que tenha conseguido o projeto, Wesley!

E caso tenha mais problemas ou dúvidas, pode contar com os monitores ou outros estudantes aqui no fórum.

Bons estudos!