Project Templates
Social Network
Como Construir uma Rede Social com React
122 min
introdução neste tutorial, você irá construir o back4gram, uma plataforma de rede social completa semelhante ao instagram o back4gram permite que os usuários criem contas, compartilhem postagens com imagens, interajam por meio de curtidas e comentários, busquem conteúdo e se comuniquem via mensagens em tempo real este projeto demonstra como combinar as poderosas capacidades de frontend do react com os robustos serviços de backend do back4app para criar uma aplicação social moderna e rica em recursos o react é a escolha perfeita para o frontend de uma rede social devido à sua arquitetura baseada em componentes, que permite elementos de ui reutilizáveis e renderização eficiente enquanto isso, o back4app fornece um backend gerenciado do parse server que lida com autenticação de usuários, armazenamento de dados, uploads de arquivos e recursos em tempo real, sem exigir que você construa uma infraestrutura de servidor complexa do zero ao completar este tutorial, você construirá uma rede social completa com autenticação de usuário (cadastro, login, redefinição de senha) gerenciamento de perfil criação de postagens com uploads de imagem interações sociais (curtidas, comentários) mensagens em tempo real com indicadores de digitação funcionalidade de busca de conteúdo configurações e preferências do usuário ao longo do caminho, você adquirirá habilidades valiosas em desenvolvimento em react com hooks e contexto desenvolvimento de ui com chakra ui integração com parse server através do back4app gerenciamento de dados em tempo real com livequery fluxos de autenticação de usuário manipulação de uploads de arquivos implementação de design responsivo se você está procurando lançar sua própria plataforma social ou simplesmente quer entender como as redes sociais modernas são construídas, este tutorial fornecerá o conhecimento e a experiência prática para alcançar seus objetivos a qualquer momento, você pode acessar o código completo em github pré requisitos para completar este tutorial, você precisará uma conta back4app inscreva se para uma conta gratuita em back4app com https //www back4app com/ você usará isso para criar e gerenciar seus serviços de backend node js e npm instalados na sua máquina local instale o node js (versão 14 x ou posterior) e npm de nodejs org https //nodejs org/ verifique sua instalação executando node v e npm v no seu terminal compreensão básica do react familiaridade com componentes react, hooks e jsx se você precisa revisar o react, confira a documentação oficial do react https //reactjs org/docs/getting started html editor de código qualquer editor de código moderno como visual studio code, sublime text ou atom git (opcional) para controle de versão e acompanhamento do repositório recursos suplementares documentação do back4app https //www back4app com/docs/get started/welcome guia javascript do parse https //docs parseplatform org/js/guide/ documentação do chakra ui https //chakra ui com/docs/getting started documentação do react router https //reactrouter com/en/main passo 1 — configurando seu backend back4app nesta etapa, você criará um novo projeto back4app e configurará o esquema de banco de dados necessário para seu aplicativo de rede social o back4app fornece um parse server gerenciado que cuidará da autenticação de usuários, armazenamento de dados e recursos em tempo real criando um novo projeto back4app faça login na sua conta back4app e navegue até o painel clique no botão "criar um novo aplicativo" digite "back4gram" como o nome do seu aplicativo, selecione a região do servidor mais próxima e clique em "criar" uma vez que seu aplicativo é criado, você será redirecionado para o painel do aplicativo compreendendo o esquema do banco de dados antes de criar classes em seu banco de dados, vamos entender o modelo de dados necessário para nossa rede social com base nos requisitos da nossa aplicação, precisaremos das seguintes classes usuário (já existe por padrão no parse) esta classe lida com a autenticação do usuário e informações do perfil vamos estendê lo com campos adicionais como biografia e avatar postagem armazena postagens de usuários, incluindo conteúdo de texto e imagens campos conteúdo (string), autor (ponteiro para usuário), imagem (arquivo), curtidas (número), comentários (array), criadoem (data) comentário armazena comentários em postagens campos conteúdo (string), autor (ponteiro para usuário), postagem (ponteiro para postagem), criadoem (data) conversa representa uma conversa de chat entre usuários campos participantes (array de ponteiros para usuário), últimamensagem (string), atualizadoem (data) mensagem mensagens individuais dentro de uma conversa campos texto (string), remetente (ponteiro para usuário), conversa (ponteiro para conversa), criadoem (data) status de digitação (para indicadores de digitação em tempo real) rastreia quando os usuários estão digitando em uma conversa campos usuário (ponteiro para usuário), conversa (ponteiro para conversa), estádigitando (booleano) criando classes de banco de dados agora, vamos criar essas classes no seu banco de dados back4app navegue até a seção "banco de dados" no seu painel do back4app estendendo a classe user clique na classe "usuário" que já existe adicione as seguintes colunas bio (tipo string) avatar (tipo arquivo) seguidores (tipo número, padrão 0) seguinte (tipo número, padrão 0) criando a classe post clique em "criar uma turma" digite "post" como o nome da classe e selecione "criar uma classe vazia" adicione as seguintes colunas conteúdo (tipo string) autor (tipo ponteiro para usuário) imagem (tipo arquivo) curtidas (tipo número, padrão 0) comentários (tipo array) criadoem (tipo data, adicionado automaticamente) criando a classe comment clique em "criar uma turma" digite "comentário" como o nome da classe e selecione "criar uma classe vazia" adicione as seguintes colunas conteúdo (tipo string) autor (tipo ponteiro para usuário) post (tipo ponteiro para postagem) criadoem (tipo data, adicionado automaticamente) criando a classe conversation clique em "criar uma turma" digite "conversa" como o nome da classe e selecione "criar uma classe vazia" adicione as seguintes colunas participantes (tipo array) últimamensagem (tipo string) atualizadoem (tipo data, adicionado automaticamente) criando a classe message clique em "criar uma turma" digite "mensagem" como o nome da classe e selecione "criar uma classe vazia" adicione as seguintes colunas texto (tipo string) remetente (tipo ponteiro para usuário) conversa (tipo ponteiro para conversa) criadoem (tipo data, adicionado automaticamente) criando a classe typingstatus clique em "criar uma turma" digite "typingstatus" como o nome da classe e selecione "criar uma classe vazia" adicione as seguintes colunas usuário (tipo ponteiro para usuário) conversa (tipo ponteiro para conversa) estádigitando (tipo booleano) configurando permissões de classe (opcional) para garantir a segurança dos dados, precisamos configurar listas de controle de acesso (acls) apropriadas para cada classe navegue até a seção "segurança e chaves" no seu painel do back4app sob "segurança de nível de classe", configure as seguintes permissões classe de usuário acesso público de leitura habilitado (para que os usuários possam ver os perfis de outros usuários) acesso de escrita público desativado (os usuários podem apenas modificar seus próprios perfis) aula pós acesso público de leitura ativado (todos podem ver as postagens) acesso de escrita público habilitado (usuários autenticados podem criar postagens) adicione um clp para atualização/exclusão para restringir apenas ao autor comentário da classe acesso público de leitura ativado (todos podem ver os comentários) acesso de escrita público habilitado (usuários autenticados podem criar comentários) adicione um clp para atualização/exclusão para restringir apenas ao autor aula de conversação acesso público de leitura desativado (as conversas são privadas) acesso de escrita público habilitado (usuários autenticados podem criar conversas) adicione um clp para restringir o acesso de leitura/gravação aos participantes da conversa classe de mensagem acesso público de leitura desativado (as mensagens são privadas) acesso de escrita público habilitado (usuários autenticados podem enviar mensagens) adicione um clp para restringir o acesso de leitura/gravação aos participantes da conversa classe typingstatus acesso público de leitura desativado (o status de digitação é privado) acesso de escrita público habilitado (usuários autenticados podem atualizar o status de digitação) adicione um clp para restringir o acesso de leitura/gravação aos participantes da conversa configurando livequery para recursos em tempo real para habilitar recursos em tempo real, como mensagens e indicadores de digitação, precisamos configurar o livequery navegue até a seção "configurações do servidor" no seu painel do back4app em "parse server", encontre a seção "livequery" e habilite a adicione as seguintes classes para serem monitoradas pelo livequery mensagem status de digitação postagem (para atualizações em tempo real de curtidas e comentários) salve suas alterações obtendo suas chaves de aplicação você precisará das chaves da sua aplicação back4app para conectar seu frontend react ao backend navegue até a seção "configurações do app" > "segurança e chaves" anote as seguintes chaves id da aplicação chave javascript url do servidor url do servidor livequery (configuração de subdomínio, para recursos em tempo real) você usará essas chaves em sua aplicação react para inicializar o parse passo 2 — criando um projeto frontend react neste passo, você configurará um novo projeto react e o configurará para trabalhar com seu backend back4app você instalará as dependências necessárias, criará a estrutura do projeto e se conectará ao seu servidor parse configurando um novo projeto react vamos começar criando uma nova aplicação react usando o create react app, que fornece uma configuração de build moderna sem necessidade de configuração abra seu terminal e navegue até o diretório onde deseja criar seu projeto execute o seguinte comando para criar uma nova aplicação react npx create react app back4gram uma vez que o projeto é criado, navegue até o diretório do projeto cd back4gram inicie o servidor de desenvolvimento para garantir que tudo esteja funcionando npm start isso abrirá sua nova aplicação react no seu navegador em http //localhost 3000 http //localhost 3000 instalando dependências necessárias agora, vamos instalar os pacotes que precisaremos para nossa aplicação de rede social pare o servidor de desenvolvimento (pressione ctrl+c no seu terminal) instale o sdk do parse para conectar ao back4app npm install parse instale o react router para navegação npm install react router dom instale o chakra ui para nossos componentes de interface do usuário npm install @chakra ui/react @emotion/react @emotion/styled framer motion instale utilitários de ui adicionais e bibliotecas de ícones npm install react icons explicação da estrutura do projeto vamos organizar nosso projeto com uma estrutura clara crie os seguintes diretórios na src pasta mkdir p src/components/ui src/pages src/contexts src/utils aqui está para que serve cada diretório components componentes de ui reutilizáveis ui componentes básicos de ui como botões, formulários, modais outras pastas de componentes para recursos específicos (por exemplo, postagens, comentários) pages componentes de página completa que correspondem a rotas contexts provedores de contexto react para gerenciamento de estado utils funções utilitárias e auxiliares criando variáveis de ambiente para armazenar suas credenciais do back4app de forma segura, crie um env arquivo na raiz do seu projeto crie um novo arquivo chamado env local na raiz do projeto touch env local abra o arquivo e adicione suas credenciais do back4app react app parse app id=seu id de aplicacao react app parse js key=sua chave javascript react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //seu id de aplicacao back4app io substitua os valores de espaço reservado pelas suas credenciais reais do back4app da etapa 1 certifique se de adicionar env local ao seu gitignore arquivo para evitar o comprometimento de informações sensíveis configurando o sdk parse com credenciais do back4app agora, vamos configurar o sdk parse para conectar ao seu backend do back4app crie um novo arquivo src/utils/parseconfig js // src/utils/parseconfig js import parse from 'parse/dist/parse min js'; // inicializar parse parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // inicializar consultas ao vivo if (process env react app parse live query url) { parse livequeryserverurl = process env react app parse live query url; } export default parse; atualize seu src/index js arquivo para importar a configuração do parse import react from 'react'; import reactdom from 'react dom/client'; import ' /index css'; import app from ' /app'; import reportwebvitals from ' /reportwebvitals'; import ' /utils/parseconfig'; // importar configuração do parse const root = reactdom createroot(document getelementbyid('root')); root render( \<react strictmode> \<app /> \</react strictmode> ); reportwebvitals(); configurando o componente app com roteamento vamos atualizar o componente principal app para incluir roteamento e o provedor chakra ui atualizar src/app js import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; // importar páginas (vamos criar estas a seguir) import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // criar um tema personalizado const theme = extendtheme({ config { initialcolormode 'dark', usesystemcolormode false, }, colors { brand { 50 '#e5f4ff', 100 '#b8dcff', 200 '#8ac5ff', 300 '#5cadff', 400 '#2e96ff', 500 '#147dff', 600 '#0061cc', 700 '#004799', 800 '#002d66', 900 '#001433', }, }, }); function app() { return ( \<chakraprovider theme={theme}> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={\<feedpage />} /> \<route path="/profile" element={\<profilepage />} /> \<route path="/post/\ id" element={\<postdetailspage />} /> \<route path="/messages" element={\<messagespage />} /> \<route path="/search" element={\<searchpage />} /> \<route path="/settings" element={\<settingspage />} /> \</routes> \</router> \</chakraprovider> ); } export default app; criando um componente de rota protegida para proteger rotas que requerem autenticação, vamos criar um componente protectedroute primeiro, crie um authcontext para gerenciar o estado de autenticação do usuário // src/contexts/authcontext js import react, { createcontext, usestate, usecontext, useeffect } from 'react'; import parse from 'parse/dist/parse min js'; const authcontext = createcontext(); export function useauth() { return usecontext(authcontext); } export function authprovider({ children }) { const \[currentuser, setcurrentuser] = usestate(null); const \[isloading, setisloading] = usestate(true); useeffect(() => { // check if user is already logged in const checkuser = async () => { try { const user = await parse user current(); setcurrentuser(user); } catch (error) { console error('error checking current user ', error); } finally { setisloading(false); } }; checkuser(); }, \[]); // login function const login = async (username, password) => { try { const user = await parse user login(username, password); setcurrentuser(user); return user; } catch (error) { throw error; } }; // signup function const signup = async (username, email, password) => { try { const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); const result = await user signup(); setcurrentuser(result); return result; } catch (error) { throw error; } }; // logout function const logout = async () => { try { await parse user logout(); setcurrentuser(null); } catch (error) { throw error; } }; // reset password function const resetpassword = async (email) => { try { await parse user requestpasswordreset(email); } catch (error) { throw error; } }; const value = { currentuser, isloading, login, signup, logout, resetpassword, }; return \<authcontext provider value={value}>{children}\</authcontext provider>; } agora, crie o componente protectedroute // src/components/protectedroute js import react from 'react'; import { navigate } from 'react router dom'; import { useauth } from ' /contexts/authcontext'; import { flex, spinner } from '@chakra ui/react'; function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } export default protectedroute; atualize o componente app para usar o authprovider e protectedroute // src/app js (updated) import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; import { authprovider } from ' /contexts/authcontext'; import protectedroute from ' /components/protectedroute'; // import pages import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // theme configuration (same as before) const theme = extendtheme({ // theme configuration }); function app() { return ( \<chakraprovider theme={theme}> \<authprovider> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> \<route path="/profile" element={ \<protectedroute> \<profilepage /> \</protectedroute> } /> \<route path="/post/\ id" element={ \<protectedroute> \<postdetailspage /> \</protectedroute> } /> \<route path="/messages" element={ \<protectedroute> \<messagespage /> \</protectedroute> } /> \<route path="/search" element={ \<protectedroute> \<searchpage /> \</protectedroute> } /> \<route path="/settings" element={ \<protectedroute> \<settingspage /> \</protectedroute> } /> \</routes> \</router> \</authprovider> \</chakraprovider> ); } export default app; criando uma página de destino básica vamos criar uma página de destino simples para testar nossa configuração // src/pages/landingpage js import react from 'react'; import { box, heading, text, button, vstack, flex } from '@chakra ui/react'; import { link as routerlink } from 'react router dom'; function landingpage() { return ( \<box bg="gray 900" minh="100vh" color="white"> \<flex direction="column" align="center" justify="center" textalign="center" py={20} \> \<heading size="2xl" mb={4}> back4gram \</heading> \<text fontsize="lg" maxw="600px" mb={8}> join a vibrant community where your voice matters share stories, ideas, and moments with friends and the world \</text> \<vstack spacing={4} maxw="md" mx="auto"> \<button as={routerlink} to="/signup" colorscheme="brand" size="lg" w="full" \> create account \</button> \<button as={routerlink} to="/login" variant="outline" size="lg" w="full" \> log in \</button> \</vstack> \</flex> \</box> ); } export default landingpage; testando sua configuração agora que você configurou a estrutura básica da sua aplicação react e a conectou ao back4app, vamos testá la inicie o servidor de desenvolvimento npm start abra seu navegador e navegue até http //localhost 3000 http //localhost 3000 você deve ver a página inicial com botões para se inscrever ou fazer login verifique o console do seu navegador para garantir que não há erros relacionados à inicialização do parse passo 3 — implementando recursos de autenticação neste passo, implementaremos recursos de autenticação de usuários para nossa aplicação de rede social usando o parse server do back4app vamos examinar como o sistema de autenticação do parse funciona e implementar funcionalidades de login, inscrição e redefinição de senha entendendo o sistema de autenticação de usuários do parse o parse server do back4app fornece um sistema abrangente de gerenciamento de usuários através da parse user classe vamos entender como a autenticação do parse funciona em nossa aplicação a classe parse user o parse user é uma subclasse especial de parse object projetada especificamente para gerenciamento de usuários em nosso aplicativo back4gram, usamos para armazenar credenciais de usuário (nome de usuário, e mail, senha) gerenciar o estado de autenticação gerenciar tokens de sessão automaticamente olhando para nossa implementação, podemos ver como interagimos com a classe parse user // from signuppage js const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); este código cria um novo objeto parse user, define os campos obrigatórios e chama o método signup() para registrar o usuário no back4app fluxo de autenticação no parse vamos examinar como a autenticação funciona em nossa aplicação fluxo de registro na nossa signuppage js, coletamos nome de usuário, e mail e senha validamos os dados de entrada (verificando campos vazios, formato de e mail válido, comprimento da senha) criamos um novo objeto parse user e definimos as credenciais chamamos signup() que envia os dados para o back4app o parse faz o hash da senha antes de armazená la ao sucesso, o usuário é automaticamente conectado com um token de sessão fluxo de login na nossa loginpage js, coletamos nome de usuário e senha chamamos parse user login() com essas credenciais o parse verifica as credenciais em relação aos dados armazenados se válido, o parse gera um token de sessão o token da sessão é armazenado automaticamente no armazenamento do navegador gerenciamento de sessão o parse inclui automaticamente o token da sessão em todas as requisições da api usamos parse user current() para recuperar o usuário atualmente logado as sessões persistem entre atualizações de página implementando o registro de usuário vamos examinar nosso componente signuppage para entender como o registro de usuário é implementado validação de formulário antes de enviar dados para o back4app, validamos a entrada do usuário // from signuppage js const validateform = () => { const newerrors = {}; if (!username trim()) { newerrors username = 'username is required'; } if (!email trim()) { newerrors email = 'email is required'; } else if (!/\s+@\s+\\ \s+/ test(email)) { newerrors email = 'email is invalid'; } if (!password) { newerrors password = 'password is required'; } else if (password length < 6) { newerrors password = 'password must be at least 6 characters'; } if (password !== confirmpassword) { newerrors confirmpassword = 'passwords do not match'; } seterrors(newerrors); return object keys(newerrors) length === 0; }; essa validação garante que o nome de usuário não está vazio o email é válido a senha tem pelo menos 6 caracteres a senha e a confirmação coincidem tratando erros de registro nosso manipulador de inscrição inclui tratamento de erros específicos do parse // from signuppage js try { // create a new user const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); toaster create({ title 'success', description 'account created successfully!', type 'success', }); navigate('/feed'); } catch (error) { toaster create({ title 'signup failed', description error message, type 'error', }); // handle specific parse errors if (error code === 202) { seterrors({ errors, username 'username already taken'}); } else if (error code === 203) { seterrors({ errors, email 'email already in use'}); } } back4app retorna códigos de erro específicos que podemos usar para fornecer feedback útil ao usuário código 202 nome de usuário já em uso código 203 email já em uso o código completo para registro/inscrição de usuário pode ser encontrado aqui implementando login de usuário nosso componente loginpage lida com a autenticação do usuário usando parse user login() formulário de login o formulário de login coleta nome de usuário e senha // from loginpage js \<form onsubmit={handlelogin}> \<vstack spacing={4}> \<field label="username"> \<input type="text" value={username} onchange={(e) => setusername(e target value)} placeholder="your username" required /> \</field> \<field label="password" errortext={error} \> \<input type="password" value={password} onchange={(e) => setpassword(e target value)} placeholder="your password" required /> \</field> \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> \<button colorscheme="blue" width="full" type="submit" loading={isloading} \> log in \</button> \</vstack> \</form> verificação de sessão como mostrado anteriormente, verificamos se há uma sessão existente quando a página de login é carregada // from loginpage js useeffect(() => { const checkcurrentuser = async () => { try { const user = await parse user current(); if (user) { setcurrentuser(user); navigate('/feed'); } } catch (error) { console error('error checking current user ', error); } }; checkcurrentuser(); }, \[navigate]); este é um recurso chave do parse ele gerencia automaticamente o token de sessão no armazenamento do navegador, permitindo que verifiquemos facilmente se um usuário já está logado implementando a redefinição de senha back4app fornece um fluxo de redefinição de senha embutido em nossa aplicação, vinculamos a uma página de redefinição de senha a partir do formulário de login // from loginpage js \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> o processo de redefinição de senha no back4app funciona da seguinte forma o usuário solicita uma redefinição de senha com seu e mail parse envia um link especial de redefinição para o e mail do usuário o usuário clica no link e define uma nova senha parse atualiza o hash da senha no banco de dados para implementar isso em nossa aplicação, usaríamos // example password reset implementation try { await parse user requestpasswordreset(email); // show success message } catch (error) { // handle error } protegendo rotas para usuários autenticados para proteger certas rotas em nossa aplicação, usamos um componente protectedroute que verifica se um usuário está autenticado // from protectedroute js function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } este componente usa nosso authcontext para verificar se um usuário está logado mostra um carregador enquanto verifica redireciona para a página de login se nenhum usuário for encontrado renderiza o conteúdo protegido se um usuário estiver autenticado usamos este componente em nossa configuração de roteamento // from app js \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> configuração de autenticação back4app back4app fornece várias opções de configuração para autenticação no painel verificação de email você pode exigir verificação de email antes que os usuários possam fazer login configure isso em "configurações do servidor" > "parse server" > "autenticação de usuário" política de senha defina o comprimento mínimo da senha e os requisitos de complexidade configure isso em "configurações do servidor" > "parse server" > "autenticação de usuário" duração da sessão controle quanto tempo as sessões de usuário permanecem válidas configure isso em "configurações do servidor" > "parse server" > "configuração da sessão" modelos de email personalize os emails de verificação e redefinição de senha configure isso em "configurações do app" > "modelos de email" testando sua implementação de autenticação para garantir que seu sistema de autenticação funcione corretamente testar registro de usuário tente registrar com credenciais válidas tente registrar com um nome de usuário existente (deve mostrar um erro) verifique se o usuário aparece no seu painel do back4app na classe " user" testar login de usuário tente fazer login com credenciais corretas (deve redirecionar para o feed) tente fazer login com credenciais incorretas (deve mostrar um erro) testar persistência de sessão faça login e atualize a página (deve permanecer logado) feche e reabra o navegador (deve permanecer logado se a sessão for válida) testar rotas protegidas tente acessar /feed quando estiver desconectado (deve redirecionar para login) tente acessar /feed quando estiver conectado (deve mostrar a página do feed) o código para o componente de login pode ser encontrado aqui passo 4 — desenvolvendo a funcionalidade do feed neste passo, você implementará o recurso central de rede social o feed é aqui que os usuários criarão postagens, visualizarão conteúdo de outros e interagirão por meio de curtidas e comentários usaremos o parse server do back4app para armazenar e recuperar postagens, lidar com uploads de arquivos para imagens e implementar atualizações em tempo real entendendo a estrutura da página de feed a página de feed em nossa aplicação tem três componentes principais uma barra lateral para navegação a área principal do feed com criação e listagem de postagens uma seção de tendências (em telas maiores) vamos examinar como isso é implementado em nosso feedpage js // from feedpage js main structure function feedpage() { // state and hooks return ( \<flex minh="100vh" bg="gray 800" color="white"> {/ left sidebar (navigation) /} \<box w={\['0px', '200px']} bg="gray 900" p={4} display={\['none', 'block']}> {/ navigation links /} \</box> {/ main content (feed) /} \<box flex="1" p={4} overflowy="auto"> {/ post creation form /} {/ posts list /} \</box> {/ right sidebar (trending) /} \<box w={\['0px', '250px']} bg="gray 700" p={4} display={\['none', 'block']}> {/ trending content /} \</box> \</flex> ); } este layout responsivo se adapta a diferentes tamanhos de tela, ocultando as barras laterais em dispositivos móveis criando a classe post no back4app antes de implementar o frontend, vamos garantir que nosso banco de dados back4app esteja devidamente configurado para posts a classe post deve ter os seguintes campos conteúdo (string) o conteúdo de texto da postagem imagem (arquivo) anexo de imagem opcional autor (ponteiro para usuário) o usuário que criou a postagem curtidas (número) contagem de curtidas na postagem curtidopor (array) array de ids de usuários que curtiram a postagem criadoem (data) adicionado automaticamente pelo parse defina permissões apropriadas para a classe post acesso público de leitura todos devem ser capazes de ler postagens acesso público de escrita usuários autenticados devem ser capazes de criar postagens permissões de atualização/exclusão apenas o autor deve ser capaz de modificar suas postagens implementando a criação de postagens vamos examinar como a criação de postagens é implementada em nosso componente feedpage // from feedpage js post creation state const \[postcontent, setpostcontent] = usestate(''); const \[postimage, setpostimage] = usestate(null); const \[imagepreview, setimagepreview] = usestate(''); const \[issubmitting, setissubmitting] = usestate(false); // image selection handler const handleimageselect = (e) => { if (e target files && e target files\[0]) { const file = e target files\[0]; setpostimage(file); // create preview url const previewurl = url createobjecturl(file); setimagepreview(previewurl); } }; // post submission handler const handlesubmitpost = async () => { if (!postcontent trim() && !postimage) { toaster create({ title 'error', description 'please add some text or an image to your post', type 'error', }); return; } setissubmitting(true); try { // create a new post object const post = parse object extend('post'); const newpost = new post(); // set post content newpost set('content', postcontent); newpost set('author', parse user current()); newpost set('likes', 0); newpost set('likedby', \[]); // if there's an image, upload it if (postimage) { const parsefile = new parse file(postimage name, postimage); await parsefile save(); newpost set('image', parsefile); } // save the post await newpost save(); // reset form setpostcontent(''); setpostimage(null); setimagepreview(''); // refresh posts fetchposts(); toaster create({ title 'success', description 'your post has been published!', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setissubmitting(false); } }; pontos chave sobre a criação de postagens manipulação de arquivos no parse parse file é usado para fazer upload de imagens para o armazenamento do back4app o arquivo é salvo primeiro, depois anexado ao objeto de postagem back4app gerencia automaticamente o armazenamento de arquivos e gera urls criando objetos parse estendemos a classe 'post' com parse object extend('post') criamos uma nova instância com new post() definimos propriedades usando o set() método salvamos o objeto no back4app com save() associação de usuário associamos a postagem com o usuário atual usando parse user current() isso cria uma relação de ponteiro no banco de dados a interface do formulário de criação de postagens se parece com isso {/ post creation form /} \<box mb={6} bg="gray 700" p={4} borderradius="md"> \<vstack align="stretch" spacing={4}> \<textarea placeholder="what's on your mind?" value={postcontent} onchange={(e) => setpostcontent(e target value)} minh="100px" /> {imagepreview && ( \<box position="relative"> \<image src={imagepreview} maxh="200px" borderradius="md" /> \<iconbutton icon={\<closeicon />} size="sm" position="absolute" top="2" right="2" onclick={() => { setpostimage(null); setimagepreview(''); }} /> \</box> )} \<hstack> \<button lefticon={\<attachmenticon />} onclick={() => document getelementbyid('image upload') click()} \> add image \</button> \<input id="image upload" type="file" accept="image/ " onchange={handleimageselect} display="none" /> \<button colorscheme="blue" ml="auto" isloading={issubmitting} onclick={handlesubmitpost} disabled={(!postcontent trim() && !postimage) || issubmitting} \> post \</button> \</hstack> \</vstack> \</box> buscando e exibindo posts now let's look at how we fetch and display posts from back4app // from feedpage js fetching posts const \[posts, setposts] = usestate(\[]); const \[isloading, setisloading] = usestate(true); const \[page, setpage] = usestate(0); const \[hasmore, sethasmore] = usestate(true); const postsperpage = 10; const fetchposts = async (loadmore = false) => { try { const currentpage = loadmore ? page + 1 0; // create a query for the post class const post = parse object extend('post'); const query = new parse query(post); // include the author object (pointer) query include('author'); // sort by creation date, newest first query descending('createdat'); // pagination query limit(postsperpage); query skip(currentpage postsperpage); // execute the query const results = await query find(); // process the results const fetchedposts = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); // update state if (loadmore) { setposts(prevposts => \[ prevposts, fetchedposts]); setpage(currentpage); } else { setposts(fetchedposts); setpage(0); } // check if there are more posts to load sethasmore(results length === postsperpage); } catch (error) { console error('error fetching posts ', error); toaster create({ title 'error', description 'failed to load posts please try again ', type 'error', }); } finally { setisloading(false); } }; // load posts when component mounts useeffect(() => { fetchposts(); }, \[]); pontos chave sobre a busca de postagens consultas parse criamos uma consulta com new parse query(post) incluímos objetos relacionados com query include('author') ordenamos com query descending('createdat') paginação com query limit() e query skip() executamos a consulta com query find() processando resultados objetos parse têm um get() método para acessar propriedades para campos de arquivo, usamos file url() para obter a url transformamos objetos parse em objetos javascript simples para o estado do react paginação implementamos a funcionalidade "carregar mais" com rastreamento de página verificamos se há mais postagens para carregar antes de fazer solicitações adicionais os posts são exibidos em uma lista {/ posts list /} {isloading ? ( \<center py={10}> \<spinner size="xl" /> \</center> ) posts length > 0 ? ( \<vstack spacing={4} align="stretch"> {posts map(post => ( \<box key={post id} p={4} bg="gray 700" borderradius="md"> {/ post header with author info /} \<hstack mb={2}> \<avatar root size="sm"> \<avatar fallback name={post author username} /> \<avatar image src={post author avatar} /> \</avatar root> \<text fontweight="bold">{post author username}\</text> \<text fontsize="sm" color="gray 400">• {formatdate(post createdat)}\</text> \</hstack> {/ post content /} \<text mb={4}>{post content}\</text> {/ post image if any /} {post image && ( \<image src={post image} maxh="400px" borderradius="md" mb={4} /> )} {/ post actions /} \<hstack> \<button variant="ghost" lefticon={\<likeicon />} onclick={() => handlelikepost(post id, post likedby)} color={post likedby includes(currentuser id) ? "blue 400" "white"} \> {post likes} likes \</button> \<button variant="ghost" lefticon={\<commenticon />} as={routerlink} to={`/post/${post id}`} \> comments \</button> \</hstack> \</box> ))} {/ load more button /} {hasmore && ( \<button onclick={() => fetchposts(true)} isloading={isloadingmore}> load more \</button> )} \</vstack> ) ( \<center py={10}> \<text>no posts yet be the first to post!\</text> \</center> )} implementando funcionalidade de curtida vamos examinar como a funcionalidade de curtir é implementada // from feedpage js like functionality const handlelikepost = async (postid, likedby) => { try { const currentuserid = parse user current() id; const isliked = likedby includes(currentuserid); // get the post object const post = parse object extend('post'); const query = new parse query(post); const post = await query get(postid); // update likes count and likedby array if (isliked) { // unlike remove user from likedby and decrement likes post set('likedby', likedby filter(id => id !== currentuserid)); post set('likes', (post get('likes') || 1) 1); } else { // like add user to likedby and increment likes post set('likedby', \[ likedby, currentuserid]); post set('likes', (post get('likes') || 0) + 1); } // save the updated post await post save(); // update local state setposts(prevposts => prevposts map(p => p id === postid ? { p, likes isliked ? p likes 1 p likes + 1, likedby isliked ? p likedby filter(id => id !== currentuserid) \[ p likedby, currentuserid] } p ) ); } catch (error) { console error('error liking post ', error); toaster create({ title 'error', description 'failed to like post please try again ', type 'error', }); } }; pontos chave sobre a funcionalidade de curtidas atualizações otimistas atualizamos a interface do usuário imediatamente antes que o servidor confirme a alteração isso faz com que o aplicativo pareça mais responsivo atualizações de objetos parse buscamos a postagem específica com query get(postid) modificamos suas propriedades com post set() salvamos as alterações com post save() rastreamento de curtidas mantemos tanto uma contagem ( curtidas ) quanto uma lista de usuários ( curtiram ) isso nos permite mostrar contagens precisas e determinar se o usuário atual curtiu uma postagem implementando atualizações em tempo real com livequery (opcional) para fazer a atualização do feed em tempo real quando novas postagens são criadas, podemos usar o parse livequery // from feedpage js livequery setup const livequerysubscription = useref(null); useeffect(() => { // set up livequery for real time updates const setuplivequery = async () => { try { const post = parse object extend('post'); const query = new parse query(post); // subscribe to new posts livequerysubscription current = await query subscribe(); // when a new post is created livequerysubscription current on('create', (post) => { // only add to feed if it's not already there setposts(prevposts => { if (prevposts some(p => p id === post id)) return prevposts; const newpost = { id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } }; return \[newpost, prevposts]; }); }); // when a post is updated (e g , liked) livequerysubscription current on('update', (post) => { setposts(prevposts => prevposts map(p => p id === post id ? { p, content post get('content'), image post get('image') ? post get('image') url() p image, likes post get('likes') || 0, likedby post get('likedby') || \[] } p ) ); }); } catch (error) { console error('error setting up livequery ', error); } }; setuplivequery(); // clean up subscription when component unmounts return () => { if (livequerysubscription current) { livequerysubscription current unsubscribe(); } }; }, \[]); pontos chave sobre o livequery configuração de assinatura criamos uma consulta e nos inscrevemos nela com query subscribe() isso estabelece uma conexão websocket com o servidor livequery do back4app manipulação de eventos ouvimos eventos de 'criação' quando novas postagens são criadas ouvimos eventos de 'atualização' quando as postagens são modificadas atualizamos nosso estado local de acordo limpeza nós nos desinscrevemos quando o componente é desmontado para evitar vazamentos de memória otimização do carregamento de postagens com paginação já implementamos a paginação básica com o botão "carregar mais" vamos aprimorá la com rolagem infinita // from feedpage js infinite scrolling const \[isloadingmore, setisloadingmore] = usestate(false); const feedref = useref(null); // intersection observer for infinite scrolling useeffect(() => { if (!hasmore) return; const observer = new intersectionobserver( (entries) => { if (entries\[0] isintersecting && !isloading && !isloadingmore) { loadmoreposts(); } }, { threshold 0 5 } ); const loadmoretrigger = document getelementbyid('load more trigger'); if (loadmoretrigger) { observer observe(loadmoretrigger); } return () => { if (loadmoretrigger) { observer unobserve(loadmoretrigger); } }; }, \[hasmore, isloading, isloadingmore]); const loadmoreposts = async () => { if (!hasmore || isloadingmore) return; setisloadingmore(true); try { await fetchposts(true); } finally { setisloadingmore(false); } }; e adicione isso ao final da lista de posts {/ infinite scroll trigger /} {hasmore && ( \<box id="load more trigger" h="20px" /> )} pontos chave sobre rolagem infinita intersection observer usamos a api intersection observer para detectar quando o usuário rola até o fundo quando o elemento de gatilho se torna visível, carregamos mais postagens estados de carregamento acompanhamos estados de carregamento separados para o carregamento inicial e "carregar mais" isso evita múltiplas solicitações simultâneas considerações de desempenho carregamos apenas um número fixo de postagens por vez (paginação) verificamos se há mais postagens para carregar antes de fazer solicitações adicionais otimização de desempenho back4app para otimizar o desempenho ao trabalhar com back4app use índices adicione índices aos campos frequentemente consultados no seu painel do back4app para a classe post, adicione índices em 'createdat' e 'author' consultas seletivas use query select() para buscar apenas os campos que você precisa isso reduz a transferência de dados e melhora o desempenho otimização de contagem em vez de buscar todos os posts para contá los, use query count() isso é mais eficiente para determinar contagens totais passo 6 — adicionando interações sociais nesta etapa, vamos aprimorar nossa rede social implementando recursos chave de interação social comentários em postagens, perfis de usuários e configurações de usuários vamos nos concentrar em como esses recursos interagem com o backend do back4app e os mecanismos que os fazem funcionar implementando comentários em postagens comentários são um recurso fundamental de interação social que requer modelagem de dados adequada no back4app vamos examinar como nosso aplicativo interage com o parse server para implementar comentários modelo de dados do back4app para comentários no back4app, os comentários são implementados como uma classe separada com relacionamentos tanto com usuários quanto com postagens estrutura da classe comentário conteúdo (string) o conteúdo textual do comentário autor (ponteiro para usuário) aponta para o usuário que criou o comentário postagem (ponteiro para postagem) aponta para a postagem que está sendo comentada createdat (data) gerenciado automaticamente pelo parse tipos de relacionamento usuário → comentários um para muitos (um usuário pode criar muitos comentários) postagem → comentários um para muitos (uma postagem pode ter muitos comentários) buscando comentários do back4app nossa postdetailspage usa consultas parse para buscar comentários de um post específico // from postdetailspage js comment fetching const fetchcomments = async () => { try { // create a query on the comment class const comment = parse object extend('comment'); const query = new parse query(comment); // find comments for this specific post using a pointer equality constraint query equalto('post', postobject); // include the author information query include('author'); // sort by creation date (newest first) query descending('createdat'); // execute the query const results = await query find(); // transform parse objects to plain objects for react state const commentslist = results map(comment => ({ id comment id, content comment get('content'), createdat comment get('createdat'), author { id comment get('author') id, username comment get('author') get('username'), avatar comment get('author') get('avatar') ? comment get('author') get('avatar') url() null } })); setcomments(commentslist); } catch (error) { console error('error fetching comments ', error); toaster create({ title 'error', description 'failed to load comments', type 'error', }); } }; mecanismos chave do back4app parse object extend() cria uma referência à classe comment no back4app query equalto() cria uma restrição para encontrar apenas comentários de um post específico query include() realiza uma operação semelhante a um join para buscar objetos relacionados em uma única consulta query descending() ordena os resultados por um campo específico criando comentários no back4app quando um usuário adiciona um comentário, criamos um novo objeto parse e estabelecemos as relações // from postdetailspage js adding a comment const handleaddcomment = async (e) => { e preventdefault(); if (!newcomment trim()) { return; } setiscommenting(true); try { // create a new comment object in back4app const comment = parse object extend('comment'); const comment = new comment(); // set comment data and relationships comment set('content', newcomment); comment set('author', parse user current()); // pointer to current user comment set('post', postobject); // pointer to current post // save the comment to back4app await comment save(); // clear the input setnewcomment(''); // refresh comments fetchcomments(); toaster create({ title 'success', description 'your comment has been added', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setiscommenting(false); } }; mecanismos principais do back4app new comment() cria uma nova instância da classe comentário comment set() define propriedades no objeto parse, incluindo ponteiros para objetos relacionados comment save() envia o objeto para o back4app para armazenamento parse user current() obtém o usuário autenticado atualmente para estabelecer o relacionamento do autor segurança do back4app para comentários para proteger adequadamente os comentários no back4app configurar permissões de nível de classe (clps) ler público (todos podem ler comentários) escrever apenas usuários autenticados (apenas usuários logados podem comentar) atualizar/excluir somente o criador (apenas o autor do comentário pode modificar ou excluir) configure essas permissões no seu painel do back4app { "find" { " " true }, "get" { " " true }, "create" { " " true }, "update" { "requiresauthentication" true }, "delete" { "requiresauthentication" true }, "addfield" { "requiresauthentication" true } } passo 7 construindo perfis de usuário com back4app os perfis de usuário em nossa aplicação aproveitam a classe de usuário embutida do parse com campos personalizados vamos examinar como o profilepage js interage com o back4app extensões da classe de usuário do back4app a classe de usuário do parse é estendida com campos adicionais para nossa rede social campos personalizados de usuário avatar (arquivo) foto de perfil armazenada no armazenamento de arquivos do back4app bio (string) biografia do usuário website (string) url do site do usuário displayname (string) nome de exibição do usuário buscando dados do usuário e postagens nossa profilepage busca tanto os dados do usuário quanto as postagens do usuário // from profilepage js profile data fetching const fetchuserdata = async () => { try { // get current user from parse session const currentuser = await parse user current(); if (!currentuser) { navigate('/login'); return; } setuser(currentuser); // create a query to find posts by this user const post = parse object extend('post'); const query = new parse query(post); query equalto('author', currentuser); query include('author'); query descending('createdat'); const results = await query find(); // transform parse objects to plain objects const postslist = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setposts(postslist); setstats(prevstats => ({ prevstats, posts postslist length })); } catch (error) { console error('error fetching user data ', error); toaster create({ title 'error', description 'failed to load profile data', type 'error', }); } finally { setisloading(false); } }; principais mecanismos do back4app parse user current() recupera o usuário autenticado do token da sessão query equalto('author', currentuser) cria uma restrição de igualdade de ponteiro para encontrar postagens do usuário atual post get('image') url() acessa a url de um objeto de arquivo parse armazenado no back4app implementando configurações do usuário a página de configurações permite que os usuários atualizem suas informações de perfil e gerenciem as configurações da conta vamos examinar como ela interage com o back4app // from settingspage js user settings implementation function settingspage() { const \[privacysettings, setprivacysettings] = usestate({ profilevisibility 'public', postprivacy 'friends' }); const \[twofactorauth, settwofactorauth] = usestate(false); const \[isopen, setisopen] = usestate(false); const cancelref = useref(); // save user settings to back4app const savesettings = async (settingstype, settingsdata) => { try { const currentuser = await parse user current(); if (!currentuser) { toaster create({ title 'error', description 'you must be logged in to save settings', type 'error', }); return; } // update the appropriate settings based on type switch (settingstype) { case 'privacy' currentuser set('privacysettings', settingsdata); break; case 'security' currentuser set('securitysettings', settingsdata); break; case 'notifications' currentuser set('notificationsettings', settingsdata); break; default break; } // save the user object await currentuser save(); toaster create({ title 'success', description 'your settings have been saved', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } }; return ( \<box maxw="800px" mx="auto" p={4}> \<heading mb={6}>account settings\</heading> \<tabs root defaultvalue="profile"> \<tabs list> \<tabs trigger value="profile">profile\</tabs trigger> \<tabs trigger value="privacy">privacy\</tabs trigger> \<tabs trigger value="security">security\</tabs trigger> \<tabs trigger value="notifications">notifications\</tabs trigger> \<tabs indicator /> \</tabs list> {/ settings tabs content /} {/ /} \</tabs root> {/ account deactivation dialog /} \<dialog root open={isopen} onopenchange={setisopen}> {/ /} \</dialog root> \</box> ); } mecanismos principais do back4app parse user current() obtém o usuário atual para atualizar suas configurações currentuser set() atualiza as propriedades do usuário no objeto parse user currentuser save() persiste as alterações no back4app esquema de configurações do usuário do back4app para implementar configurações no back4app adicione estes campos à classe user privacysettings (objeto) objeto json contendo preferências de privacidade securitysettings (objeto) objeto json contendo configurações de segurança notificationsettings (objeto) objeto json contendo preferências de notificação exemplo de esquema para esses objetos // privacysettings { "profilevisibility" "public", // ou "friends" ou "private" "postprivacy" "friends", // ou "public" ou "private" "showactivity" true } // securitysettings { "twofactorauth" false, "loginalerts" true } // notificationsettings { "likes" true, "comments" true, "follows" true, "messages" true } funções do back4app cloud para interações sociais para interações sociais mais complexas, você pode implementar funções em nuvem no back4app por exemplo, para rastrear notificações de comentários // example cloud function for comment notifications parse cloud aftersave("comment", async (request) => { // only run for new comments, not updates if (request original) return; const comment = request object; const post = comment get("post"); const commenter = request user; // skip if user is commenting on their own post const postquery = new parse query("post"); const fullpost = await postquery get(post id, { usemasterkey true }); const postauthor = fullpost get("author"); if (postauthor id === commenter id) return; // create a notification const notification = parse object extend("notification"); const notification = new notification(); notification set("type", "comment"); notification set("fromuser", commenter); notification set("touser", postauthor); notification set("post", post); notification set("read", false); await notification save(null, { usemasterkey true }); }); para implementar isso vá para o seu painel do back4app navegue até "cloud code" > "cloud functions" crie uma nova função com o código acima implante a função passo 8 — construindo mensagens em tempo real neste passo, implementaremos a funcionalidade de mensagens em tempo real usando o recurso livequery do back4app isso permitirá que os usuários troquem mensagens instantaneamente sem atualizar a página, criando uma experiência de chat dinâmica semelhante às plataformas de mensagens populares entendendo o livequery do back4app antes de mergulhar na implementação, vamos entender como funciona o livequery do back4app o que é livequery? livequery é um recurso do parse server que permite que os clientes se inscrevam em consultas quando os objetos que correspondem a essas consultas mudam, o servidor automaticamente envia atualizações para os clientes inscritos isso cria funcionalidade em tempo real sem implementar você mesmo o manuseio complexo de websocket como o livequery funciona livequery estabelece uma conexão websocket entre o cliente e o servidor os clientes se inscrevem em consultas específicas que desejam monitorar quando os dados que correspondem a essas consultas mudam, o servidor envia eventos pelo websocket o cliente recebe esses eventos e atualiza a interface do usuário de acordo eventos livequery criar acionado quando um novo objeto que corresponde à consulta é criado atualizar acionado quando um objeto existente que corresponde à consulta é atualizado entrar acionado quando um objeto começa a corresponder à consulta sair acionado quando um objeto não corresponde mais à consulta excluir acionado quando um objeto correspondente à consulta é excluído configurando o livequery no back4app para habilitar o livequery para seu aplicativo, siga estas etapas ative seu subdomínio back4app faça login na sua conta back4app navegue até "configurações do app" > "configurações do servidor" encontre o bloco "url do servidor e consulta ao vivo" e clique em "configurações" verifique a opção "ativar seu subdomínio back4app" este subdomínio servirá como seu servidor livequery ativar livequery na mesma página de configurações, marque a opção "ativar consulta ao vivo" selecione as classes que você deseja monitorar com livequery mensagem (para mensagens de chat) status de digitação (para indicadores de digitação) conversa (para atualizações de conversa) salve suas alterações anote a url do seu servidor livequery a url do seu servidor livequery estará no formato wss\ //yourappname back4app io você precisará deste url para inicializar o cliente livequery em sua aplicação react configurando livequery em seu app react para usar livequery em sua aplicação react, você precisa inicializar um cliente livequery // from parseconfig js or app js parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // initialize live queries with your subdomain parse livequeryserverurl = process env react app parse live query url; // e g , 'wss\ //yourappname back4app io' no seu env local arquivo, certifique se de incluir react app parse live query url=wss\ //yourappname back4app io criando o modelo de dados para mensagens nosso sistema de mensagens requer duas classes principais no back4app aula de conversação participantes (array) array de ponteiros de usuário para os usuários na conversa últimamensagem (string) o conteúdo da mensagem mais recente datadaúltimamensagem (data) carimbo de data/hora da mensagem mais recente atualizadoem (data) gerenciado automaticamente pelo parse classe de mensagem conversa (ponteiro) aponta para a conversa à qual esta mensagem pertence remetente (ponteiro) aponta para o usuário que enviou a mensagem conteúdo (string) o conteúdo de texto da mensagem ler (boolean) se a mensagem foi lida criadoem (data) gerenciado automaticamente pelo parse classe typingstatus conversa (ponteiro) aponta para a conversa usuário (ponteiro) aponta para o usuário que está digitando estádigitando (booleano) se o usuário está digitando atualmente implementando a interface de mensagens vamos examinar como nossa messagespage implementa mensagens em tempo real // from messagespage js component structure function messagespage() { const \[conversations, setconversations] = usestate(\[]); const \[selectedconversation, setselectedconversation] = usestate(null); const \[messages, setmessages] = usestate(\[]); const \[newmessage, setnewmessage] = usestate(''); const \[isloading, setisloading] = usestate(true); const \[typingusers, settypingusers] = usestate(\[]); const messagesendref = useref(null); const messagesubscription = useref(null); const typingsubscription = useref(null); const conversationsubscription = useref(null); const typingtimeoutref = useref(null); // rest of the component } o componente mantém várias partes do estado conversations lista das conversas do usuário selectedconversation a conversa atualmente selecionada messages mensagens na conversa selecionada typingusers usuários digitando atualmente na conversa ele também usa refs para armazenar assinaturas de livequery e gerenciar indicadores de digitação inscrevendo se no livequery para mensagens a chave para mensagens em tempo real é assinar o livequery para mensagens na conversa atual // from messagespage js livequery subscription for messages const subscribetomessages = async (conversation) => { try { // unsubscribe from previous subscription if exists if (messagesubscription current) { messagesubscription current unsubscribe(); } // create a query for messages in this conversation const message = parse object extend('message'); const query = new parse query(message); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find messages for this conversation query equalto('conversation', conversationpointer); // include the sender information query include('sender'); // subscribe to the query const subscription = await query subscribe(); messagesubscription current = subscription; // when a new message is created subscription on('create', (message) => { // add the new message to our state setmessages(prevmessages => { // check if message already exists in our list const exists = prevmessages some(m => m id === message id); if (exists) return prevmessages; // add the new message return \[ prevmessages, { id message id, content message get('content'), createdat message get('createdat'), sender { id message get('sender') id, username message get('sender') get('username'), avatar message get('sender') get('avatar') ? message get('sender') get('avatar') url() null }, read message get('read') }]; }); // scroll to bottom when new message arrives scrolltobottom(); // mark message as read if from other user if (message get('sender') id !== parse user current() id) { markmessageasread(message); } }); } catch (error) { console error('error subscribing to messages ', error); } }; mecanismos principais do livequery criando uma consulta criamos uma consulta para mensagens na conversa atual inscrevendo se na consulta chamamos query subscribe() para começar a ouvir por mudanças tratando eventos usamos subscription on('create', callback) para tratar novas mensagens cancelando a inscrição armazenamos a referência da inscrição e cancelamos quando necessário implementando indicadores de digitação com livequery indicadores de digitação são outro recurso em tempo real implementado com livequery // from messagespage js livequery for typing indicators const subscribetotypingstatus = async (conversation) => { try { // unsubscribe from previous subscription if exists if (typingsubscription current) { typingsubscription current unsubscribe(); } // create a query for typing status in this conversation const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find typing status for this conversation query equalto('conversation', conversationpointer); // include the user information query include('user'); // subscribe to the query const subscription = await query subscribe(); typingsubscription current = subscription; // when a typing status is updated subscription on('update', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // update typing users list settypingusers(prevtypingusers => { // if user is typing, add them to the list if (istyping) { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; } else { // if user stopped typing, remove them from the list return prevtypingusers filter(u => u id !== user id); } }); }); // when a new typing status is created subscription on('create', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // only add to typing users if they are actually typing if (istyping && user id !== parse user current() id) { settypingusers(prevtypingusers => { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; }); } }); } catch (error) { console error('error subscribing to typing status ', error); } }; atualizando o status de digitação quando um usuário digita, atualizamos seu status de digitação // from messagespage js updating typing status const updatetypingstatus = async (istyping) => { if (!selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // check if a typing status already exists const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); query equalto('conversation', conversationpointer); query equalto('user', currentuser); let typingstatus = await query first(); if (typingstatus) { // update existing typing status typingstatus set('istyping', istyping); } else { // create new typing status typingstatus = new typingstatus(); typingstatus set('conversation', conversationpointer); typingstatus set('user', currentuser); typingstatus set('istyping', istyping); } await typingstatus save(); } catch (error) { console error('error updating typing status ', error); } }; // handle typing indicator with debounce const handletyping = () => { updatetypingstatus(true); // clear previous timeout if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } // set typing to false after 3 seconds of inactivity typingtimeoutref current = settimeout(() => { updatetypingstatus(false); }, 3000); }; enviando mensagens ao enviar mensagens, criamos um novo objeto mensagem e deixamos o livequery lidar com as atualizações // from messagespage js sending messages const sendmessage = async (e) => { e preventdefault(); if (!newmessage trim() || !selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // create a new message const message = parse object extend('message'); const message = new message(); message set('content', newmessage); message set('sender', currentuser); message set('conversation', conversationpointer); message set('read', false); await message save(); // update the conversation with the last message const conversation = await new parse query(conversation) get(selectedconversation id); conversation set('lastmessage', newmessage); conversation set('lastmessagedate', new date()); await conversation save(); // clear the input setnewmessage(''); // set typing status to false updatetypingstatus(false); } catch (error) { console error('error sending message ', error); toast({ title 'error', description 'failed to send message', status 'error', duration 3000, isclosable true, }); } }; limpeza de assinaturas livequery é importante limpar as assinaturas livequery quando não são mais necessárias // from messagespage js cleanup useeffect(() => { // fetch initial conversations fetchconversations(); subscribetoconversations(); // cleanup subscriptions when component unmounts return () => { if (messagesubscription current) { messagesubscription current unsubscribe(); } if (typingsubscription current) { typingsubscription current unsubscribe(); } if (conversationsubscription current) { conversationsubscription current unsubscribe(); } if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } }; }, \[]); considerações de desempenho do livequery do back4app ao implementar o livequery, considere estas dicas de desempenho seja específico com consultas inscreva se apenas nos dados que você precisa use restrições para limitar o escopo das inscrições por exemplo, inscreva se apenas em mensagens na conversa atual gerencie inscrições com cuidado cancele a inscrição quando os dados não forem mais necessários crie novas inscrições quando o contexto mudar armazene referências de inscrição para cancelar a inscrição mais tarde use acls para segurança defina acls adequadas nos objetos de mensagem e conversa garanta que os usuários só possam acessar conversas das quais fazem parte livequery respeita acls, então usuários não autorizados não receberão atualizações otimize o servidor livequery no painel do back4app, configure as classes que precisam de livequery não ative livequery para classes que não precisam de atualizações em tempo real passo 9 — implementando a funcionalidade de busca nesta etapa, implementaremos uma funcionalidade de busca abrangente para nossa rede social os usuários poderão buscar por outros usuários, postagens por conteúdo e hashtags este recurso facilitará a descoberta de conteúdo e a conexão com outros na plataforma entendendo a busca no back4app antes de mergulhar na implementação, vamos entender como a busca funciona no back4app sistema de consulta parse back4app usa o sistema de consulta do parse server para busca consultas podem ser realizadas em várias classes você pode buscar por correspondência exata, contém, começa com, etc opções de busca de texto começacom encontra strings que começam com uma string específica contém encontra strings que contêm uma substring específica corresponde usa expressões regulares para correspondência de padrões mais complexos textocompleto (recurso enterprise) fornece capacidades avançadas de busca de texto completo considerações de desempenho buscas de texto podem ser intensivas em recursos índices devem ser criados para campos frequentemente pesquisados consultas devem ser otimizadas para limitar o número de resultados construindo a página de busca nosso componente searchpage lidará com diferentes tipos de buscas e exibirá os resultados vamos examinar sua estrutura // from searchpage js component structure function searchpage() { const \[searchquery, setsearchquery] = usestate(''); const \[searchtype, setsearchtype] = usestate('users'); // 'users', 'posts', 'hashtags' const \[searchresults, setsearchresults] = usestate(\[]); const \[isloading, setisloading] = usestate(false); const \[trendingtopics, settrendingtopics] = usestate(\[]); // rest of the component } o componente mantém estado para a consulta de pesquisa inserida pelo usuário o tipo de pesquisa que está sendo realizada os resultados da pesquisa estado de carregamento tópicos em alta implementando a busca de usuários vamos ver como buscamos usuários no back4app // from searchpage js user search implementation const searchusers = async (query) => { setisloading(true); try { // create a query on the user class const userquery = new parse query(parse user); // search for usernames that contain the query string (case insensitive) userquery matches('username', new regexp(query, 'i')); // limit results to improve performance userquery limit(20); const users = await userquery find(); // transform parse objects to plain objects const userresults = users map(user => ({ id user id, username user get('username'), avatar user get('avatar') ? user get('avatar') url() null, bio user get('bio') || '' })); setsearchresults(userresults); } catch (error) { console error('error searching users ', error); toaster create({ title 'error', description 'failed to search users', type 'error', }); } finally { setisloading(false); } }; mecanismos chave do back4app new parse query(parse user) cria uma consulta na classe user userquery matches('username', new regexp(query, 'i')) realiza uma correspondência regex sem diferenciação entre maiúsculas e minúsculas nos nomes de usuário userquery limit(20) limita os resultados para melhorar o desempenho userquery find() executa a consulta e retorna os usuários correspondentes implementando a busca de conteúdo de postagens agora vamos ver como buscamos postagens por conteúdo // from searchpage js post search implementation const searchposts = async (query) => { setisloading(true); try { // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the query string postquery matches('content', new regexp(query, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const postresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(postresults); } catch (error) { console error('error searching posts ', error); toaster create({ title 'error', description 'failed to search posts', type 'error', }); } finally { setisloading(false); } }; principais mecanismos do back4app parse object extend('post') referencia a classe post postquery matches('content', new regexp(query, 'i')) realiza uma correspondência regex sem diferenciação entre maiúsculas e minúsculas no conteúdo do post postquery include('author') inclui as informações do autor em uma única consulta postquery descending('createdat') ordena os resultados pela data de criação implementando a busca por hashtags a busca por hashtags requer uma abordagem diferente vamos buscar por postagens que contenham hashtags // from searchpage js hashtag search implementation const searchhashtags = async (query) => { setisloading(true); try { // remove # if present at the beginning const hashtagquery = query startswith('#') ? query substring(1) query; // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the hashtag // we use word boundaries to find actual hashtags postquery matches('content', new regexp(`#${hashtagquery}\\\b`, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const hashtagresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(hashtagresults); } catch (error) { console error('error searching hashtags ', error); toaster create({ title 'error', description 'failed to search hashtags', type 'error', }); } finally { setisloading(false); } }; mecanismos chave do back4app usamos uma regex com limites de palavras ( \\\b ) para encontrar hashtags reais essa abordagem encontra postagens onde o conteúdo contém a hashtag específica implementando tópicos em alta para implementar tópicos em alta, precisamos analisar postagens recentes e contar as ocorrências de hashtags // from searchpage js fetching trending topics const fetchtrendingtopics = async () => { try { // create a query on the post class const post = parse object extend('post'); const query = new parse query(post); // get posts from the last 7 days const oneweekago = new date(); oneweekago setdate(oneweekago getdate() 7); query greaterthan('createdat', oneweekago); // limit to a reasonable number for analysis query limit(500); const posts = await query find(); // extract hashtags from post content const hashtagcounts = {}; posts foreach(post => { const content = post get('content') || ''; // find all hashtags in the content const hashtags = content match(/#(\w+)/g) || \[]; // count occurrences of each hashtag hashtags foreach(hashtag => { const tag = hashtag tolowercase(); hashtagcounts\[tag] = (hashtagcounts\[tag] || 0) + 1; }); }); // convert to array and sort by count const trendingarray = object entries(hashtagcounts) map((\[hashtag, count]) => ({ hashtag, count })) sort((a, b) => b count a count) slice(0, 10); // get top 10 settrendingtopics(trendingarray); } catch (error) { console error('error fetching trending topics ', error); } }; mecanismos chave do back4app consultamos postagens dos últimos 7 dias usando query greaterthan('createdat', oneweekago) analisamos o conteúdo para extrair e contar hashtags classificamos por frequência para encontrar as hashtags mais populares tratando a execução da busca agora vamos ver como tratamos a execução da busca com base no tipo de busca // from searchpage js search execution const handlesearch = (e) => { e preventdefault(); if (!searchquery trim()) return; switch (searchtype) { case 'users' searchusers(searchquery); break; case 'posts' searchposts(searchquery); break; case 'hashtags' searchhashtags(searchquery); break; default searchusers(searchquery); } }; otimizando a busca no back4app para otimizar o desempenho da busca no back4app criar índices navegue até o seu painel do back4app vá para "navegador de banco de dados" > selecione a classe (por exemplo, usuário, postagem) clique na aba "índices" crie índices para campos frequentemente pesquisados para a classe usuário crie um índice em nome de usuário para a classe postagem crie um índice em conteúdo use restrições de consulta sempre use limit() para restringir o número de resultados use select() para buscar apenas os campos que você precisa use skip() para paginação ao lidar com grandes conjuntos de resultados considere funções em nuvem para pesquisas complexas para lógica de pesquisa mais complexa, implemente uma função em nuvem isso permite que você realize processamento do lado do servidor e retorne resultados otimizados exemplo de função em nuvem para busca avançada // example cloud function for advanced search parse cloud define("advancedsearch", async (request) => { const { query, type, limit = 20 } = request params; if (!query) { throw new error("search query is required"); } let results = \[]; switch (type) { case 'users' const userquery = new parse query(parse user); userquery matches('username', new regexp(query, 'i')); userquery limit(limit); results = await userquery find({ usemasterkey true }); break; case 'posts' const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(query, 'i')); postquery include('author'); postquery limit(limit); results = await postquery find({ usemasterkey true }); break; // add more search types as needed default throw new error("invalid search type"); } return results; }); passo 10 — testando e implantando sua rede social nesta etapa final, abordaremos como testar seu aplicativo, prepará lo para produção, implantá lo em um serviço de hospedagem e monitorar e escalar seu backend do back4app essas etapas são cruciais para garantir que sua rede social funcione sem problemas em um ambiente de produção testando seu aplicativo antes de implantar, é importante testar minuciosamente seu aplicativo para detectar quaisquer bugs ou problemas 1\ teste manual crie um plano de teste que cubra todos os recursos principais do seu aplicativo autenticação do usuário teste de registro com entradas válidas e inválidas teste de login com credenciais corretas e incorretas testar a funcionalidade de redefinição de senha teste a persistência da sessão e logout funcionalidade do post teste a criação de postagens com texto e imagens teste de visualização de postagens no feed teste de curtir e comentar em postagens teste a exclusão de postagens interações sociais teste de visualização de perfis de usuário teste de comentários em postagens teste de mensagens em tempo real funcionalidade de pesquisa teste de busca por usuários teste de busca por postagens teste de busca por hashtag teste entre navegadores teste no chrome, firefox, safari e edge teste em navegadores móveis 2\ testes automatizados para testes mais robustos, implemente testes automatizados // example jest test for the login component import react from 'react'; import { render, fireevent, waitfor } from '@testing library/react'; import loginpage from ' /src/pages/loginpage'; import parse from 'parse/dist/parse min js'; // mock parse jest mock('parse/dist/parse min js', () => ({ user { login jest fn() } })); test('login form submits with username and password', async () => { parse user login mockresolvedvalueonce({ id '123', get () => 'testuser' }); const { getbylabeltext, getbyrole } = render(\<loginpage />); // fill in the form fireevent change(getbylabeltext(/username/i), { target { value 'testuser' } }); fireevent change(getbylabeltext(/password/i), { target { value 'password123' } }); // submit the form fireevent click(getbyrole('button', { name /log in/i })); // check if parse user login was called with correct arguments await waitfor(() => { expect(parse user login) tohavebeencalledwith('testuser', 'password123'); }); }); 3\ testando o back4app teste sua configuração do back4app funções em nuvem teste todas as funções em nuvem com várias entradas segurança verifique se as permissões de nível de classe estão funcionando corretamente livequery teste a funcionalidade em tempo real com vários clientes preparando para produção antes de implantar, otimize seu aplicativo para produção 1\ configuração do ambiente crie arquivos de ambiente separados para desenvolvimento e produção \# env development react app parse app id=your dev app id react app parse js key=your dev js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your dev app back4app io \# env production react app parse app id=your production app id react app parse js key=your production js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your prod app back4app io 2\ otimização da build otimize sua build do react // in your package json "scripts" { "analyze" "source map explorer 'build/static/js/ js'", "build" "generate sourcemap=false react scripts build" } instale o source map explorer para analisar o tamanho do seu bundle npm install save dev source map explorer 3\ otimização de desempenho implemente divisão de código para reduzir o tempo de carregamento inicial // in app js, use react lazy for route components import react, { suspense, lazy } from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider } from '@chakra ui/react'; import loadingspinner from ' /components/loadingspinner'; // lazy load pages const landingpage = lazy(() => import(' /pages/landingpage')); const loginpage = lazy(() => import(' /pages/loginpage')); const feedpage = lazy(() => import(' /pages/feedpage')); // other pages function app() { return ( \<chakraprovider> \<router> \<suspense fallback={\<loadingspinner />}> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/feed" element={\<feedpage />} /> {/ other routes /} \</routes> \</suspense> \</router> \</chakraprovider> ); } implantando em um serviço de hospedagem existem várias opções para implantar sua aplicação react 1\ implantando no vercel vercel é uma ótima opção para aplicações react instale o cli do vercel npm install g vercel implante sua aplicação vercel para implantação em produção vercel prod 2\ implantando no netlify netlify é outra excelente opção instale o cli do netlify npm install g netlify cli construa sua aplicação npm run build implante no netlify netlify deploy para implantação em produção netlify deploy prod 3\ implantando no github pages para uma opção de implantação simples instale gh pages npm install save dev gh pages adicione ao package json "homepage" "https //seunome github io/seu nome do repo", "scripts" { "predeploy" "npm run build", "deploy" "gh pages d build" } implantar npm run deploy monitoramento e escalonamento do seu backend back4app à medida que sua rede social cresce, você precisará monitorar e escalar seu backend back4app 1\ monitorando o desempenho back4app fornece várias ferramentas para monitorar sua aplicação análise do painel monitore solicitações de api, uso de armazenamento e operações de arquivo registros verifique os registros do servidor em busca de erros e problemas de desempenho métricas de desempenho acompanhe os tempos de resposta e identifique gargalos para acessar essas ferramentas vá para o seu painel do back4app navegue até "análise" para estatísticas de uso verifique "registros" para logs detalhados de operações 2\ escalando seu backend quando sua base de usuários cresce, você pode precisar escalar seu backend do back4app atualize seu plano mova para um plano de nível superior com mais recursos otimize consultas use índices e limite consultas para melhorar o desempenho implemente cache use cache do lado do cliente para dados acessados com frequência 3\ otimização de banco de dados otimize seu banco de dados para melhor desempenho criar índices adicione índices a campos frequentemente consultados // exemplo criando um índice no campo 'username' na classe user const schema = new parse schema(' user'); schema addindex('username index', { username 1 }); schema update(); usar pipeline de agregação para operações de dados complexas // exemplo contar postagens por usuário const pipeline = \[ { group { objectid '$author', count { $sum 1 } } } ]; const results = await parse cloud aggregate('post', pipeline); 4\ implementando um cdn para mídia para entrega mais rápida de imagens e mídias configure um cdn como cloudflare ou amazon cloudfront atualize suas configurações de armazenamento de arquivos do back4app para usar o cdn atualize as urls dos arquivos em seu aplicativo para usar o domínio do cdn 5\ configurando alertas de monitoramento configure alertas para ser notificado sobre problemas vá para o seu painel do back4app navegue até "configurações do app" > "alertas" configure alertas para uso elevado da api picos na taxa de erro limites de tamanho do banco de dados tempo de inatividade do servidor resumo do que foi realizado ao longo deste tutorial, você configure uma infraestrutura de backend robusta criou uma conta no back4app e configurou seu aplicativo desenhei um esquema de banco de dados para usuários, postagens, comentários e mensagens configurações de segurança configuradas e permissões em nível de classe configurar livequery para funcionalidade em tempo real construiu um frontend moderno em react criou uma interface responsiva com componentes do chakra ui implementado o roteamento do lado do cliente com react router desenvolveu componentes reutilizáveis para postagens, comentários e perfis de usuário conectou seu frontend ao back4app usando o sdk javascript parse implementou recursos principais de rede social autenticação de usuário (cadastro, login, redefinição de senha) criação de postagens e interação (curtidas, comentários) perfis de usuário e configurações mensagens em tempo real entre usuários funcionalidade de busca para usuários, postagens e hashtags otimizado para produção implementou otimizações de desempenho como divisão de código configurar as configurações do ambiente para desenvolvimento e produção aprendeu como implantar seu aplicativo em serviços de hospedagem explorou estratégias de monitoramento e escalonamento para o seu backend back4app agora você tem uma base sólida para um aplicativo de rede social que pode ser expandido e personalizado para atender às suas necessidades específicas próximos passos para expandir o aplicativo aqui estão algumas maneiras empolgantes de aprimorar seu aplicativo de rede social recursos avançados de mídia adicionar suporte para uploads e reprodução de vídeo implemente filtros de imagem e ferramentas de edição crie histórias ou recursos de conteúdo efêmero adicionar suporte para gifs e outros mídias ricas interações sociais aprimoradas implemente um mecanismo de recomendação para sugestões de amigos adicionar funcionalidade de grupos ou comunidades crie eventos com capacidades de rsvp desenvolver um sistema de notificações para todas as interações do usuário opções de monetização implementar recursos de associação premium adicione compras dentro do aplicativo para bens digitais crie um mercado para transações de usuário para usuário integre se com processadores de pagamento como stripe experiência móvel converta seu aplicativo em um aplicativo web progressivo (pwa) desenvolver aplicativos móveis nativos usando react native implemente notificações push para dispositivos móveis otimize a interface do usuário para diferentes tamanhos de tela e orientações análise e insights integre ferramentas de análise para rastrear o engajamento do usuário crie painéis para desempenho de conteúdo implemente testes a/b para novos recursos desenvolver insights sobre o comportamento do usuário para melhorar a plataforma moderação de conteúdo implementar filtragem de conteúdo automatizada criar sistemas de relatórios para conteúdo inadequado desenvolver ferramentas administrativas para moderação de conteúdo use aprendizado de máquina para análise inteligente de conteúdo recursos adicionais para aprendizado para continuar expandindo seu conhecimento e habilidades, aqui estão alguns recursos valiosos documentação e tutoriais do back4app documentação do back4app https //www back4app com/docs/get started/welcome guia javascript do parse https //docs parseplatform org/js/guide/ canal do youtube back4app https //www youtube com/c/back4app react e javascript moderno documentação do react https //reactjs org/docs/getting started html javascript info https //javascript info/ cursos de react do egghead io https //egghead io/q/react design de ui e ux documentação do chakra ui https //chakra ui com/docs/getting started padrões de design de ui https //ui patterns com/ pesquisa de ux do nielsen norman group https //www nngroup com/articles/ otimização de desempenho desempenho do web dev https //web dev/performance scoring/ otimização de desempenho do react https //reactjs org/docs/optimizing performance html google pagespeed insights https //developers google com/speed/pagespeed/insights/ comunidade e suporte stack overflow https //stackoverflow\ com/questions/tagged/parse platform fórum da comunidade parse https //community parseplatform org/ comunidade dev do react https //dev to/t/react lembre se de que construir uma rede social de sucesso é um processo iterativo comece com uma base sólida (que você já tem), colete feedback dos usuários e melhore continuamente seu aplicativo com base em padrões de uso do mundo real esperamos que este tutorial tenha fornecido a você o conhecimento e a confiança para construir aplicações incríveis com react e back4app feliz codificação