Project Templates
Social Network
Comment construire un backend pour un réseau social ?
49 min
introduction dans ce guide, vous découvrirez comment mettre en place un backend robuste pour un réseau social en utilisant une plateforme de backend en tant que service ce backend sera conçu pour gérer l'authentification des utilisateurs, la gestion des données, la communication en temps réel, et plus encore—tout en garantissant la sécurité et l'efficacité à la fin de ce tutoriel, votre projet ressemblera à ceci aperçu du réseau social astuce pour le code source complet, visitez notre dépôt github à github https //github com/templates back4app/back4gram points clés à retenir gestion simplifiée du backend profitez du backend géré de back4app pour rationaliser l'authentification des utilisateurs, le stockage des données et les fonctionnalités en temps réel interactions en temps réel utilisez livequery pour permettre la messagerie dynamique et les mises à jour de contenu immédiates gestion complète des utilisateurs et du contenu établissez des systèmes robustes pour les profils d'utilisateurs, les publications, les commentaires et les interactions sociales évolutivité et performance appliquez l'optimisation des requêtes, le caching et les opérations par lots pour maintenir une haute performance à mesure que votre réseau se développe intégrations avancées améliorez votre backend avec des fonctions cloud, des tâches en arrière plan et des notifications push pour une expérience utilisateur enrichie prérequis avant de commencer, assurez vous d'avoir un compte back4app inscrivez vous gratuitement sur back4app com https //www back4app com/ node js et npm installez node js (v14 x ou plus récent) depuis nodejs org https //nodejs org/ connaissances de base en javascript et react un éditeur de code utilisez des éditeurs comme visual studio code ou sublime text étape 1 — configurer votre backend back4app créer un nouveau projet sur back4app connectez vous à votre tableau de bord back4app cliquez sur "créer une nouvelle application" nommez votre application (par exemple, "back4social") et choisissez la région du serveur la plus proche de vous une fois le projet créé, vous le verrez listé dans votre tableau de bord back4app ce projet sera la base de toutes les configurations backend discutées dans ce tutoriel conception du schéma de base de données votre backend de réseau social utilisera les classes suivantes utilisateur (fourni par parse par défaut) étendre avec des champs comme bio et photo de profil publication contient du texte et des téléchargements d'images commentaire stocke les commentaires des utilisateurs sur les publications conversation représente une session de chat entre utilisateurs message contient des messages individuels dans une conversation statutdesaisie indique quand un utilisateur est en train de taper ajout de classes de base de données dans back4app, les données sont stockées dans des classes vous pouvez créer une nouvelle classe dans le tableau de bord back4app ou via un agent ia pour créer en utilisant le tableau de bord, veuillez naviguer vers la section “base de données” dans votre tableau de bord back4app créer une nouvelle classe et ajouter des colonnes pertinentes, telles que titre (string) et iscompleted (boolean) votre backend de réseau social utilisera les classes suivantes pour configurer votre base de données allez à la "base de données" section de votre tableau de bord back4app améliorer la classe utilisateur ouvrez la classe existante utilisateur ajouter des colonnes bio (chaîne) avatar (fichier) abonnés (nombre, par défaut 0) suivant (nombre, par défaut 0) créer la classe post cliquez sur "créer une classe" nommez le post et ajoutez contenu (chaîne) auteur (pointeur vers utilisateur ) image (fichier) aime (nombre, par défaut 0) aimépar (tableau) créer la classe commentaire nommez le comment et incluez contenu (chaîne) auteur (pointeur vers utilisateur ) publication (pointeur vers la publication) créer la classe conversation nommez le conversation et ajoutez participants (tableau) derniermessage (chaîne) créer la classe message nommez le message et ajoutez texte (chaîne) expéditeur (pointeur vers utilisateur ) conversation (pointeur vers la conversation) créer la classe typingstatus nommez le typingstatus et ajoutez utilisateur (pointeur vers utilisateur ) conversation (pointeur vers la conversation) estentraindetaper (booléen) pour créer en utilisant l'agent ia, veuillez ouvrez l'agent ia depuis votre tableau de bord d'application ou le menu décrivez votre modèle de données en langage simple laissez l'agent ia créer le schéma automatiquement configurer les autorisations de classe pour protéger vos données, ajustez les autorisations de niveau de classe (clp) allez à "sécurité et clés" dans votre tableau de bord sous "sécurité de niveau de classe" , définissez les règles de lecture/écriture par exemple, autorisez l'accès public en lecture pour post tout en restreignant les opérations de mise à jour/suppression à l'auteur du post activation des fonctionnalités en temps réel avec livequery dans "paramètres du serveur" , localisez parse server et activez livequery inclure ces classes pour la surveillance en temps réel message statut de saisie publication (pour mettre à jour les likes et les commentaires en temps réel) configuration de livequery récupérez vos clés d'application accédez à "paramètres de l'application" > "sécurité et clés" enregistrez votre id d'application , clé javascript , url du serveur , et url du serveur livequery clés de sécurité étape 2 — lier votre frontend avec back4app configuration des variables d'environnement créez un env local fichier dans le répertoire racine de votre projet 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 remplacez les espaces réservés par vos véritables identifiants configuration du sdk parse créez un fichier de configuration pour initialiser parse // src/utils/parseconfig js import parse from 'parse/dist/parse min js'; // initialize parse with your environment variables 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; // enable livequery if configured if (process env react app parse live query url) { parse livequeryserverurl = process env react app parse live query url; } export default parse; ensuite, importez ce fichier dans le point d'entrée de votre application (par exemple, index js ) // src/index js import react from 'react'; import reactdom from 'react dom/client'; import ' /index css'; import app from ' /app'; import ' /utils/parseconfig'; // initialize parse const root = reactdom createroot(document getelementbyid('root')); root render( \<react strictmode> \<app /> \</react strictmode> ); étape 3 — configuration de l'authentification des utilisateurs le serveur parse de back4app offre un système de gestion des utilisateurs intégré via la parse user classe comment parse gère les utilisateurs gère les informations d'identification des utilisateurs (nom d'utilisateur, email, mot de passe) gère l'état d'authentification et les jetons de session simplifie les processus d'inscription et de connexion exemple d'inscription d'utilisateur // function to handle user sign up const handlesignup = async () => { try { const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); // set additional user info user set('bio', ''); user set('followers', 0); user set('following', 0); await user signup(); console log('registration successful'); navigate('/feed'); } catch (error) { console error('sign up error ', error message); // handle errors based on error codes if (error code === 202) { seterrors({ errors, username 'username is taken'}); } else if (error code === 203) { seterrors({ errors, email 'email already exists'}); } } }; exemple de connexion d'utilisateur // function to handle user login const handlelogin = async () => { try { const user = await parse user login(username, password); console log('login successful ', user getusername()); navigate('/feed'); } catch (error) { console error('login error ', error message); setloginerror(error message); } }; vérification d'une session existante const checkcurrentuser = async () => { try { const currentuser = parse user current(); if (currentuser) { console log('logged in as ', currentuser getusername()); return currentuser; } return null; } catch (error) { console error('error checking user ', error); return null; } }; mise en œuvre de la réinitialisation du mot de passe const handlepasswordreset = async () => { try { await parse user requestpasswordreset(email); console log('reset email sent'); setresetemailsent(true); } catch (error) { console error('password reset error ', error message); setreseterror(error message); } }; étape 4 — gestion des publications création de nouvelles publications const createpost = async () => { if (!postcontent trim() && !postimage) { console error('please add content or an image'); return; } try { const post = parse object extend('post'); const newpost = new post(); newpost set('content', postcontent); newpost set('author', parse user current()); newpost set('likes', 0); newpost set('likedby', \[]); if (postimage) { const parsefile = new parse file(postimage name, postimage); await parsefile save(); newpost set('image', parsefile); } await newpost save(); console log('post created successfully'); return newpost; } catch (error) { console error('error creating post ', error message); throw error; } }; étape 4 — gestion des publications récupération des publications const fetchposts = async (page = 0, limit = 10) => { try { const post = parse object extend('post'); const query = new parse query(post); query include('author'); query descending('createdat'); query limit(limit); query skip(page limit); const results = await query find(); 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; } }; implémentation de la fonctionnalité j'aime / ne j'aime pas const togglelike = async (postid) => { try { const currentuser = parse user current(); const userid = currentuser id; const post = parse object extend('post'); const query = new parse query(post); const post = await query get(postid); const likedby = post get('likedby') || \[]; const isliked = likedby includes(userid); if (isliked) { post set('likedby', likedby filter(id => id !== userid)); post set('likes', math max((post get('likes') || 1) 1, 0)); } else { post set('likedby', \[ likedby, userid]); post set('likes', (post get('likes') || 0) + 1); } await post save(); return !isliked; } catch (error) { console error('error toggling like ', error); throw error; } }; étape 5 — activation des commentaires sur les publications ajouter un commentaire const addcomment = async (postid, commentcontent) => { if (!commentcontent trim()) { console error('comment cannot be empty'); return; } try { const post = parse object extend('post'); const postquery = new parse query(post); const post = await postquery get(postid); const comment = parse object extend('comment'); const comment = new comment(); comment set('content', commentcontent); comment set('author', parse user current()); comment set('post', post); await comment save(); console log('comment added successfully'); return comment; } catch (error) { console error('error adding comment ', error); throw error; } }; récupération des commentaires pour une publication spécifique const fetchcomments = async (postid) => { try { const post = parse object extend('post'); const postquery = new parse query(post); const post = await postquery get(postid); const comment = parse object extend('comment'); const query = new parse query(comment); query equalto('post', post); query include('author'); query ascending('createdat'); const results = await query find(); 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; } }; étape 6 — gestion des profils utilisateurs récupération d'un profil utilisateur const fetchuserprofile = async (userid) => { try { const query = new parse query(parse user); const user = await query get(userid); 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(); 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; } }; mise à jour d'un profil utilisateur const updateuserprofile = async (profiledata) => { try { const currentuser = parse user current(); 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); } await currentuser save(); console log('profile updated successfully'); return currentuser; } catch (error) { console error('error updating profile ', error); throw error; } }; étape 7 — mise en œuvre de la messagerie en temps réel la fonctionnalité livequery de back4app permet la messagerie en temps réel et les indicateurs de saisie créer une conversation const createconversation = async (participantids) => { try { const currentuser = parse user current(); const allparticipantids = \[ new set(\[currentuser id, participantids])]; const existingconversation = await findexistingconversation(allparticipantids); if (existingconversation) { return existingconversation; } const participantpointers = await promise all( allparticipantids map(async (id) => { const userquery = new parse query(parse user); return await userquery get(id); }) ); 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 an existing conversation (simplified for demo) const findexistingconversation = async (participantids) => { try { const conversation = parse object extend('conversation'); const query = new parse query(conversation); const results = await query find(); for (const conversation of results) { const participants = conversation get('participants') || \[]; const ids = participants map(p => p id); if (ids length === participantids length && ids every(id => participantids includes(id))) { return conversation; } } return null; } catch (error) { console error('error finding conversation ', error); return null; } }; envoyer un message const sendmessage = async (conversationid, messagetext) => { try { const currentuser = parse user current(); const conversation = parse object extend('conversation'); const conversationquery = new parse query(conversation); const conversation = await conversationquery get(conversationid); 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(); conversation set('lastmessage', messagetext); await conversation save(); console log('message sent successfully'); return message; } catch (error) { console error('error sending message ', error); throw error; } }; s'abonner aux mises à jour de messages via livequery const subscribetomessages = async (conversationid, onnewmessage) => { try { const message = parse object extend('message'); const query = new parse query(message); const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; query equalto('conversation', conversationpointer); query include('sender'); const subscription = await query subscribe(); subscription on('create', (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') }; onnewmessage(newmessage); if (message get('sender') id !== parse user current() id) { markmessageasread(message); } }); return subscription; } catch (error) { console error('error subscribing to messages ', error); throw error; } }; const markmessageasread = async (message) => { try { message set('read', true); await message save(); } catch (error) { console error('error marking message as read ', error); } }; implémentation des indicateurs de saisie const updatetypingstatus = async (conversationid, istyping) => { try { const currentuser = parse user current(); const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; 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) { typingstatus set('istyping', istyping); } else { 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); } }; const subscribetotypingstatus = async (conversationid, ontypingstatuschange) => { try { const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversationid; query equalto('conversation', conversationpointer); query include('user'); const subscription = await query subscribe(); subscription on('update', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); ontypingstatuschange({ userid user id, username user get('username'), istyping }); }); subscription on('create', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); ontypingstatuschange({ userid user id, username user get('username'), istyping }); }); return subscription; } catch (error) { console error('error subscribing to typing status ', error); throw error; } }; étape 8 — ajout de capacités de recherche recherche d'utilisateur const searchusers = async (query, limit = 20) => { try { const userquery = new parse query(parse user); userquery matches('username', new regexp(query, 'i')); userquery limit(limit); const users = await userquery find(); 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; } }; recherche de publication const searchposts = async (query, limit = 20) => { try { const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(query, 'i')); postquery include('author'); postquery descending('createdat'); postquery limit(limit); const posts = await postquery find(); 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; } }; recherche de hashtag const searchhashtags = async (tag, limit = 20) => { try { const hashtagquery = tag startswith('#') ? tag substring(1) tag; const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(`#${hashtagquery}\\\b`, 'i')); postquery include('author'); postquery descending('createdat'); postquery limit(limit); const posts = await postquery find(); 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; } }; étape 9 — exploiter les fonctionnalités avancées de back4app back4app offre des outils avancés comme les fonctions cloud, les tâches en arrière plan et les hooks pour enrichir votre backend fonctions cloud pour la logique côté serveur // cloud function example to notify users when a comment is added parse cloud aftersave("comment", async (request) => { if (request original) return; const comment = request object; const post = comment get("post"); const commenter = request user; 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; 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 }); }); recherche avancée avec le code cloud 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; }); tâches de fond pour les tâches récurrentes parse cloud job("calculatetrendingtopics", async () => { 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); query limit(1000); const posts = await query find({ usemasterkey true }); 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; }); }); const trendingarray = object entries(hashtagcounts) map((\[hashtag, count]) => ({ hashtag, count })) sort((a, b) => b count a count) slice(0, 10); 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"; }); fonctionnalités avancées supplémentaires notifications push utilisez le service de notification push de back4app pour alerter les utilisateurs sur les nouveaux messages contrôle d'accès basé sur les rôles définissez des rôles pour gérer les autorisations pour des actions comme la modération de contenu webhooks intégrez des services externes en déclenchant des webhooks depuis le cloud code étape 10 — optimisation des performances à mesure que votre backend évolue, l'optimisation des performances est essentielle considérez les techniques suivantes indexation de base de données créez des index sur les champs fréquemment interrogés (par exemple, auteur , createdat , nom d'utilisateur ) pour améliorer les performances des requêtes optimisation des requêtes optimisez les requêtes en sélectionnant uniquement les champs nécessaires et en utilisant la pagination pour limiter les ensembles de résultats opérations par lots utilisez des mises à jour et des suppressions par lots pour minimiser les appels api 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; }); posts foreach(post => { object entries(updatedata) foreach((\[key, value]) => { post set(key, value); }); }); await parse object saveall(posts); console log('posts updated successfully'); } catch (error) { console error('error updating posts ', error); throw error; } }; stratégies de mise en cache implémentez la mise en cache côté client pour les données fréquemment accessibles afin de réduire les requêtes api utilisation efficace de livequery abonnez vous uniquement aux données nécessaires et désabonnez vous toujours lorsque les données ne sont plus nécessaires optimisation de la gestion des fichiers redimensionnez les images avant de les télécharger pour économiser de la bande passante et de l'espace de stockage 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 = () => { 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; } const canvas = document createelement('canvas'); canvas width = width; canvas height = height; const ctx = canvas getcontext('2d'); ctx drawimage(img, 0, 0, width, height); canvas toblob(async (blob) => { const resizedfile = new file(\[blob], originalfile name, { type originalfile type, lastmodified date now() }); const parsefile = new parse file(resizedfile name, resizedfile); await parsefile save(); resolve(parsefile); }, originalfile type, 0 8); }; img src = event target result; }; reader readasdataurl(originalfile); } catch (error) { reject(error); } }); }; surveillance et mise à l'échelle utilisez les analyses et les journaux du tableau de bord de back4app pour surveiller l'utilisation et les performances de l'api envisagez de mettre à niveau votre plan ou de mettre en œuvre des solutions de sharding et de cdn à mesure que votre base d'utilisateurs se développe conclusion dans ce tutoriel, vous avez appris à configurer un backend de réseau social avec back4app, couvrant l'authentification des utilisateurs, la gestion des publications, la messagerie en temps réel, les fonctionnalités de recherche et les techniques d'optimisation avancées veuillez également lire notre article de blog détaillant comment développer une application de réseau social bonne programmation et bonne chance avec votre backend de réseau social!