Project Templates
Social Network
Cómo Crear una Plataforma de Redes Sociales: Guía Paso a Paso (2025)
71 min
introducción en este tutorial, aprenderás cómo construir una plataforma de red social como instagram utilizando back4app como tu servicio de backend back4app proporciona un backend de parse server gestionado que simplifica la autenticación de usuarios, el almacenamiento de datos, las cargas de archivos y las características en tiempo real sin requerir una infraestructura de servidor compleja al completar este tutorial, construirás una red social completa con autenticación de usuarios (registro, inicio de sesión, restablecimiento de contraseña) gestión de perfiles creación de publicaciones con cargas de imágenes interacciones sociales (me gusta, comentarios) mensajería en tiempo real con indicadores de escritura funcionalidad de búsqueda de contenido vista previa de la red socialinterfaz de chatperfil de usuariopágina de feed en cualquier momento puedes acceder al código completo en github requisitos previos para completar este tutorial, necesitarás una cuenta de back4app regístrate para obtener una cuenta gratuita en back4app com https //www back4app com/ node js y npm instalados en tu máquina local instala node js (versión 14 x o posterior) y npm desde nodejs org https //nodejs org/ comprensión básica de javascript y react editor de código cualquier editor de código moderno como visual studio code o sublime text paso 1 — configurando tu backend de back4app primero, vamos a crear un nuevo proyecto de back4app y configurar el esquema de la base de datos para nuestra red social creando un nuevo proyecto de back4app inicia sesión en tu cuenta de back4app y navega al panel de control haz clic en "crear una nueva aplicación" ingresa "back4gram" como el nombre de tu aplicación, selecciona la región del servidor más cercana y haz clic en "crear" entendiendo el esquema de la base de datos nuestra red social requiere las siguientes clases en back4app usuario (ya existe por defecto en parse) se extenderá con campos adicionales como biografía y avatar publicación almacena publicaciones de usuarios, incluyendo contenido de texto e imágenes comentario almacena comentarios en publicaciones conversación representa una conversación de chat entre usuarios mensaje mensajes individuales dentro de una conversación estadodeescritura rastrea cuándo los usuarios están escribiendo en una conversación creando clases de base de datos vamos a crear estas clases en tu base de datos de back4app navega a la sección "base de datos" en tu panel de control de back4app extendiendo la clase usuario haz clic en la clase "usuario" que ya existe agregue las siguientes columnas biografía (tipo cadena) avatar (tipo archivo) seguidores (tipo número, predeterminado 0) siguiente (tipo número, predeterminado 0) creando la clase post haz clic en "crear una clase" ingrese "post" como el nombre de la clase y seleccione "crear una clase vacía" agrega las siguientes columnas contenido (tipo cadena) autor (tipo puntero a usuario) imagen (tipo archivo) me gusta (tipo número, predeterminado 0) gustadopor (tipo array) creadoen (tipo fecha, añadido automáticamente) creando la clase comment crea una nueva clase llamada "comentario" con estas columnas contenido (tipo cadena) autor (tipo puntero a usuario) publicación (tipo puntero a publicación) creadoen (tipo fecha, añadido automáticamente) creando la clase conversation crea una nueva clase llamada "conversación" con estas columnas participantes (tipo array) últimomensaje (tipo cadena) actualizadoen (tipo fecha, añadido automáticamente) creando la clase message crea una nueva clase llamada "mensaje" con estas columnas texto (tipo cadena) remitente (tipo puntero a usuario) conversación (tipo puntero a conversación) creadoen (tipo fecha, añadido automáticamente) creando la clase typingstatus crea una nueva clase llamada "typingstatus" con estas columnas usuario (tipo puntero a usuario) conversación (tipo puntero a conversación) estáescribiendo (tipo booleano) configuración de permisos de clase para asegurar los datos de su aplicación, configure listas de control de acceso (acl) apropiadas para cada clase navega a la sección "seguridad y claves" en tu panel de back4app bajo "seguridad a nivel de clase", configura permisos para cada clase por ejemplo, en la clase post acceso de lectura público habilitado (todos pueden ver publicaciones) acceso de escritura público habilitado (los usuarios autenticados pueden crear publicaciones) agrega clps para actualizar/eliminar para restringir solo al autor configurando livequery para características en tiempo real para habilitar características en tiempo real como mensajería e indicadores de escritura navega a "configuración del servidor" en tu panel de back4app bajo "parse server", encuentra "livequery" y habilítalo agrega estas clases para ser monitoreadas por livequery mensaje estado de escritura publicación (para actualizaciones en tiempo real de me gusta y comentarios) obteniendo tus claves de aplicación necesitarás tus claves de aplicación de back4app para conectar tu frontend navega a "configuración de la aplicación" > "seguridad y claves" anota las siguientes claves id de aplicación clave de javascript url del servidor url del servidor livequery claves de seguridad paso 2 — conectando tu frontend a back4app vamos a configurar la conexión entre tu frontend y el backend de back4app creando variables de entorno crea un env local archivo en la raíz de tu proyecto para almacenar tus credenciales de 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 reemplace los valores de marcador de posición con sus credenciales reales de back4app configurando parse sdk con back4app cree un archivo de configuración para inicializar parse con sus credenciales de 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; luego importe esta configuración en el punto de entrada de su aplicación // 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> ); paso 3 — implementando la autenticación con back4app el servidor parse de back4app proporciona un sistema integral de gestión de usuarios a través de la parse user clase entendiendo la autenticación de usuario en parse la clase parse user está diseñada específicamente para la gestión de usuarios almacena las credenciales del usuario (nombre de usuario, correo electrónico, contraseña) gestiona el estado de autenticación maneja los tokens de sesión automáticamente implementando el registro de usuario así es como implementar el registro de usuario con 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 el inicio de sesión del usuario así es como implementar el inicio de sesión del usuario con 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 el usuario actual parse almacena automáticamente el token de sesión, lo que te permite verificar si un usuario ya ha iniciado sesión // 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 la restablecimiento de contraseña back4app proporciona un flujo de restablecimiento de contraseña integrado // 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); } }; paso 4 — creando y mostrando publicaciones ahora implementemos la creación y recuperación de publicaciones utilizando back4app creando publicaciones así es como crear una nueva publicación con 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 clave de back4app parse object extend('post') hace referencia a la clase post en back4app new post() crea una nueva instancia de la clase post parsefile save() sube el archivo al almacenamiento de back4app newpost save() guarda el objeto de publicación en back4app obteniendo publicaciones así es como obtener publicaciones de 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 clave de back4app new parse query(post) crea una consulta en la clase post query include('author') realiza una operación similar a un join para obtener objetos relacionados query descending('createdat') ordena los resultados por fecha de creación query limit() y query skip() implementa paginación post get('image') url() obtiene la url de un objeto parse file implementando la funcionalidad de me gusta así es como implementar los me gusta en las publicaciones // 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; } }; paso 5 — implementando comentarios en las publicaciones implementemos la funcionalidad de comentarios utilizando back4app creando comentarios así es como agregar un comentario a una publicación // 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; } }; obteniendo comentarios para una publicación así es como obtener comentarios para una publicación 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; } }; paso 6 — implementando perfiles de usuario ahora implementemos perfiles de usuario utilizando back4app obteniendo datos del usuario así es como se obtienen los datos del perfil del usuario // 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; } }; actualizando el perfil del usuario así es como actualizar el perfil de un usuario // 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; } }; paso 7 — implementación de mensajería en tiempo real con livequery ahora implementemos la mensajería en tiempo real utilizando la función livequery de back4app creando conversaciones así es como crear una nueva conversación // 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 mensajes así es como enviar un mensaje en una conversación // 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 mensajes así es como suscribirse a actualizaciones de mensajes en tiempo 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 escritura así es como implementar indicadores de escritura con 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; } }; paso 8 — implementando funcionalidad de búsqueda implementemos la funcionalidad de búsqueda utilizando el sistema de consultas de back4app buscando usuarios así es como buscar usuarios // 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 hashtags aquí está cómo buscar publicaciones con hashtags específicos // 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; } }; paso 9 — implementación de características avanzadas de back4app exploremos algunas características avanzadas de back4app que pueden mejorar su aplicación de red social funciones en la nube back4app te permite implementar lógica del lado del servidor utilizando funciones en la nube estas son funciones de javascript que se ejecutan en el servidor y pueden ser llamadas desde tu aplicación cliente aquí hay un ejemplo de una función en la nube para rastrear notificaciones de comentarios // 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 esto ve a tu panel de control de back4app navega a "código en la nube" > "funciones en la nube" crea una nueva función con el código anterior despliega la función código en la nube para búsqueda avanzada para una funcionalidad de búsqueda más compleja, puedes crear una función en la nube // 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 llamar a esta función desde tu 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 trabajos en segundo plano para tareas recurrentes como el cálculo de temas en tendencia, puedes usar trabajos en 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 trabajo ve a tu panel de control de back4app navega a "cloud code" > "background jobs" crea un nuevo trabajo con el código anterior programa que se ejecute diariamente o semanalmente usando hooks de parse server back4app te permite implementar ganchos del lado del servidor que se ejecutan automáticamente antes o después de ciertas operaciones estos son útiles para la validación de datos, modificación o activación de efectos secundarios // 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}`); }); implementación de notificaciones push back4app proporciona un sistema robusto de notificaciones push que te permite enviar notificaciones a los dispositivos de tus usuarios aquí te mostramos cómo implementar notificaciones push primero, configura una configuración de push en tu panel de back4app ve a "configuración de la app" > "push" configura tus credenciales de ios y/o android envía una notificación push cuando un usuario reciba un nuevo mensaje // 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 }); } } }); uso de control de acceso basado en roles back4app proporciona un sistema de control de acceso basado en roles que te permite gestionar permisos a un nivel más 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; } }; implementación de webhooks back4app te permite configurar webhooks para integrarte con servicios 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); } }); paso 10 — optimizando el rendimiento de back4app a medida que tu red social crece, necesitarás optimizar tu backend de back4app para el rendimiento y la escalabilidad esta sección cubre estrategias esenciales para asegurar que tu aplicación siga siendo rápida y receptiva incluso a medida que tu base de usuarios se expande optimización de la base de datos un diseño y consulta de base de datos eficientes son cruciales para el rendimiento de la aplicación creando índices los índices mejoran drásticamente el rendimiento de las consultas en campos que se buscan con frecuencia ve a tu panel de control de back4app navega a "explorador de base de datos" > selecciona una clase (por ejemplo, publicación) haz clic en la pestaña "índices" crea índices para campos que se buscan con frecuencia // 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 clave para indexar autor en la clase publicación para consultas más rápidas específicas de usuario createdat para una carga de línea de tiempo más rápida nombre de usuario en la clase usuario para búsquedas de usuario más rápidas participantes en la clase conversación para un filtrado de mensajes más rápido optimización de consultas optimiza tus consultas para reducir la carga del servidor y el tiempo de respuesta // 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; }; operaciones por lotes para operaciones que involucran múltiples objetos, utiliza operaciones por lotes para reducir las llamadas a la 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; } }; implementación de caché implementar caché del lado del cliente para datos de acceso frecuente para reducir las llamadas a la 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); }; uso eficiente de livequery livequery es poderoso para características en tiempo real, pero debe usarse de manera eficiente para evitar problemas de rendimiento // 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(); } }; mejores prácticas para livequery solo suscríbete a los datos que necesitan actualizaciones en tiempo real limita los campos que solicitas con select() siempre cancela la suscripción cuando los datos ya no sean necesarios utiliza restricciones para reducir el alcance de las suscripciones considera combinar livequery con consultas regulares para la carga inicial de datos optimización del manejo de archivos el manejo eficiente de archivos es crucial para una red social con cargas de imágenes // 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 }; }; monitoreo de su aplicación back4app back4app proporciona varias herramientas para monitorear el rendimiento de su aplicación análisis del panel proporciona estadísticas de uso a alto nivel monitorear solicitudes de api, uso de almacenamiento y operaciones de archivos identificar patrones de uso y momentos pico registros proporcionar información detallada sobre las operaciones revisar los registros del servidor en busca de errores y problemas de rendimiento filtrar registros por tipo para aislar problemas métricas de rendimiento rastrear tiempos de respuesta y salud del sistema monitorear el rendimiento de las consultas identificar operaciones lentas configura alertas para ser notificado de problemas potenciales ve a "configuración de la aplicación" > "alertas" en tu panel de back4app configura alertas para uso alto de api (acercándose a los límites de tu plan) picos en la tasa de errores (indicando problemas en la aplicación) tiempo de inactividad del servidor (afectando a todos los usuarios) uso de almacenamiento (acercándose a la capacidad) // 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; } }; estrategias de escalado a medida que tu aplicación crece, considera estas estrategias de escalado actualiza tu plan de back4app pasa a un nivel superior a medida que tu base de usuarios se expande monitorea el uso y actualiza antes de alcanzar los límites implementa sharding para colecciones grandes divide los datos en múltiples clases para conjuntos de datos muy grandes por ejemplo, divide los mensajes por año o mes // 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 caché usar redis u otras soluciones de caché para datos de acceso frecuente implementar a través de cloud code y servicios externos usar redes de entrega de contenido (cdns) configurar cdn para una entrega de archivos más rápida especialmente importante para imágenes y archivos multimedia optimizaciones de seguridad la seguridad y el rendimiento están estrechamente relacionados estas optimizaciones mejoran ambos implementar permisos de puntero usar permisos de puntero para restringir el acceso a objetos relacionados esto permite consultas más eficientes sin sacrificar la seguridad // 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); } }; usar acls de parse de manera eficiente las acls proporcionan seguridad pero pueden afectar el rendimiento si se usan en exceso considera usar permisos a nivel de clase para casos comunes y acls para excepciones // 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; }; optimización del servidor back4app optimiza la configuración de tu servidor parse en back4app configurar parámetros del servidor ir a "configuración de la aplicación" > "configuración del servidor" ajustar la configuración de tiempo de espera para operaciones de larga duración configurar límites máximos de solicitudes configurar limitación de tasa implementar limitación de tasa para prevenir abusos y asegurar un uso justo configurar a través del código en la nube como se mostró anteriormente al implementar estas estrategias de optimización del rendimiento, tu aplicación de red social estará bien posicionada para manejar el crecimiento y mantener un excelente rendimiento, incluso a medida que tu base de usuarios se expanda recuerda monitorear continuamente el rendimiento de tu aplicación y hacer mejoras incrementales según sea necesario conclusión en este tutorial, has aprendido cómo construir una aplicación de red social completa utilizando back4app como tu servicio de backend has implementado características principales como autenticación de usuarios y gestión de perfiles creación de publicaciones e interacciones sociales mensajería en tiempo real con livequery funcionalidad de búsqueda características avanzadas como cloud functions y background jobs el servidor parse de back4app proporciona una solución de backend poderosa y escalable que te permite concentrarte en construir grandes experiencias de usuario sin preocuparte por una infraestructura de servidor compleja próximos pasos para mejorar aún más tu aplicación de red social, considera implementar notificaciones push utilizando el servicio de push de back4app agregar análisis para rastrear la participación de los usuarios configurar opciones de almacenamiento de archivos para archivos multimedia más grandes implementar moderación de contenido utilizando cloud functions crear una aplicación móvil utilizando react native con el mismo backend de back4app al aprovechar las características de back4app y seguir las estrategias de optimización descritas en este tutorial, puedes construir una red social que escale para manejar miles de usuarios mientras mantienes un alto rendimiento recursos adicionales para seguir ampliando tu conocimiento sobre back4app, aquí hay algunos recursos valiosos documentación de back4app documentación de back4app https //www back4app com/docs/get started/welcome guía de javascript de parse https //docs parseplatform org/js/guide/ canal de youtube de back4app https //www youtube com/c/back4app servidor de parse y livequery documentación de parse server https //docs parseplatform org/parse server/guide/ guía de livequery https //docs parseplatform org/parse server/guide/#live queries seguridad y optimización guía de permisos a nivel de clase https //docs parseplatform org/rest/guide/#class level permissions guía de seguridad acl https //docs parseplatform org/js/guide/#security optimización del rendimiento https //docs parseplatform org/js/guide/#performance funciones avanzadas guía de cloud code https //docs parseplatform org/cloudcode/guide/ trabajos en segundo plano https //docs parseplatform org/cloudcode/guide/#cloud jobs notificaciones push https //docs parseplatform org/js/guide/#push notifications recuerda que construir una red social exitosa es un proceso iterativo comienza con una base sólida (que ahora tienes), recopila comentarios de los usuarios y mejora continuamente tu aplicación en función de los patrones de uso del mundo real esperamos que este tutorial te haya proporcionado el conocimiento y la confianza para construir aplicaciones increíbles con back4app ¡feliz codificación!