Project Templates
Social Network
Wie man ein Backend für ein soziales Netzwerk aufbaut?
49 min
einführung in diesem leitfaden erfahren sie, wie sie ein robustes backend für ein soziales netzwerk mit einer backend as a service plattform dieses backend wird so konzipiert, dass es die benutzerauthentifizierung, datenverwaltung, echtzeitkommunikation und mehr verarbeitet – und dabei sicherheit und effizienz gewährleistet am ende dieses tutorials wird ihr projekt so aussehen übersicht über das soziale netzwerk tipp für den vollständigen quellcode besuchen sie unser github repository unter github https //github com/templates back4app/back4gram wichtige erkenntnisse vereinfachte backend verwaltung nutzen sie das verwaltete backend von back4app, um die benutzerauthentifizierung, datenspeicherung und echtzeitfunktionen zu optimieren echtzeit interaktionen nutzen sie livequery, um dynamische nachrichten und sofortige inhaltsaktualisierungen zu ermöglichen umfassende benutzer und inhaltsverwaltung etablieren sie robuste systeme für benutzerprofile, beiträge, kommentare und soziale interaktionen skalierbarkeit & leistung wenden sie abfrageoptimierung, caching und batch operationen an, um eine hohe leistung aufrechtzuerhalten, während ihr netzwerk wächst erweiterte integrationen verbessern sie ihr backend mit cloud funktionen, hintergrundjobs und push benachrichtigungen für ein reichhaltigeres benutzererlebnis voraussetzungen bevor sie beginnen, stellen sie sicher, dass sie ein back4app konto registrieren sie sich kostenlos unter back4app com https //www back4app com/ node js und npm installieren sie node js (v14 x oder neuer) von nodejs org https //nodejs org/ grundkenntnisse in javascript und react ein code editor verwenden sie editoren wie visual studio code oder sublime text schritt 1 — konfigurieren sie ihr back4app backend erstellen eines neuen projekts auf back4app melden sie sich bei ihrem back4app dashboard an klicken sie auf "erstellen sie eine neue app" benennen sie ihre app (zum beispiel "back4social") und wählen sie die serverregion, die ihnen am nächsten ist sobald das projekt erstellt ist, sehen sie es in ihrem back4app dashboard aufgelistet dieses projekt wird die grundlage für alle backend konfigurationen sein, die in diesem tutorial besprochen werden entwerfen des datenbankschemas ihr backend für soziale netzwerke verwendet die folgenden klassen benutzer (standardmäßig von parse bereitgestellt) erweitern sie mit feldern wie biografie und profilbild beitrag hält textinhalte und bild uploads kommentar speichert benutzerkommentare zu beiträgen unterhaltung stellt eine chatsitzung zwischen benutzern dar nachricht enthält einzelne nachrichten in einer unterhaltung tippstatus zeigt an, wenn ein benutzer tippt datenbankklassen hinzufügen in back4app werden daten in klassen sie können eine neue klasse im back4app dashboard oder über einen ki agenten erstellen um mit dem dashboard zu erstellen, bitte navigieren sie zum abschnitt „datenbank“ in ihrem back4app dashboard erstellen sie eine neue klasse und fügen sie relevante spalten hinzu, wie titel (string) und iscompleted (boolean) ihr backend für soziale netzwerke wird die folgenden klassen verwenden um ihre datenbank einzurichten gehe zum "datenbank" abschnitt auf deinem back4app dashboard verbessern sie die benutzerklasse öffnen sie die vorhandene benutzer klasse spalten hinzufügen biografie (string) avatar (datei) follower (anzahl, standard 0) folgend (nummer, standard 0) erstellen sie die post klasse klicken sie "eine klasse erstellen" nenne es post und füge hinzu inhalt (string) autor (zeiger auf benutzer ) bild (datei) mag (zahl, standard 0) mochte von (array) erstellen sie die kommentar klasse nenne es kommentar und füge hinzu inhalt (string) autor (zeiger auf benutzer ) beitrag (zeiger auf beitrag) erstellen sie die conversation klasse nenne es gespräch und füge hinzu teilnehmer (array) letztenachricht (string) erstellen sie die message klasse nenne es nachricht und füge hinzu text (string) absender (zeiger auf benutzer ) gespräch (verweis auf gespräch) erstellen sie die typingstatus klasse nennen sie es typingstatus und fügen sie hinzu benutzer (zeiger auf benutzer ) gespräch (verweis auf gespräch) schreibt (boolean) um mit dem ki agenten zu erstellen, bitte öffnen sie den ki agenten von ihrem app dashboard oder dem menü beschreiben sie ihr datenmodell in einfacher sprache lassen sie den ki agenten das schema automatisch erstellen konfigurieren von klassenberechtigungen um ihre daten zu schützen, passen sie die klassenebene berechtigungen (clps) an gehen sie zu "sicherheit & schlüssel" in ihrem dashboard unter "klassenebene sicherheit" , legen sie lese /schreibregeln fest zum beispiel, erlauben sie öffentlichen lesezugriff für beitrag und beschränken sie die update /löschoperationen auf den autor des beitrags echtzeitfunktionen mit livequery aktivieren in "servereinstellungen" , suchen sie parse server und aktivieren sie livequery fügen sie diese klassen für die echtzeitüberwachung hinzu nachricht tippstatus beitrag (um likes und kommentare in echtzeit zu aktualisieren) livequery setup rufen sie ihre anwendungs schlüssel ab navigieren sie zu "app einstellungen" > "sicherheit & schlüssel" speichern sie ihre anwendungs id , javascript schlüssel , server url , und livequery server url sicherheitsschlüssel schritt 2 — verknüpfung ihres frontends mit back4app umgebungsvariablen einrichten erstellen sie eine env local datei im stammverzeichnis ihres projekts 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 ersetzen sie die platzhalter durch ihre tatsächlichen anmeldeinformationen konfigurieren des parse sdk erstellen sie eine konfigurationsdatei, um parse zu initialisieren // 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; importieren sie dann diese datei im einstiegspunkt ihrer app (z b 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> ); schritt 3 — benutzer authentifizierung einrichten der parse server von back4app bietet ein integriertes benutzermanagementsystem über die parse user klasse wie parse benutzer verwaltet verwaltet benutzeranmeldeinformationen (benutzername, e mail, passwort) verwaltet den authentifizierungsstatus und sitzungstoken vereinfacht registrierungs und anmeldeprozesse beispiel für die benutzerregistrierung // 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'}); } } }; beispiel für die benutzeranmeldung // 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); } }; überprüfung auf eine bestehende sitzung 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; } }; implementierung der passwortzurücksetzung 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); } }; schritt 4 — handhabung von beiträgen erstellen neuer beiträge 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; } }; beiträge abrufen 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; } }; implementierung der like/unlike funktion 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; } }; schritt 5 — kommentare zu beiträgen aktivieren einen kommentar hinzufügen 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; } }; kommentare für einen bestimmten beitrag abrufen 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; } }; schritt 6 — verwaltung von benutzerprofilen abrufen eines benutzerprofils 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; } }; aktualisieren eines benutzerprofils 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; } }; schritt 7 — implementierung von echtzeitnachrichten die livequery funktion von back4app ermöglicht echtzeitnachrichten und tippindikatoren ein gespräch erstellen 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; } }; eine nachricht senden 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; } }; abonnieren von nachrichtenaktualisierungen über 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); } }; implementierung von tippindikatoren 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; } }; schritt 8 — hinzufügen von suchfunktionen benutzersuche 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; } }; beitragsuche 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; } }; hashtag suche 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; } }; schritt 9 — nutzung fortgeschrittener back4app funktionen back4app bietet fortschrittliche tools wie cloud funktionen, hintergrundjobs und hooks, um ihr backend zu bereichern cloud funktionen für serverseitige logik // 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 }); }); erweiterte suche mit cloud code 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; }); hintergrundjobs für wiederkehrende aufgaben 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"; }); zusätzliche erweiterte funktionen push benachrichtigungen verwenden sie den push dienst von back4app, um benutzer über neue nachrichten zu informieren rollenbasierte zugriffskontrolle definieren sie rollen, um berechtigungen für aktionen wie inhaltsmoderation zu verwalten webhooks integrieren sie externe dienste, indem sie webhooks aus dem cloud code auslösen schritt 10 — leistungsoptimierung wenn ihr backend skaliert, ist die leistungsoptimierung entscheidend berücksichtigen sie die folgenden techniken datenbankindizierung erstellen sie indizes für häufig abgefragte felder (z b author , createdat , username ) zur verbesserung der abfrageleistung abfrageoptimierung optimieren sie abfragen, indem sie nur die erforderlichen felder auswählen und die paginierung verwenden, um die ergebnismengen zu begrenzen batch operationen verwenden sie batch updates und löschungen, um api aufrufe zu minimieren 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; } }; caching strategien implementieren sie clientseitiges caching für häufig abgerufene daten, um api anfragen zu reduzieren effiziente livequery nutzung abonnieren sie nur die notwendigen daten und kündigen sie immer das abonnement, wenn die daten nicht mehr benötigt werden optimierung der dateiverwaltung ändern sie die größe von bildern vor dem hochladen, um bandbreite und speicherplatz zu sparen 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); } }); }; überwachung und skalierung verwenden sie die dashboard analysen und protokolle von back4app, um die api nutzung und leistung zu überwachen erwägen sie, ihren plan zu aktualisieren oder sharding und cdn lösungen zu implementieren, während ihre benutzerbasis wächst fazit in diesem tutorial haben sie gelernt, wie sie ein backend für ein soziales netzwerk mit back4app einrichten, einschließlich benutzerauthentifizierung, beitragsverwaltung, echtzeitnachrichten, suchfunktionen und fortgeschrittenen optimierungstechniken bitte lesen sie auch unseren blogbeitrag, der wie man eine social media app entwickelt viel spaß beim programmieren und viel erfolg mit ihrem backend für das soziale netzwerk!