NextJS
NodeJS
NotionAPI
Como construí um blog com NextJS + NotionAPI
Um guia prático sobre como desenvolvi um blog utilizando tecnologias e arquiteturas modernas como NextJS, NodeJS, NotionAPI, etc.
Última atualização realizada em:
A habilidade de transmitir conhecimento é tão ou até mais importante do que simplesmente possuí-lo. Digo isso porque, ao longo dos últimos anos, tenho utilizado tecnologia para resolver problemas — sejam próprios ou de negócios —, mas até então não havia tomado a iniciativa de compartilhar com outras pessoas o conhecimento que adquiri durante essa trajetória.
Isso mudou. Decidi criar este blog pessoal como um espaço para dividir com vocês, minhas experiências técnicas e profissionais, minhas habilidades, registrar os desafios enfrentados e, principalmente, construir uma comunidade que também aprenda e evolua junto comigo.
Tabela de Conteúdos
Stack e ferramentas utilizadasDecisões técnicas e integraçõesArquitetura e estrutura do projetoVisão da arquiteturaEstrutura de pastas (Monorepo)Design de API RESTPipeline de Deploy (CI/CD)Desafios e soluções encontradasRoadmap e próximos passosConclusão
Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui
A construção e publicação de um blog hoje em dia é uma tarefa simples. Com tantas opções de ferramentas de desenvolvimento no-code e low-code, além da infinidade de CMS que facilitam e se integram ao desenvolvimento para lançar um site em questão de minutos. A personalização, que antes era um diferencial, também deixou de ser um obstáculo graças à soluções geradas por IA que criam designs e templates completos, personalizados e específicos para cada necessidade.
Diante desse cenário, talvez pareça pouco estratégico investir tempo na construção de um blog do absoluto zero. Mas foi exatamente essa reflexão que me levou a considerar o que poderia ser favorável: o aprendizado. Para mim, não faz sentido compartilhar com vocês o meu conhecimento técnico sem que a estrutura da base — ou seja, o blog em si — não fosse desenvolvida a partir das próprias habilidades técnicas que possuo.
Construir esse espaço me permite explorar uma gama de tecnologias atuais, passar por desafios e aprendizados e encontrar soluções para cada etapa do desenvolvimento. Para mim, vale mais do que apenas publicar textos, o blog será a vitrine prática da minha maneira de pensar e resolver os problemas em tecnologia.
Stack e ferramentas utilizadas
Quais ferramentas utilizei nesse projeto e porquê:
- NextJS: Parte fundamental da minha stack como desenvolvedor, o Next.js oferece um ecossistema robusto e em constante evolução, apoiado por uma comunidade ativa. Suas funcionalidades de geração de conteúdo estático (SSG), renderização no servidor (SSR) e caching inteligente tornam o blog mais rápido, escalável e otimizado para SEO — pontos cruciais para qualquer aplicação moderna.
- NotionAPI oficial (@notionhq/client): Escolhi o Notion como CMS justamente porque já faz parte do workflow pessoal e profissional. Integrar a API permite que o fluxo de criação e gestão de conteúdo aconteça dentro da mesma ferramenta que uso para organizar minhas próprias tarefas e projetos, tantos os simples, quanto complexos. Isso elimina fricções, centraliza meu trabalho e ainda garante flexibilidade para escalar o blog sem depender de soluções externas de gestão de conteúdo.
- Lib notion-react-x: Optei por essa biblioteca porque ela facilita e agiliza o consumo dos dados expostos pela API do Notion, permitindo colocar o blog no ar rapidamente. Apesar de ser útil na fase inicial, encaro-a como uma solução temporária: a visão de longo prazo é evoluir para um nível de abstração em que o CMS seja apenas uma parte da engrenagem, podendo ser substituído conforme as necessidades do projeto.
- Lib notion-client (cliente não oficial): De forma complementar ao react-notion-x, utilizei essa biblioteca para tratar os dados da API do Notion e entregá-los prontos para renderização. Ela agiliza o desenvolvimento inicial, mas também será substituída futuramente à medida que o blog evoluir e ganhar uma camada própria de gerenciamento de dados, mais robusta e duradoura.
- Eslint e prettier: Embora sejam ferramentas distintas, elas atuam em conjunto para garantir qualidade e consistência no código. O ESLint ajuda a antecipar erros e potenciais bugs ainda na fase de desenvolvimento, enquanto o Prettier assegura que o estilo e a formatação sigam um padrão único. Dessa forma, mesmo que novas pessoas colaborem com o projeto no futuro, haverá uma base sólida de padronização que facilitará a manutenção e a evolução.
- NodeJS: Parte fundamental da minha stack, escolhi o Node.js pela facilidade de integração com todo o ecossistema da aplicação. Como tanto o Node quanto o Next.js são baseados em JavaScript, toda a solução “fala a mesma língua”, o que simplifica a comunicação entre camadas e acelera o desenvolvimento. Além disso, o próprio Next.js evoluiu apoiado no Node, o que reforça a sinergia entre as ferramentas.
- TypeScript: Embora a base seja JavaScript, optei por desenvolver o blog em TypeScript, um superset que adiciona tipagem estática à linguagem. Isso traz vantagens importantes como detecção antecipada de erros, escalabilidade, clareza na manutenção e até mesmo melhor documentação implícita do código, facilitando a colaboração e evolução do projeto futuramente.
- ExpressJS: Escolhi esse framework pela sua simplicidade e flexibilidade. Com poucas linhas de código é possível configurar um servidor robusto, capaz de escalar conforme as necessidades do projeto. Além disso, por ser um dos frameworks mais utilizados no ecossistema Node.js, conta com uma comunidade ampla e uma grande variedade de módulos complementares, o que acelera o desenvolvimento e reduz a necessidade de reinventar soluções.
- Vercel: Para o deploy do Next.js, optei pela Vercel, plataforma criada pela própria equipe responsável pelo framework. Essa escolha traz benefícios imediatos como deploy automatizado, funções serverless, computação de borda e uma infraestrutura totalmente otimizada para aplicações Next.js. Isso agiliza o desenvolvimento, melhora a performance e garante escalabilidade desde o início. Além disso, enquanto o blog não atinge um grande volume de tráfego, o plano gratuito é suficiente, permitindo manter a aplicação no ar sem custos iniciais de manutenção.
- AWS EC2: Em paralelo, utilizei a Amazon EC2, que também oferece um nível gratuito robusto e confiável. A plataforma proporciona flexibilidade total de configuração e controle sobre a infraestrutura, permitindo subir aplicações Node.js com poucos comandos. O console de gerenciamento é intuitivo, o que acelera o processo de provisionamento e torna a solução ideal para experimentação e aprendizado em ambientes de nuvem corporativa.
- Github: Além de hospedar o código do projeto, o GitHub funciona como uma vitrine do meu processo de desenvolvimento. A integração com o Git possibilita colaboração distribuída, gestão de histórico completo e segurança no controle de versões. A CLI torna o fluxo de publicação rápido e eficiente, e os recursos de repositório remoto asseguram alta disponibilidade e resiliência do código, independentemente do ambiente local.
- Nginx (Servidor Proxy): A escolha do Nginx demonstra preocupação tanto com desempenho quanto com segurança. Sua configuração simples permite utilizá-lo como proxy reverso, otimizando a entrega do conteúdo e preparando a aplicação para cenários de maior escala. Além disso, a facilidade para adicionar certificados SSL garante uma camada extra de proteção para os usuários desde os primeiros estágios do projeto.
- PM2 (gerenciador de processos): A adoção do PM2 tem como objetivo assegurar a alta disponibilidade da aplicação. Ele mantém o servidor sempre ativo, reiniciando automaticamente em caso de falhas inesperadas. Além disso, oferece recursos de monitoramento e balanceamento de carga, reforçando a confiabilidade da solução em ambientes reais de produção
Decisões técnicas e integrações
As escolhas do projeto foram guiadas por três princípios: simplicidade inicial, escalabilidade futura e boas práticas de arquitetura.
- Separação de camadas: mesmo com o suporte a rotas serverless no Next.js, optei por manter um backend em Node/Express para refletir arquiteturas reais de mercado e facilitar a evolução do sistema.
- CMS via Notion: uso diário no meu workflow e fácil integração via API, garantindo centralização do conteúdo e flexibilidade para futuras substituições.
- Monorepo: adotado para padronizar, evitar duplicações e permitir compartilhamento de código entre frontend e backend.
- CI/CD: Vercel para o frontend (deploy automatizado integrado ao GitHub) e GitHub Actions + runner self-hosted na EC2 para o backend, garantindo baixo custo e confiabilidade.
- Nginx e PM2: Nginx como proxy reverso com SSL e PM2 para manter a API sempre disponível, reiniciando em caso de falhas.
- Qualidade de código: TypeScript, ESLint e Prettier formam a base para padronização, prevenção de erros e manutenção futura.
Cada decisão foi pensada para unir rapidez no desenvolvimento com robustez para o crescimento do projeto.
Arquitetura e estrutura do projeto
Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui
Visão da arquitetura
Embora a aplicação esteja em estágio inicial — um backend (NodeJS) responsável por servir artigos que serão consumidos pelo frontend (NextJS) — desde o início, tenho a ideia de um projeto voltado para escalabilidade e boas práticas arquiteturais. O meu objetivo não é apenas apresentar a construção de um blog, mas apresentar a maneira de pensar e organizar uma aplicação sustentável.
Por este motivo, separei claramente as responsabilidades de cada camada. O frontend atua como a camada de interface preparada para consumir os dados fornecidos pela camada de aplicação, enquanto o backend se concentra em manter as regras de negócio e a lógica de fornecimento de dados. Acredito que com essa divisão, alcançarei uma maior manutenabilidade e modularidade, preparando a aplicação para o futuro (já que pretendo continuar expandindo o conhecimento e compartilhando com a comunidade).
Ainda que o Next ofereça funcionalidades de backend serveless (Route Handler) que poderiam suprir essas necessidades iniciais, optei por um design mais robusto e desacoplado para refletir as arquiteturas modernas de cenários reais do mercado.

Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui
É importante destacar que, conforme a aplicação evolui, novas versões e melhorias também serão publicadas nesse repositório. Assim, além de acompanhar o progresso do blog em produção, você poderá visualizar a evolução do código e as diferentes etapas de desenvolvimento do projeto.
Estrutura de pastas (Monorepo)

Optei por organizar o projeto numa estrutura de pastas em formato monorepo. Para mim, essa abordagem melhor se adapta ao meu estilo de pensar e desenvolver aplicações. A ideia é centralizar todo o código num único repositório, mantendo ainda a separação clara entre as responsabilidades de cada parte do sistema.
Desta maneira, aproveito os benefícios de suas vantagens, que são: padronização de código, visão ampla do tamanho da aplicação, facilidade de manutenção e prevenção de erros, além de uma clara oportunidade de reutilizar e compartilhar trechos de códigos dentro do próprio sistema, evitando duplicações desnecessárias.
Óbvio que não é uma abordagem que só apresenta vantagens. Já foi possível perceber algumas desvantagens durante a execução desse pequeno projeto. Ainda assim, encaro essas limitações como parte das decisões de negócio, e acredito que, no meu caso, as vantagens superam as desvantagens.
Design de API REST
No backend, o design escolhido para a API segue o padrão REST, o que garante a simplicidade inicial e uma padronização entre sistemas. Vejo que não há necessidade de implementar estruturas mais complexas por enquanto (Keep it Simple,Stupid) já que o escopo do backend é enxuto.
- Rota GET /posts
Responsável por retornar todos os posts disponíveis para consumo, entregando um array com os dados de capa (covers) que serão exibidos na listagem inicial do blog.
Implementação:
app.get('/posts', async (req, res) => { try { const list = await getPostsList() return res.json(list) } catch (e) { console.error(e) // eslint-disable-line no-console return res.status(500).json({ error: 'Internal server error' }) } })
- Rota GET /post/:slug
Retorna o conteúdo completo de um artigo específico, identificado pelo
slug. Além do corpo do texto, a resposta inclui propriedades adicionais, como título, data de publicação e tags.Implementação:
app.get('/post/:slug', async (req, res) => { try { const { slug } = req.params const pageId = await getPostIdWithSlug({ slug }) if (!pageId) return res.status(400).json({ message: 'Bad request' }) const page = await getPostData({ pageId }) if (!page) return res.status(404).json({ message: 'Post not found' }) return res.json(page) } catch (e) { console.error(e) // eslint-disable-line no-console return res.status(500).json({ error: 'Internal server error' }) } })
Essa abordagem minimalista é suficiente para sustentar o estágio atual do projeto, ao mesmo tempo em que preserva a flexibilidade necessária para evoluções futuras, como a inclusão de novas rotas, versionamento de API, etc.
Pipeline de Deploy (CI/CD)
O pipeline de deploy do projeto possui dois objetivos principais: facilidade de manutenção e baixo custo (aproveitando os níveis níveis gratuitos disponíveis, pelo menos durante o início).
Mas por que isso importa? Quando desenvolvemos projetos, principalmente os solo — como este — cada minuto economizado conta muito. Refazer manualmente todo o processo de deploy a cada pequena alteração geraria um retrabalho imenso, muito desgaste e consumo de energia que poderiam ser dedicados a outra funcionalidade que evolua o projeto em tamanho e qualidade. Automatizar esse fluxo é uma maneira inteligente de reduzir esforços, conservar a integridade do nosso projeto e agilizar o entregas.
Pensando nesse fluxo de trabalho e de acordo com a stack que considerei para entregar o projeto, escolhi a Vercel para hospedar o aplicativo frontend em NextJS. Os motivos foram especificados na seção “Stack e ferramentas utilizadas”, dentre eles os mais importantes são a integração nativa entre o framework e a plataforma, que simplifica e otimiza todo o processo de publicação. Além disso, a plataforma facilidade a criação do fluxo de CI/CD totalmente automatizado e conectado ao Github.
Outro ponto decisivo foi o nível gratuito oferecido pela plataforma, que é robusto e inclui muitos recursos essenciais como certificado SSL, analytics e outras funcionalidades que fortalecem a aplicação desde os estágios iniciais sem gerar um custo adicional.
Se no frontend a Vercel cobre toda a parte do fluxo de deploy, no backend a estratégia teve que ser outra. Optei por hospedar a API numa instância EC2 (AWS), que oferece maior controle sobre a infraestrutura e flexibilidade para ajustar a aplicação conforme as necessidades do projeto.
Para não transformar esse processo em algo manual e trabalhoso (que seria por natureza), configurei um pipeline de deploy automatizado usando o GitHub Actions. Nesse caso, um runner self-hosted foi instalado diretamente dentro da instância EC2. Esse runner funciona como o “elo” entre o GitHub e o servidor, permitindo que cada push ou merge no repositório dispare um fluxo automatizado de atualização da aplicação.

Esse pipeline é responsável por:
- Puxar a versão mais recente do código diretamente do repositório.
- Construir a aplicação dentro de um container, garantindo consistência no ambiente de execução.
- Reiniciar a aplicação automaticamente (com ajuda do PM2) para aplicar as mudanças sem downtime.
Assim, mesmo rodando em uma infraestrutura tradicional (fora do modelo serverless), consigo manter o backend alinhado com as boas práticas de CI/CD, assegurando entregas rápidas, confiáveis e sem intervenção manual a cada alteração.
Essa combinação — Vercel no frontend e EC2 no backend — equilibra simplicidade e flexibilidade. O frontend se beneficia da automação total e da escalabilidade nativa da Vercel, enquanto o backend ganha robustez e controle dentro de uma infraestrutura altamente personalizável na AWS.
Desafios e soluções encontradas
Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui
- Integração com o Notion:
Desafio: A API do Notion é flexível, mas traz dados em formato complexo e pouco amigável para renderização direta.
Solução: Utilizei bibliotecas de apoio (notion-react-x e notion-client) para acelerar o desenvolvimento inicial, planejando substituí-las futuramente por uma camada própria de abstração.
- Estrutura em Monorepo:
Desafio: Organizar frontend e backend no mesmo repositório trouxe dificuldades na configuração de dependências e scripts de build.
Solução: Padronizei scripts e usei ferramentas de gerenciamento para manter consistência, além de documentar os processos para evitar retrabalho.
- Pipeline de Deploy
Desafio: Garantir automação de deploy sem custos adicionais e sem depender de fluxos manuais.
Solução: Combinei Vercel (frontend) com GitHub Actions + runner self-hosted (backend/EC2), mantendo baixo custo e confiabilidade.
- Manutenção da API
Desafio: Manter a aplicação sempre ativa em ambiente de produção, prevenindo falhas ou quedas inesperadas.
Solução: Adotei o PM2 como gerenciador de processos, garantindo reinício automático e monitoramento básico.
- Segurança e escalabilidade
Desafio: Desde o início, era importante pensar em HTTPS e em um caminho para lidar com aumento de tráfego.
Solução: Configurei o Nginx como proxy reverso, facilitando a adição de SSL e preparando o ambiente para cenários de maior demanda.
Roadmap e próximos passos
- Implementação de testes automatizados
- Uso de CDN para otimizar a entrega do conteúdo estático no frontend visando SEO.
- Melhoria de design para refletir um blog mais moderno
- Adicionar scripts de monitoramento de performance do conteúdo
- Utilização de ferramenta de bundle para diminuir o tamanho da aplicação devido ao monorepo
- Refatoração do código para desacoplar e preparar a aplicação para futuras substituições
Conclusão
Construir este blog do zero foi, antes de tudo, uma decisão voltada para o compartilhamento de experiência e aprendizado. Em vez de optar por soluções prontas, escolhi enfrentar os desafios de estruturar cada camada, integrar ferramentas e pensar em arquitetura de forma consciente. Esse processo não apenas fortaleceu minhas habilidades técnicas, como também abriu espaço para compartilhar com mais clareza a forma como enxergo e resolvo problemas em tecnologia.
Mais do que um repositório de artigos, este espaço se torna uma vitrine prática do meu raciocínio e da minha evolução como desenvolvedor. Cada decisão técnica, cada desafio superado e cada etapa documentada aqui tem o objetivo de gerar valor tanto para mim quanto para quem acompanha.
Esse é apenas o começo. O roadmap mostra que ainda há muito a explorar — desde otimizações de performance até novas camadas de automação e escalabilidade. Convido você a acompanhar essa jornada, seja lendo os artigos, explorando o código no GitHub ou até trocando experiências. Afinal, como disse na introdução: a habilidade de transmitir conhecimento é tão ou até mais importante do que simplesmente possuí-lo.
Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui