Project Templates
Social Network
Como Criar uma Plataforma de Rede Social: Guia Passo a Passo (2025)
71 min
introdução neste tutorial, você aprenderá como construir uma plataforma de rede social como o instagram usando o back4app como seu serviço de backend o back4app fornece um backend gerenciado do parse server que simplifica a autenticação de usuários, armazenamento de dados, uploads de arquivos e recursos em tempo real, sem exigir uma infraestrutura de servidor complexa 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 imagens interações sociais (curtidas, comentários) mensagens em tempo real com indicadores de digitação funcionalidade de busca de conteúdo pré visualização da rede socialinterface de chatperfil do usuáriopágina de feed 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/ node js e npm instalados em sua máquina local instale o node js (versão 14 x ou posterior) e npm a partir de nodejs org https //nodejs org/ compreensão básica de javascript e react editor de código qualquer editor de código moderno como visual studio code ou sublime text passo 1 — configurando seu backend back4app primeiro, vamos criar um novo projeto back4app e configurar o esquema do banco de dados para nossa rede social criando um novo projeto back4app faça login na sua conta back4app e navegue até o painel clique em "criar um novo app" digite "back4gram" como o nome do seu app, selecione a região do servidor mais próxima e clique em "criar" entendendo o esquema do banco de dados nossa rede social requer as seguintes classes no back4app usuário (já existe por padrão no parse) será estendido com campos adicionais como bio e avatar postagem armazena postagens de usuários, incluindo conteúdo de texto e imagens comentário armazena comentários em postagens conversa representa uma conversa de chat entre usuários mensagem mensagens individuais dentro de uma conversa statusdedigitação rastreia quando os usuários estão digitando em uma conversa criando classes de banco de dados 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) curtidopor (tipo array) criadoem (tipo data, adicionado automaticamente) criando a classe comment crie uma nova classe chamada "comentário" com estas colunas conteúdo (tipo string) autor (tipo ponteiro para usuário) post (tipo ponteiro para post) criadoem (tipo data, adicionado automaticamente) criando a classe conversation crie uma nova classe chamada "conversation" com estas colunas participantes (tipo array) últimamensagem (tipo string) atualizadoem (tipo data, adicionado automaticamente) criando a classe message crie uma nova classe chamada "mensagem" com estas colunas texto (tipo string) remetente (tipo ponteiro para usuário) conversa (tipo ponteiro para conversa) criadoem (tipo data, adicionado automaticamente) criando a classe typingstatus crie uma nova classe chamada "typingstatus" com estas colunas usuário (tipo ponteiro para usuário) conversa (tipo ponteiro para conversa) estádigitando (tipo booleano) configurando permissões de classe para proteger os dados da sua aplicação, configure 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 permissões para cada classe por exemplo, na classe post acesso público de leitura habilitado (todos podem ver postagens) acesso público de escrita habilitado (usuários autenticados podem criar postagens) adicione clps para atualização/exclusão para restringir ao autor apenas configurando livequery para recursos em tempo real para habilitar recursos em tempo real como mensagens e indicadores de digitação navegue até "configurações do servidor" no seu painel do back4app sob "parse server", encontre "livequery" e ative o adicione essas classes para serem monitoradas pelo livequery mensagem status de digitação postagem (para atualizações em tempo real de curtidas e comentários) obtendo suas chaves de aplicação você precisará das chaves da sua aplicação back4app para conectar seu frontend navegue até "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 chaves de segurança passo 2 — conectando seu frontend ao back4app vamos configurar a conexão entre seu frontend e o backend do back4app criando variáveis de ambiente crie um env local arquivo na raiz do seu projeto para armazenar suas credenciais do back4app react app parse app id=your application id react app parse js key=your javascript key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your app id back4app io substitua os valores de espaço reservado pelas suas credenciais reais do back4app configurando o parse sdk com o back4app crie um arquivo de configuração para inicializar o parse com suas credenciais do back4app // src/utils/parseconfig js import parse from 'parse/dist/parse min js'; // initialize 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; // initialize live queries if (process env react app parse live query url) { parse livequeryserverurl = process env react app parse live query url; } export default parse; em seguida, importe esta configuração no ponto de entrada do seu aplicativo // src/index js import react from 'react'; import reactdom from 'react dom/client'; import ' /index css'; import app from ' /app'; import ' /utils/parseconfig'; // import parse configuration const root = reactdom createroot(document getelementbyid('root')); root render( \<react strictmode> \<app /> \</react strictmode> ); passo 3 — implementando autenticação com o back4app o parse server do back4app fornece um sistema abrangente de gerenciamento de usuários através da parse user classe entendendo a autenticação de usuário do parse a classe parse user é projetada especificamente para gerenciamento de usuários armazena credenciais de usuário (nome de usuário, e mail, senha) gerencia o estado de autenticação gerencia tokens de sessão automaticamente implementando registro de usuário veja como implementar o registro de usuário com o parse // signup function const handlesignup = async () => { try { // create a new user const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); // additional user data user set('bio', ''); user set('followers', 0); user set('following', 0); // sign up the user await user signup(); // success user is automatically logged in console log('user registered successfully'); // navigate to feed or home page navigate('/feed'); } catch (error) { // handle specific parse errors console error('error signing up ', error message); if (error code === 202) { seterrors({ errors, username 'username already taken'}); } else if (error code === 203) { seterrors({ errors, email 'email already in use'}); } } }; implementando login de usuário aqui está como implementar o login de usuário com parse // login function const handlelogin = async () => { try { // log in the user const user = await parse user login(username, password); // success user is logged in console log('user logged in successfully ', user getusername()); // navigate to feed or home page navigate('/feed'); } catch (error) { // handle login errors console error('error logging in ', error message); setloginerror(error message); } }; verificando usuário atual o parse armazena automaticamente o token da sessão, permitindo que você verifique se um usuário já está logado // check if user is already logged in const checkcurrentuser = async () => { try { const currentuser = await parse user current(); if (currentuser) { console log('current user ', currentuser getusername()); return currentuser; } return null; } catch (error) { console error('error checking current user ', error); return null; } }; implementando a redefinição de senha back4app fornece um fluxo de redefinição de senha embutido // password reset function const handlepasswordreset = async () => { try { await parse user requestpasswordreset(email); // success email sent to user console log('password reset email sent'); setresetemailsent(true); } catch (error) { // handle reset errors console error('error requesting password reset ', error message); setreseterror(error message); } }; passo 4 — criando e exibindo postagens agora vamos implementar a criação e recuperação de postagens usando o back4app criando postagens aqui está como criar uma nova postagem com parse // create post function const createpost = async () => { if (!postcontent trim() && !postimage) { console error('post must have content or image'); return; } 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(); console log('post created successfully'); return newpost; } catch (error) { console error('error creating post ', error message); throw error; } }; mecanismos chave do back4app parse object extend('post') referencia a classe post no back4app new post() cria uma nova instância da classe post parsefile save() faz o upload do arquivo para o armazenamento do back4app newpost save() salva o objeto de postagem no back4app buscando postagens aqui está como buscar postagens do back4app // fetch posts function const fetchposts = async (page = 0, limit = 10) => { try { // 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(limit); query skip(page limit); // execute the query const results = await query find(); // process the results const posts = 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 } })); return posts; } catch (error) { console error('error fetching posts ', error); throw error; } }; mecanismos principais do back4app new parse query(post) cria uma consulta na classe post query include('author') realiza uma operação semelhante a um join para buscar objetos relacionados query descending('createdat') ordena os resultados pela data de criação query limit() e query skip() implementa paginação post get('image') url() obtém a url de um objeto parse file implementando a funcionalidade de curtir veja como implementar curtidas em postagens // like/unlike post function const togglelike = async (postid) => { try { const currentuser = parse user current(); const userid = currentuser id; // get the post object const post = parse object extend('post'); const query = new parse query(post); const post = await query get(postid); // get current likes and likedby array const likedby = post get('likedby') || \[]; const isliked = likedby includes(userid); // update likes count and likedby array if (isliked) { // unlike remove user from likedby and decrement likes post set('likedby', likedby filter(id => id !== userid)); post set('likes', math max((post get('likes') || 1) 1, 0)); } else { // like add user to likedby and increment likes post set('likedby', \[ likedby, userid]); post set('likes', (post get('likes') || 0) + 1); } // save the updated post await post save(); return !isliked; // return new like status } catch (error) { console error('error toggling like ', error); throw error; } }; passo 5 — implementando comentários em postagens vamos implementar a funcionalidade de comentários usando o back4app criando comentários aqui está como adicionar um comentário a uma postagem // add comment function const addcomment = async (postid, commentcontent) => { if (!commentcontent trim()) { console error('comment cannot be empty'); return; } try { // get the post object const post = parse object extend('post'); const postquery = new parse query(post); const post = await postquery get(postid); // create a new comment object const comment = parse object extend('comment'); const comment = new comment(); // set comment data comment set('content', commentcontent); comment set('author', parse user current()); comment set('post', post); // save the comment await comment save(); console log('comment added successfully'); return comment; } catch (error) { console error('error adding comment ', error); throw error; } }; buscando comentários para uma postagem aqui está como buscar comentários para uma postagem específica // fetch comments function const fetchcomments = async (postid) => { try { // get the post object const post = parse object extend('post'); const postquery = new parse query(post); const post = await postquery get(postid); // create a query for comments const comment = parse object extend('comment'); const query = new parse query(comment); // find comments for this post query equalto('post', post); // include the author information query include('author'); // sort by creation date query ascending('createdat'); // execute the query const results = await query find(); // process the results const comments = 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 } })); return comments; } catch (error) { console error('error fetching comments ', error); throw error; } }; passo 6 — implementando perfis de usuário agora vamos implementar perfis de usuário usando o back4app buscando dados do usuário aqui está como buscar dados do perfil do usuário // fetch user profile function const fetchuserprofile = async (userid) => { try { // create a query for the user class const query = new parse query(parse user); // get the user by id const user = await query get(userid); // get user posts const post = parse object extend('post'); const postsquery = new parse query(post); postsquery equalto('author', user); postsquery include('author'); postsquery descending('createdat'); const posts = await postsquery find(); // process user data const userdata = { id user id, username user get('username'), bio user get('bio') || '', avatar user get('avatar') ? user get('avatar') url() null, followers user get('followers') || 0, following user get('following') || 0, posts 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') })) }; return userdata; } catch (error) { console error('error fetching user profile ', error); throw error; } }; atualizando o perfil do usuário aqui está como atualizar o perfil de um usuário // update user profile function const updateuserprofile = async (profiledata) => { try { const currentuser = await parse user current(); // update profile fields if (profiledata bio !== undefined) { currentuser set('bio', profiledata bio); } if (profiledata avatarfile) { const parsefile = new parse file('avatar jpg', profiledata avatarfile); await parsefile save(); currentuser set('avatar', parsefile); } // save the updated user await currentuser save(); console log('profile updated successfully'); return currentuser; } catch (error) { console error('error updating profile ', error); throw error; } }; passo 7 — implementando mensagens em tempo real com livequery agora vamos implementar mensagens em tempo real usando o recurso livequery do back4app criando conversas aqui está como criar uma nova conversa // create conversation function const createconversation = async (participantids) => { try { // ensure current user is included in participants const currentuser = await parse user current(); const allparticipantids = \[ new set(\[currentuser id, participantids])]; // check if conversation already exists const existingconversation = await findexistingconversation(allparticipantids); if (existingconversation) { return existingconversation; } // get user objects for all participants const participantpointers = await promise all( allparticipantids map(async (id) => { const userquery = new parse query(parse user); return await userquery get(id); }) ); // create new conversation const conversation = parse object extend('conversation'); const conversation = new conversation(); conversation set('participants', participantpointers); conversation set('lastmessage', ''); await conversation save(); console log('conversation created successfully'); return conversation; } catch (error) { console error('error creating conversation ', error); throw error; } }; // helper to find existing conversation const findexistingconversation = async (participantids) => { try { const conversation = parse object extend('conversation'); const query = new parse query(conversation); // this is a simplified approach in production you'd need more complex query const results = await query find(); for (const conversation of results) { const participants = conversation get('participants') || \[]; const participantids = participants map(p => p id); // check if arrays have same elements (regardless of order) if (participantids length === participantids length && participantids every(id => participantids includes(id))) { return conversation; } } return null; } catch (error) { console error('error finding existing conversation ', error); return null; } }; enviando mensagens aqui está como enviar uma mensagem em uma conversa // send message function const sendmessage = async (conversationid, messagetext) => { try { const currentuser = parse user current(); // get the conversation const conversation = parse object extend('conversation'); const conversationquery = new parse query(conversation); const conversation = await conversationquery get(conversationid); // create a new message const message = parse object extend('message'); const message = new message(); message set('text', messagetext); message set('sender', currentuser); message set('conversation', conversation); message set('read', false); await message save(); // update conversation with last message conversation set('lastmessage', messagetext); await conversation save(); console log('message sent successfully'); return message; } catch (error) { console error('error sending message ', error); throw error; } }; configurando livequery para mensagens aqui está como se inscrever para atualizações de mensagens em tempo real // subscribe to messages function const subscribetomessages = async (conversationid, onnewmessage) => { try { // create a query for messages in this conversation const message = parse object extend('message'); const query = new parse query(message); // get the conversation pointer const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; // set up query constraints query equalto('conversation', conversationpointer); query include('sender'); // subscribe to the query const subscription = await query subscribe(); // listen for create events (new messages) subscription on('create', (message) => { // process the message const newmessage = { id message id, text message get('text'), 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') }; // call the callback with the new message onnewmessage(newmessage); // mark the message as read if from another user if (message get('sender') id !== parse user current() id) { markmessageasread(message); } }); return subscription; } catch (error) { console error('error subscribing to messages ', error); throw error; } }; // helper to mark message as read const markmessageasread = async (message) => { try { message set('read', true); await message save(); } catch (error) { console error('error marking message as read ', error); } }; implementando indicadores de digitação aqui está como implementar indicadores de digitação com livequery // update typing status function const updatetypingstatus = async (conversationid, istyping) => { try { const currentuser = parse user current(); // get the conversation pointer const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; // check if typing status 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 status typingstatus set('istyping', istyping); } else { // create new 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); } }; // subscribe to typing status function const subscribetotypingstatus = async (conversationid, ontypingstatuschange) => { try { // create a query for typing status in this conversation const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); // get the conversation pointer const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; // set up query constraints query equalto('conversation', conversationpointer); query include('user'); // subscribe to the query const subscription = await query subscribe(); // listen for update events subscription on('update', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // call the callback with typing status ontypingstatuschange({ userid user id, username user get('username'), istyping }); }); // also listen for create events subscription on('create', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // call the callback with typing status ontypingstatuschange({ userid user id, username user get('username'), istyping }); }); return subscription; } catch (error) { console error('error subscribing to typing status ', error); throw error; } }; passo 8 — implementando funcionalidade de busca vamos implementar a funcionalidade de busca usando o sistema de consulta do back4app buscando usuários aqui está como buscar usuários // search users function const searchusers = async (query, limit = 20) => { try { // create a query on the user class const userquery = new parse query(parse user); // search by username (case insensitive) userquery matches('username', new regexp(query, 'i')); // limit results to improve performance userquery limit(limit); // execute the query const users = await userquery find(); // process the results 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') || '' })); return userresults; } catch (error) { console error('error searching users ', error); throw error; } }; \### searching for posts here's how to search for posts by content ```javascript // search posts function const searchposts = async (query, limit = 20) => { 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(limit); // execute the query const posts = await postquery find(); // process the results 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 } })); return postresults; } catch (error) { console error('error searching posts ', error); throw error; } }; buscando por hashtags aqui está como buscar por posts com hashtags específicas // search hashtags function const searchhashtags = async (tag, limit = 20) => { try { // remove # if present at the beginning const hashtagquery = tag startswith('#') ? tag substring(1) tag; // 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 // using 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(limit); // execute the query const posts = await postquery find(); // process the results 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 } })); return hashtagresults; } catch (error) { console error('error searching hashtags ', error); throw error; } }; passo 9 — implementando recursos avançados do back4app vamos explorar alguns recursos avançados do back4app que podem aprimorar sua aplicação de rede social funções em nuvem back4app permite que você implemente lógica do lado do servidor usando funções em nuvem estas são funções javascript que são executadas no servidor e podem ser chamadas a partir do seu aplicativo cliente aqui está um exemplo de uma função em nuvem para rastrear notificações de comentários // in your back4app cloud code section 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é "código em nuvem" > "funções em nuvem" crie uma nova função com o código acima implante a função código em nuvem para pesquisa avançada para funcionalidades de busca mais complexas, você pode criar uma cloud function // in your back4app cloud code section 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; default throw new error("invalid search type"); } return results; }); para chamar essa função do seu cliente // call the advanced search cloud function const calladvancedsearch = async (query, type, limit = 20) => { try { const results = await parse cloud run('advancedsearch', { query, type, limit }); return results; } catch (error) { console error('error calling advanced search ', error); throw error; } }; implementando tarefas em segundo plano para tarefas recorrentes como cálculo de tópicos em alta, você pode usar tarefas em segundo plano // in your back4app cloud code section parse cloud job("calculatetrendingtopics", async () => { // get posts from the last 7 days const post = parse object extend("post"); const query = new parse query(post); const oneweekago = new date(); oneweekago setdate(oneweekago getdate() 7); query greaterthan('createdat', oneweekago); // limit to a reasonable number for analysis query limit(1000); const posts = await query find({ usemasterkey true }); // extract hashtags and count occurrences const hashtagcounts = {}; posts foreach(post => { const content = post get('content') || ''; const hashtags = content match(/#(\w+)/g) || \[]; 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 // save to a trendingtopics class const trendingtopics = parse object extend("trendingtopics"); const trending = new trendingtopics(); trending set("topics", trendingarray); trending set("calculatedat", new date()); await trending save(null, { usemasterkey true }); return "trending topics calculated successfully"; }); para configurar este trabalho vá para o seu painel do back4app navegue até "cloud code" > "background jobs" crie um novo trabalho com o código acima programe o para ser executado diariamente ou semanalmente usando hooks do parse server back4app permite que você implemente ganchos do lado do servidor que são executados automaticamente antes ou depois de certas operações estes são úteis para validação de dados, modificação ou acionamento de efeitos colaterais // beforesave hook to sanitize post content parse cloud beforesave("post", async (request) => { const post = request object; let content = post get("content"); // sanitize content (example remove profanity) const profanitylist = \["badword1", "badword2", "badword3"]; profanitylist foreach(word => { const regex = new regexp(word, "gi"); content = content replace(regex, " "); }); // update the content post set("content", content); }); // afterdelete hook to clean up related data parse cloud afterdelete("post", async (request) => { const post = request object; const postid = post id; // delete all comments associated with this post const comment = parse object extend("comment"); const query = new parse query(comment); query equalto("post", post); const comments = await query find({ usemasterkey true }); await parse object destroyall(comments, { usemasterkey true }); console log(`deleted ${comments length} comments for post ${postid}`); }); implementando notificações push back4app fornece um sistema robusto de notificações push que permite enviar notificações para os dispositivos dos seus usuários aqui está como implementar notificações push primeiro, configure uma configuração de push no seu painel do back4app vá para "configurações do app" > "push" configure suas credenciais de ios e/ou android envie uma notificação push quando um usuário receber uma nova mensagem // in your cloud code parse cloud aftersave("message", async (request) => { const message = request object; const conversation = message get("conversation"); const sender = message get("sender"); // get the conversation to find recipients const conversationquery = new parse query("conversation"); const fullconversation = await conversationquery get(conversation id, { usemasterkey true }); // get all participants const participants = fullconversation get("participants") || \[]; // send notification to all participants except the sender for (const participant of participants) { if (participant id !== sender id) { // create a query for this user's installations const pushquery = new parse query(parse installation); pushquery equalto("user", participant); // send the push notification await parse push send({ where pushquery, data { alert `new message from ${sender get("username")}`, sound "default", badge "increment", sender sender id, messageid message id, conversationid conversation id } }, { usemasterkey true }); } } }); usando controle de acesso baseado em funções back4app fornece um sistema de controle de acesso baseado em funções que permite gerenciar permissões em um nível mais granular // create an admin role const createadminrole = async (adminuser) => { try { // create a new role const adminrole = new parse role("administrator", new parse acl()); // set initial users of this role adminrole getusers() add(adminuser); // save the role await adminrole save(null, { usemasterkey true }); console log("admin role created successfully"); return adminrole; } catch (error) { console error("error creating admin role ", error); throw error; } }; // set permissions for posts to be moderated by admins const setpostmoderatorpermissions = async () => { try { // get the admin role const rolequery = new parse query(parse role); rolequery equalto("name", "administrator"); const adminrole = await rolequery first({ usemasterkey true }); // set class level permissions const schema = new parse schema("post"); // add class level permissions for admin role await schema setclp({ get { " " true }, find { " " true }, create { " " true }, update { "role\ administrator" true, "" true }, // creator and admins can update delete { "role\ administrator" true, "" true }, // creator and admins can delete addfield { "role\ administrator" true } }); await schema update(); console log("admin permissions for post class set successfully"); } catch (error) { console error("error setting admin permissions ", error); throw error; } }; implementando webhooks back4app permite que você configure webhooks para integrar com serviços externos // example cloud function that calls an external webhook parse cloud aftersave("post", async (request) => { const post = request object; // only trigger for new posts if (request original) return; try { // get author information const author = post get("author"); await author fetch({ usemasterkey true }); // prepare data for webhook const webhookdata = { postid post id, content post get("content"), authorname author get("username"), createdat post get("createdat") }; // call external webhook (example notify a content moderation service) const response = await parse cloud httprequest({ method 'post', url 'https //your webhook url com/new post', headers { 'content type' 'application/json' }, body webhookdata }); console log("webhook notification sent successfully ", response data); } catch (error) { console error("error sending webhook notification ", error); } }); passo 10 — otimizando o desempenho do back4app à medida que sua rede social cresce, você precisará otimizar seu backend do back4app para desempenho e escalabilidade esta seção cobre estratégias essenciais para garantir que sua aplicação permaneça rápida e responsiva, mesmo com a expansão da sua base de usuários otimização de banco de dados um design e consultas de banco de dados eficientes são cruciais para o desempenho da aplicação criando índices os índices melhoram drasticamente o desempenho das consultas em campos frequentemente pesquisados vá para o seu painel do back4app navegue até "database browser" > selecione uma classe (por exemplo, post) clique na aba "índices" crie índices para campos frequentemente pesquisados // example creating indexes programmatically const createpostindexes = async () => { try { const schema = new parse schema('post'); // add an index on the author field await schema addindex('author index', { author 1 }); // add an index on createdat for timeline queries await schema addindex('createdat index', { createdat 1 }); // add a compound index for author + createdat await schema addindex('author date index', { author 1, createdat 1 }); await schema update(); console log('indexes created successfully'); } catch (error) { console error('error creating indexes ', error); } }; campos chave para indexar autor na classe post para consultas mais rápidas específicas do usuário createdat para carregamento mais rápido da linha do tempo nome de usuário na classe user para pesquisas de usuários mais rápidas participantes na classe conversation para filtragem de mensagens mais rápida otimização de consultas otimize suas consultas para reduzir a carga do servidor e o tempo de resposta // bad fetches all fields and doesn't use limit const fetchuserposts = async (userid) => { const query = new parse query('post'); query equalto('author', userid); const results = await query find(); return results; }; // good only fetches needed fields and uses limit const fetchuserposts = async (userid, page = 0, limit = 20) => { const post = parse object extend('post'); const query = new parse query(post); // get user pointer const userpointer = { type 'pointer', classname ' user', objectid userid }; // only get posts for this user query equalto('author', userpointer); // only select fields we need query select('content', 'image', 'likes', 'createdat'); // sort by date, newest first query descending('createdat'); // implement pagination query limit(limit); query skip(page limit); const results = await query find(); return results; }; operações em lote para operações envolvendo múltiplos objetos, use operações em lote para reduzir chamadas de api // update multiple posts in a single request const updatemultipleposts = async (postids, updatedata) => { try { const post = parse object extend('post'); const posts = postids map(id => { const post = new post(); post id = id; return post; }); // set the same update data on all posts posts foreach(post => { object entries(updatedata) foreach((\[key, value]) => { post set(key, value); }); }); // save all posts in a single request await parse object saveall(posts); console log('all posts updated successfully'); } catch (error) { console error('error updating posts ', error); throw error; } }; // delete multiple comments in a single request const deletemultiplecomments = async (commentids) => { try { const comment = parse object extend('comment'); const comments = commentids map(id => { const comment = new comment(); comment id = id; return comment; }); // delete all comments in a single request await parse object destroyall(comments); console log('all comments deleted successfully'); } catch (error) { console error('error deleting comments ', error); throw error; } }; implementando cache implemente o cache do lado do cliente para dados acessados com frequência para reduzir chamadas à api // caching user profiles const userprofilecache = new map(); const cache expiry = 10 60 1000; // 10 minutes in milliseconds const getuserprofile = async (userid, forcerefresh = false) => { // check if we have a valid cache entry const now = date now(); const cacheentry = userprofilecache get(userid); if (!forcerefresh && cacheentry && (now cacheentry timestamp < cache expiry)) { console log('using cached user profile'); return cacheentry data; } try { // fetch from back4app const query = new parse query(parse user); const user = await query get(userid); // process user data const userdata = { id user id, username user get('username'), bio user get('bio') || '', avatar user get('avatar') ? user get('avatar') url() null, followers user get('followers') || 0, following user get('following') || 0 }; // store in cache with timestamp userprofilecache set(userid, { data userdata, timestamp now }); return userdata; } catch (error) { console error('error fetching user profile ', error); // return cached data even if expired in case of error if (cacheentry) { console log('using expired cache due to error'); return cacheentry data; } throw error; } }; // clear cache for a user when their profile is updated const invalidateusercache = (userid) => { userprofilecache delete(userid); }; usando livequery de forma eficiente livequery é poderoso para recursos em tempo real, mas deve ser usado de forma eficiente para evitar problemas de desempenho // efficient livequery usage const setupefficientlivequery = async (conversationid) => { try { // only subscribe to messages in the active conversation if (!conversationid) return null; // create a focused query const message = parse object extend('message'); const query = new parse query(message); // get the conversation pointer const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; // only get messages for this conversation query equalto('conversation', conversationpointer); // only include necessary fields query select('text', 'sender', 'createdat'); // limit initial results query limit(50); query descending('createdat'); // create subscription const subscription = await query subscribe(); subscription on('create', (message) => { // handle new message console log('new message ', message get('text')); // update ui with new message }); // unsubscribe when switching conversations return subscription; } catch (error) { console error('error setting up livequery ', error); return null; } }; // always unsubscribe when done const cleanuplivequery = (subscription) => { if (subscription) { subscription unsubscribe(); } }; melhores práticas para livequery apenas inscreva se em dados que precisam de atualizações em tempo real limite os campos que você solicita com select() sempre cancele a inscrição quando os dados não forem mais necessários use restrições para restringir o escopo das inscrições considere combinar livequery com consultas regulares para carregamento inicial de dados otimização do manuseio de arquivos o manuseio eficiente de arquivos é crucial para uma rede social com uploads de imagens // resize images before upload to reduce storage and bandwidth const uploadresizedimage = async (originalfile, maxwidth = 1200, maxheight = 1200) => { return new promise((resolve, reject) => { try { const reader = new filereader(); reader onload = (event) => { const img = new image(); img onload = () => { // calculate new dimensions let width = img width; let height = img height; if (width > maxwidth) { height = math round(height (maxwidth / width)); width = maxwidth; } if (height > maxheight) { width = math round(width (maxheight / height)); height = maxheight; } // create canvas for resizing const canvas = document createelement('canvas'); canvas width = width; canvas height = height; // draw resized image const ctx = canvas getcontext('2d'); ctx drawimage(img, 0, 0, width, height); // convert to blob canvas toblob(async (blob) => { const resizedfile = new file(\[blob], originalfile name, { type originalfile type, lastmodified date now() }); // upload to back4app const parsefile = new parse file(resizedfile name, resizedfile); await parsefile save(); resolve(parsefile); }, originalfile type, 0 8); // 0 8 quality for jpeg }; img src = event target result; }; reader readasdataurl(originalfile); } catch (error) { reject(error); } }); }; // use thumbnails for preview images const createthumbnailversion = async (originalfile, thumbnailwidth = 200) => { // similar to above but creating a smaller thumbnail // // return both original and thumbnail return { original originalparsefile, thumbnail thumbnailparsefile }; }; monitorando seu aplicativo back4app back4app fornece várias ferramentas para monitorar o desempenho do seu aplicativo análise do painel fornece estatísticas de uso em alto nível monitore solicitações de api, uso de armazenamento e operações de arquivos identifique padrões de uso e horários de pico logs fornece informações detalhadas sobre operações revise os logs do servidor em busca de erros e problemas de desempenho filtre logs por tipo para isolar problemas métricas de desempenho acompanhe os tempos de resposta e a saúde do sistema monitore o desempenho das consultas identifique operações lentas configure alertas para ser notificado sobre problemas potenciais vá para "configurações do app" > "alertas" no seu painel do back4app configure alertas para uso alto da api (aproximando se dos limites do seu plano) picos na taxa de erro (indicando problemas na aplicação) tempo de inatividade do servidor (afetando todos os usuários) uso de armazenamento (aproximando se da capacidade) // implementing custom logging for performance monitoring const logperformance = async (operation, starttime) => { const duration = date now() starttime; // log to back4app const performancelog = parse object extend('performancelog'); const log = new performancelog(); log set('operation', operation); log set('duration', duration); log set('user', parse user current() ? parse user current() id 'anonymous'); log set('timestamp', new date()); await log save(null, { usemasterkey true }); // log locally too console log(`performance ${operation} took ${duration}ms`); }; // example usage const fetchtimelineposts = async () => { const starttime = date now(); try { // query code here // // calculate and log performance await logperformance('fetchtimelineposts', starttime); return results; } catch (error) { console error('error ', error); throw error; } }; estratégias de escalonamento à medida que sua aplicação cresce, considere estas estratégias de escalonamento atualize seu plano back4app mova para um nível mais alto à medida que sua base de usuários se expande monitore o uso e atualize antes de atingir os limites implemente sharding para grandes coleções divida os dados em várias classes para conjuntos de dados muito grandes por exemplo, divida mensagens por ano ou mês // example of sharded messages implementation const sendshardedmessage = async (conversationid, text, sender) => { try { // determine which shard to use (e g , by current month) const now = new date(); const shardname = `message ${now\ getfullyear()} ${now\ getmonth() + 1}`; // create dynamic class reference const messageshard = parse object extend(shardname); const message = new messageshard(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; // set message data message set('conversation', conversationpointer); message set('text', text); message set('sender', sender); // save to appropriate shard await message save(); return message; } catch (error) { console error('error sending sharded message ', error); throw error; } }; // fetch messages across shards const fetchshardedmessages = async (conversationid, limit = 50) => { try { // get list of all message shards const shards = await getmessageshardnames(); // create a query for each shard const queries = shards map(shardname => { const messageshard = parse object extend(shardname); const query = new parse query(messageshard); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; query equalto('conversation', conversationpointer); return query; }); // combine queries const mainquery = parse query or( queries); mainquery descending('createdat'); mainquery limit(limit); return await mainquery find(); } catch (error) { console error('error fetching sharded messages ', error); throw error; } }; implementar servidor de cache usar redis ou outras soluções de cache para dados acessados com frequência implementar através do cloud code e serviços externos usar redes de entrega de conteúdo (cdns) configurar cdn para entrega de arquivos mais rápida especialmente importante para imagens e arquivos de mídia otimizações de segurança segurança e desempenho estão intimamente relacionados essas otimizações melhoram ambos implementar permissões de ponteiro use permissões de ponteiro para restringir o acesso a objetos relacionados isso permite consultas mais eficientes sem sacrificar a segurança // set up pointer permissions const setuppointerpermissions = async () => { try { // get the message class schema const schema = new parse schema('message'); // add pointer permissions only conversation participants can access messages const pointerpermissions = { 'conversation' { 'read' { 'participantsfield' 'participants' } } }; // update the schema with pointer permissions await schema addpointerpermissions(pointerpermissions); await schema update(); console log('pointer permissions set successfully'); } catch (error) { console error('error setting pointer permissions ', error); } }; use acls do parse de forma eficiente acls fornecem segurança, mas podem impactar o desempenho se usadas em excesso considere usar permissões de nível de classe para casos comuns e acls para exceções // efficient acl usage set default acl parse setdefaultacl(new parse acl({ publicreadaccess true }), true); // only use specific acls for private content const createprivatepost = async (content, image) => { const currentuser = parse user current(); // create a new private post const post = parse object extend('post'); const post = new post(); // set post data post set('content', content); post set('author', currentuser); if (image) { const parsefile = new parse file(image name, image); await parsefile save(); post set('image', parsefile); } // create private acl const privateacl = new parse acl(currentuser); // only the creator can read and write privateacl setreadaccess(currentuser id, true); privateacl setwriteaccess(currentuser id, true); // set the acl post setacl(privateacl); // save the post await post save(); return post; }; otimização do servidor back4app otimize a configuração do seu parse server no back4app configurar parâmetros do servidor vá para "configurações do app" > "configurações do servidor" ajuste as configurações de tempo limite para operações de longa duração configure limites máximos de requisições configurar limitação de taxa implemente limitação de taxa para prevenir abusos e garantir uso justo configure através do cloud code como mostrado anteriormente ao implementar essas estratégias de otimização de desempenho, seu aplicativo de rede social estará bem posicionado para lidar com o crescimento e manter um excelente desempenho, mesmo à medida que sua base de usuários se expande lembre se de monitorar continuamente o desempenho do seu aplicativo e fazer melhorias incrementais conforme necessário conclusão neste tutorial, você aprendeu como construir um aplicativo completo de rede social usando o back4app como seu serviço de backend você implementou recursos principais como autenticação de usuário e gerenciamento de perfil criação de postagens e interações sociais mensagens em tempo real com livequery funcionalidade de busca recursos avançados como funções em nuvem e tarefas em segundo plano o parse server do back4app fornece uma solução de backend poderosa e escalável que permite que você se concentre em construir ótimas experiências para os usuários sem se preocupar com uma infraestrutura de servidor complexa próximos passos para aprimorar ainda mais seu aplicativo de rede social, considere implementar notificações push usando o serviço de push do back4app adicionar análises para rastrear o engajamento do usuário configurar opções de armazenamento de arquivos para arquivos de mídia maiores implementar moderação de conteúdo usando funções em nuvem criar um aplicativo móvel usando react native com o mesmo backend do back4app ao aproveitar os recursos do back4app e seguir as estratégias de otimização descritas neste tutorial, você pode construir uma rede social que escala para lidar com milhares de usuários enquanto mantém alto desempenho recursos adicionais para continuar expandindo seu conhecimento sobre o back4app, aqui estão alguns recursos valiosos documentação do back4app documentação do back4app https //www back4app com/docs/get started/welcome guia do javascript do parse https //docs parseplatform org/js/guide/ canal do youtube back4app https //www youtube com/c/back4app parse server e livequery documentação do parse server https //docs parseplatform org/parse server/guide/ guia de livequery https //docs parseplatform org/parse server/guide/#live queries segurança e otimização guia de permissões em nível de classe https //docs parseplatform org/rest/guide/#class level permissions guia de segurança acl https //docs parseplatform org/js/guide/#security otimização de desempenho https //docs parseplatform org/js/guide/#performance recursos avançados guia de código em nuvem https //docs parseplatform org/cloudcode/guide/ trabalhos em segundo plano https //docs parseplatform org/cloudcode/guide/#cloud jobs notificações por push https //docs parseplatform org/js/guide/#push notifications 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 aplicativos incríveis com o back4app boa codificação!