Criando contador de visualizações no Strapi

Criando um contador de visualização para postagens no Strapi

Criando contador de visualizações no Strapi

O Strapi é um headless CMS open source muito famoso e lotado de recursos, inclusive foi a minha escolha para esse blog, como eu conto nesse post aqui.
Mesmo com várias features o projeto ainda me fez precisar criar algumas coisas para personalizar e uma delas foi um contador de visitas para os posts aqui do blog. Uma feature que eu já estava acostumado por conta do Dev.to:

posts-dev-to

Para criar o recurso eu estudei a melhor forma, e pensei em duas alternativas, a primeira utilizando os lifecycles hooks do Strapi para ouvir eventos relacionados às Models. A outra opção seria criar um controller personalizado no Strapi e uma rota para receber requisições com o Slug ou ID do post.
Das duas opções eu optei pela mais simples: Lifecycle Hooks.

Criando Lifecycle Hooks no Strapi

Para criar um Hook de ciclo de vida em uma Model no Strapi precisamos criar um arquivo chamado lifecycle.js junto as definições da nossa Model.
Irei criar um para a entidade Post, logo o caminho para o arquivo será: src/api/post/content-types/post/lifecycles.js.

Lembrando que você precisa ter um campo na sua Model para persistir as visualizações, no meu caso esse campo se chama views.

Dentro do arquivo precisamos exportar uma ou mais funções com nomes predefinidos pelo Strapi, são eles:

  • beforeCreate
  • beforeCreateMany
  • afterCreate
  • afterCreateMany
  • beforeUpdate
  • beforeUpdateMany
  • afterUpdate
  • afterUpdateMany
  • beforeDelete
  • beforeDeleteMany
  • afterDelete
  • afterDeleteMany
  • beforeCount
  • afterCount
  • beforeFindOne
  • afterFindOne
  • beforeFindMany
  • afterFindMany

Podemos ter todos esses hooks dentro do nosso arquivo ou apenas um, e esse será o nosso caso, vamos utilizar apenas o hook beforeFindMany.
Um detalhe aqui é que estou usando findMany para buscar UM post no Strapi, já que o findOne busca apenas por ID e eu faço buscas pelo Slug. Talvez você precise ajustar isso caso seja diferente na sua solução.

Com o nosso Hook escolhido, fica dessa forma nosso arquivo:

module.exports = {
  async beforeFindMany(event) {
  
    },
};

Estamos apenas exportando uma função chamada beforeFindMany que também recebe um event como argumento.
No objeto event temos acesso a algumas coisas da requisição, como os parâmetros utilizados. Vamos acessar o where dentro dos parâmetros para verificar se temos o slug como propriedade dessa requisição, dessa forma vamos deixar nosso Hook disparando apenas para requisições que busquem um post por essa propriedade, fazendo ele não contar visualizações para outras requisições:

module.exports = {
  async beforeFindMany(event) {
     if (event.params.where && event.params.where.slug) {
     
     }
    },
};

Agora que temos certeza que essa requisição está buscando um post específico pelo Slug, podemos buscar esse post e caso ele exista vamos incrementar + 1 no campo view dessa Model.
Para isso vamos utilizar o Query Engine do Strapi que nos permite interagir diretamente com o banco de dados e servirá para fazermos busca e atualizar o nosso contador. Ficando nosso código desta forma:

module.exports = {
  async beforeFindMany(event) {
    // Check if is searching for a one post
    if (event.params.where && event.params.where.slug) {
      const slug = event.params.where.slug["$eq"];

      const article = await strapi.db.query("api::post.post").findOne({
        where: { slug },
      });

      if (!article) {
        return;
      }

      const views = article.views || 0;

      await strapi.query("api::post.post").update({
        where: { slug },
        data: { views: views + 1 },
      });
    }
  },
};

Aqui estamos buscando o post pelo slug, caso não exista fazemos um early return e nada acontece, caso exista vamos pegar a quantidade atual de visualizações e em seguida fazer um update incrementando mais 1.

Então temos o nosso contador de visitas funcionando, para cada vez que um post for acessado, será incrementado +1 para esse post. Essa abordagem obviamente tem algumas possibilidades de melhoria, um exemplo é utilizar um throttling/rate limit para que o mesmo usuário não registre várias visualizações seguidas (você pode e deve configurar isso diretamente no painel do Strapi), outra melhoria é fazer com que esse Hook só dispare quando a requisição venha do Frontend, para garantir que outras requisições não interfiram na contagem.

Como último passo basta que você vá no seu dashboard do Strapi e exiba o campo de visualizações na sua listagem de posts, dessa forma:

Contador de visitas no Strapi

Espero que tenha gostado e caso tenha alguma dúvida basta deixar nos comentários aqui embaixo.