Boas-vindas ao curso de Next.js SSR! Meu nome é Vinny Neves e serei seu instrutor nesta jornada.
Estamos entusiasmados para dar continuidade a mais um curso de Next, onde Vamos aprimorar o Code Connect, que iniciamos no curso anterior, adicionando novas funcionalidades. Anteriormente, apenas exibíamos os posts, mas agora vamos introduzir interatividade, como curtir, comentar e responder aos comentários.
Para alcançar esse objetivo, aparentemente simples, vamos explorar conceitos fundamentais do Next e do Prisma. Começaremos aprimorando o banco de dados, ajustando nossa aplicação e adicionando componentes, alguns do lado do servidor e outros do lado do cliente. Além disso, aprenderemos diversas maneiras de interagir com a pessoa usuária, maximizando a produtividade ao combinar as capacidades do React e do Next.
Se você já possui conhecimento em React, compreende o funcionamento do PostgreSQL e está familiarizado com o Docker, está preparado para nos acompanhar nessa jornada. Aguardamos você no próximo vídeo para evoluir o Code Connect e desenvolver muitas funcionalidades interessantes. Até lá!
Vamos mergulhar no código? É hora de elevar o Code Connect para o próximo nível. Nosso foco estará na parte de comentários, permitindo que as pessoas usuários comentem nos posts e respondam a outros comentários. Estamos prestes a iniciar uma jornada repleta de conceitos fascinantes.
O projeto já está baixado e em execução. O terminal está rodando o comando npm run dev
, e o Docker está operando o Postgres. Temos um contêiner em execução para o Postgres, nosso banco de dados. Tudo está funcionando conforme esperado. Ao acessarmos o localhost na porta 3000, por exemplo, a lista completa de posts é exibida. Além disso, podemos clicar em um card para ver os detalhes específicos de um post. Essa funcionalidade foi desenvolvida no curso anterior e agora vamos expandir e adicionar o recurso de comentários.
No Figma, ao observarmos a parte inferior, identificamos dois níveis de comentários. Primeiramente, um comentário pode ser diretamente associado a um post, onde um usuário comenta diretamente em um post específico. Esse comentário será controlado por uma modal. No entanto, há um segundo detalhe: podemos responder a um comentário. Portanto, teremos comentários vinculados a posts e comentários vinculados a outros comentários. Vamos elaborar essa estrutura.
Comment
No VS Code, já temos no arquivo schema.prisma
todos os nossos modelos, incluindo User
e Post
. Agora vamos adicionar um novo modelo: o Comment
.
model Commment {
}
Podemos começar da mesma forma que fizemos com o Post
, definindo um id
como inteiro e marcando-o como @id
. Ele terá um valor padrão de auto-incremento.
model Commment {
id Int @id @default(autoincrement())
}
Em seguida, adicionamos um campo text
do tipo String
, e um campo createdAt
do tipo DateTime
com um valor padrão now()
. Além disso, criamos um campo updatedAt
também do tipo DateTime
e adicionamos a anotação @updatedAt
para que o Prisma atualize esse valor sempre que um comentário for atualizado.
model Commment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Um comentário estará associado a um post e terá um autor, ou seja, um user. Para indicar esse relacionamento, utilizamos a referência ao user
. Para isso, criamos um campo authorId
como inteiro e definimos o relacionamento com author
e User
usando a anotação @relation()
. Especificamos os campos que apontam para user
, que é authorId
, e as referências, que é o id
do usuário.
model Commment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
}
Agora precisamos estabelecer o relacionamento inverso. No modelo User
, indicamos que ele possui comments
, que será um array de Comment
.
model User {
id Int @id @default(autoincrement())
name String
username String @unique
avatar String
Post Post[]
comments Comment[]
}
Em seguida, replicamos o mesmo processo para o modelo Comment
, definindo um campo postId
como inteiro e estabelecendo o relacionamento post
com o id
do post.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
}
Em seguida, no modelo Post
, além de indicar o autor, adicionamos o campo comments
, que será um array de Comment
. Dessa forma, o relacionamento entre os modelos está corretamente estabelecido.
model Post {
id Int @id @default(autoincrement())
cover String
title String
slug String @unique
body String
markdown String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
comments Comment[]
}
Agora, vamos criar um relacionamento de autor. Isso, porque, um comentário pode responder a outro comentário. Existem várias maneiras de modelar isso, e neste cenário vamos criar uma coluna chamada parentId
dentro do modelo Comment
. Essa coluna será do tipo inteiro e usaremos um ponto de interrogação indicando que ela pode ser nula, ou seja, um comentário pode ou não estar respondendo a outro. O objetivo desse parentId
é identificar o comentário pai, estabelecendo uma hierarquia.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
parentId Int?
}
Assim, podemos criar um relacionamento dentro do modelo Comment
com ele mesmo: comments Comment[]
. Para configurar esse relacionamento, primeiro indicamos o relacionamento ascendente, ou seja, quem é o pai desse comentário. Então, definimos que o parent
aponta para um Commnet?
que pode ser nulo ?
(não existir). Então, utilizamos a anotação @relation()
especificando os campos que apontam para o parentId
e id
do comentário.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
parentId Int?
parent Comment? @relation(fields: [parentId], references: [id])
comments Comment[]
}
Em seguida, no relacionamento comments
, declaramos que ele também é um @relation()
.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
parentId Int?
parent Comment? @relation(fields: [parentId], references: [id])
comments Comment[] @relation()
}
Porém, o Prisma nos alerta que esse relacionamento auto-referencial está ambíguo, pois há mais de um Comment
envolvido. Para resolver isso, damos um nome para esse relacionamento antes de passar os campos. Podemos utilizar CommentChildren
, por exemplo.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
parentId Int?
parent Comment? @relation("CommentChildren", fields: [parentId], references: [id])
comments Comment[] @relation()
}
Em seguida, atribuímos o mesmo nome para os comments
na linha 48. E para manter a coesão, chamamos o relacionamento de children
.
model Comment {
id Int @id @default(autoincrement())
text String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
postid Int
post Post @relation(fields: [postId], references: [id])
parentId Int?
parent Comment? @relation("CommentChildren", fields: [parentId], references: [id])
children Comment[] @relation("CommentChildren")
}
Dessa forma, um comentário pode ter vários filhos ou pode ser filho de outro comentário, estabelecendo um relacionamento auto-referencial.
Com o relacionamento estabelecido, podemos agora adicionar um novo campo ao modelo Post
: o campo likes
, que será um contador de quantos likes o post recebeu. Como ainda não implementamos a autenticação do usuário, por enquanto teremos apenas um contador simples. Definimos o campo likes
como inteiro, com um valor padrão de zero. Ou seja, por padrão, um post tem 0 likes.
model Post {
id Int @id @default(autoincrement())
cover String
title String
slug String @unique
body String
markdown String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int
author User @relation(fields: [authorId], references: [id])
comments Comment[]
likes Int @default(0)
}
Após evoluirmos nosso esquema, vamos ao terminal, paramos a execução do npm run dev
e solicitaremos ao Prisma que execute as migrações utilizando o comando npx prisma migrate dev --name
seguido do nome da migração. O terminal sugere o nome comments-and-likes
, vamos confirmar.
npx prisma migrate dev --name comments-and-likes
O Prisma então se conectará ao Postgres e atualizará o banco de dados, criando as tabelas e colunas necessárias.
Com a migração concluída e o esquema atualizado, estamos prontos para persistir os dados e avançar no desenvolvimento do CodeConnect.
Avançamos com nosso banco de dados, utilizando o Prisma como ORM, e evoluímos nosso modelo, executando os comandos necessários para migrar e atualizar nosso banco de dados. Agora, podemos prosseguir com o lado do Next.js, começando pela criação de nossa primeira action do servidor.
Para organizar nossas ações, vamos criar uma nova pasta chamada actions
dentro de src
. No VS Code, clicamos com o botão direito e selecionamos "New Folder", nomeando-a como actions
. Dentro dessa pasta, criaremos um novo arquivo chamado index.js
para conter nossas actions.
No contexto da nossa primeira action, vamos considerar um cenário envolvendo o incremento de likes. Para isso, vamos criar uma função assíncrona chamada incrementThumbsUp()
, que receberá um objeto post
como parâmetro, representando o post que desejamos incrementar o número de likes.
export async function incrementThumbsUp(post) {
}
Dentro da função, utilizaremos operações assíncronas do Prisma para atualizar o número de likes no banco de dados. Primeiro, importamos o DB
do Prisma. Em seguida, usamos o método update
para atualizar o post específico com base no id
fornecido. Começamos definindo a cláusula where
, garantindo que estamos atualizando apenas o post desejado.
import db from "../..prima/db";
export async function incrementThumbsUp(post) {
await db.post.update({
whre: {
id: post.id
},
})
}
Em seguida, especificamos os dados que queremos atualizar, neste caso, o objeto data
, que é esperado pelo próprio Prisma, e o campo likes
. Utilizamos o operador de incremento increment
para incrementar o número de likes em um.
import db from "../..prima/db";
export async function incrementThumbsUp(post) {
await db.post.update({
whre: {
id: post.id
},
data: {
likes: {
increment: 1
}
}
})
}
Dessa forma, nossa função recebe um post
como parâmetro e atualiza o número de likes no banco de dados. Vale ressaltar que o id
corresponde ao id do post específico, e data
, aos dados que serão atualizados, no caso, a coluna de likes
. Essa é uma server action que o Next.js saberá como integrar com nossos componentes."
Para deixar claro que estamos lidando com ações do lado do servidor, podemos adicionar um comentário no início de nosso arquivo, na linha 1, antes de qualquer importação. Podemos usar aspas simples ou duplas para indicar 'use server' ou simplesmente 'server'.
'use server'
import db from "../..prima/db";
export async function incrementThumbsUp(post) {
await db.post.update({
whre: {
id: post.id
},
data: {
likes: {
increment: 1
}
}
})
}
Isso informa explicitamente ao Next.js que essas são server actions. Agora, com o banco de dados atualizado e uma action que o atualiza, precisamos conectar esses elementos para indicar quando o componente chamará a ação. Antes, porém, há alguns ajustes que precisamos fazer no CodeConnect.
Vamos começar implementando um link para a página inicial no componente aside
. Isso nos poupará de ter que digitar a URL manualmente sempre que quisermos voltar para a página inicial.
No VS Code, dentro da pasta components/Aside
, vamos acessar o arquivo index.jsx
. Nele, temos uma imagem no componente aside
. Vamos envolver essa imagem da logo em um componente <Link>
do Next.js, definindo o href
como a raiz do projeto.
Neste ponto, precisamos lembrar de também fazer a importação do componente <Link>
.
import Image from 'next/image'
import styles from './aside.module.css'
import logo from './logo.png'
import Link from 'next/link'
export const Aside = () => {
return (
<aside className={styles.aside}>
{/* <img src="/logo.png" alt="Logo da Code Connect" /> */}
<Link href="/">
<Image src={logo} alt="Logo da Code Connect"/>
</Link>
</aside>
)
}
Ao salvar, voltar ao navegador e clicar na logo "Code Connect", somos redirecionados para a home. Está funcionando como esperávamos.
Além disso, precisamos adicionar ações clicáveis dentro do componente CardPost
. Atualmente, todo o CardPost
é um link, o que não é ideal. De volta ao VS Code, dentro da pasta components/CardPost
, acessaremos o arquivo index.jsx
.
Nele, dentro de CardPost
, temos um componente <Link>
que envolve o restante do código. Vamos corrigir isso da seguinte forma: primeiro, copiaremos o href
, por volta da linha 8, e após a tag <p>
, que contém post.body
, por volta da linha 21, chamaremos o componente <Link>
do Next.js. Ele conterá o texto "Ver detalhes" e o href
que copiamos anteriormente, que serve para redirecionar para os detalhes do post.
<p>{post.body}</p>
<Link href={`/posts/${post.slug}`}>Ver detalhes</Link>
Feito isso, podemos remover a tag <Link>
com o href
que englobava o código.
Depois de fazer esses ajustes, podemos salvar e verificar no navegador se os links estão funcionando corretamente. Note que o texto "Ver detalhes" aparece em cada card como um link clicável, então está funcionando conforme o esperado.
Agora, podemos nos concentrar nas próximas ações necessárias, como implementar a contagem de likes
e os comentários em cada post. Nos encontramos na próxima aula!
O curso Next.js: construindo com Server Actions possui 98 minutos de vídeos, em um total de 42 atividades. Gostou? Conheça nossos outros cursos de Next.JS em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.