Project Templates
Social Network
How to build a backend for a social network?
48 mnt
pendahuluan dalam panduan ini, anda akan menemukan cara untuk mengatur backend yang kuat untuk jaringan sosial menggunakan platform backend sebagai layanan backend ini akan dirancang untuk menangani otentikasi pengguna, manajemen data, komunikasi waktu nyata, dan lebih banyak lagi—semua sambil memastikan keamanan dan efisiensi pada akhir tutorial ini, proyek anda akan terlihat seperti ini ikhtisar jaringan sosial tip untuk kode sumber lengkap, kunjungi repositori github kami di github https //github com/templates back4app/back4gram poin penting manajemen backend yang disederhanakan manfaatkan backend terkelola back4app untuk menyederhanakan otentikasi pengguna, penyimpanan data, dan fungsionalitas waktu nyata interaksi waktu nyata manfaatkan livequery untuk memungkinkan pengiriman pesan dinamis dan pembaruan konten segera penanganan pengguna & konten yang komprehensif membangun sistem yang kuat untuk profil pengguna, pos, komentar, dan interaksi sosial skalabilitas & kinerja terapkan optimasi kueri, caching, dan operasi batch untuk mempertahankan kinerja tinggi saat jaringan anda berkembang integrasi lanjutan tingkatkan backend anda dengan cloud functions, pekerjaan latar belakang, dan notifikasi push untuk pengalaman pengguna yang lebih kaya prasyarat sebelum anda mulai, pastikan anda memiliki akun back4app daftar secara gratis di back4app com https //www back4app com/ node js dan npm instal node js (v14 x atau lebih baru) dari nodejs org https //nodejs org/ pengetahuan dasar tentang javascript dan react editor kode gunakan editor seperti visual studio code atau sublime text langkah 1 — mengonfigurasi backend back4app anda membuat proyek baru di back4app masuk ke dasbor back4app anda klik pada "buat aplikasi baru" beri nama aplikasi anda (misalnya, "back4social") dan pilih wilayah server yang paling dekat dengan anda setelah proyek dibuat, anda akan melihatnya terdaftar di dasbor back4app anda proyek ini akan menjadi dasar untuk semua konfigurasi backend yang dibahas dalam tutorial ini merancang skema basis data backend jaringan sosial anda akan menggunakan kelas kelas berikut pengguna (disediakan oleh parse secara default) perluas dengan bidang seperti bio dan foto profil posting menyimpan konten teks dan unggahan gambar komentar menyimpan komentar pengguna pada postingan percakapan mewakili sesi obrolan antara pengguna pesan berisi pesan individu dalam sebuah percakapan statusmengetik menunjukkan ketika seorang pengguna sedang mengetik menambahkan kelas database di back4app, data disimpan dalam kelas anda dapat membuat kelas baru di dasbor back4app atau melalui agen ai untuk membuat menggunakan dasbor, silakan navigasikan ke bagian “database” di dasbor back4app anda buat kelas baru dan tambahkan kolom yang relevan, seperti judul (string) dan iscompleted (boolean) backend jaringan sosial anda akan menggunakan kelas kelas berikut untuk mengatur basis data anda pergi ke "database" bagian di dasbor back4app anda tingkatkan kelas pengguna buka user kelas yang ada tambahkan kolom bio (string) avatar (berkas) pengikut (jumlah, default 0) mengikuti (nomor, default 0) buat kelas post klik "buat kelas" namakan itu pos dan tambahkan konten (string) penulis (penunjuk ke pengguna ) gambar (berkas) suka (angka, default 0) disukaioleh (array) buat kelas komentar sebutkan komentar dan sertakan konten (string) penulis (penunjuk ke pengguna ) pos (penunjuk ke pos) buat kelas percakapan namakan itu percakapan dan tambahkan peserta (array) pesanterakhir (string) buat kelas pesan namakan itu pesan dan tambahkan teks (string) pengirim (penunjuk ke pengguna ) percakapan (penunjuk ke percakapan) buat kelas typingstatus namakan itu typingstatus dan tambahkan pengguna (penunjuk ke pengguna ) percakapan (penunjuk ke percakapan) sedang mengetik (boolean) untuk membuat menggunakan ai agent silakan buka ai agent dari dasbor aplikasi anda atau menu jelaskan model data anda dalam bahasa yang sederhana biarkan ai agent membuat skema secara otomatis mengonfigurasi izin kelas untuk melindungi data anda, sesuaikan izin tingkat kelas (clp) pergi ke "keamanan & kunci" di dasbor anda di bawah "keamanan tingkat kelas" , atur aturan baca/tulis misalnya, izinkan akses baca publik untuk post sambil membatasi operasi pembaruan/penghapusan kepada penulis pos mengaktifkan fitur waktu nyata dengan livequery di "pengaturan server" , temukan parse server dan aktifkan livequery sertakan kelas kelas ini untuk pemantauan waktu nyata pesan status mengetik pos (untuk memperbarui suka dan komentar secara waktu nyata) pengaturan livequery ambil kunci aplikasi anda navigasi ke "pengaturan aplikasi" > "keamanan & kunci" simpan id aplikasi , kunci javascript , url server , dan url server livequery kunci keamanan langkah 2 — menghubungkan frontend anda dengan back4app mengatur variabel lingkungan buat sebuah env local file di direktori root proyek anda 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 ganti placeholder dengan kredensial anda yang sebenarnya mengonfigurasi sdk parse buat file konfigurasi untuk menginisialisasi 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; kemudian impor file ini di titik masuk aplikasi anda (misalnya, 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> ); step 3 — setting up user authentication back4app’s parse server offers a built in user management system via the parse user class how parse manages users handles user credentials (username, email, password) manages authentication state and session tokens simplifies registration and login processes user registration example // 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'}); } } }; user login example // 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); } }; memeriksa sesi yang ada 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; } }; implementasi reset kata sandi 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); } }; langkah 4 — menangani postingan membuat postingan baru 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; } }; langkah 4 — menangani postingan mengambil postingan 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; } }; mengimplementasikan fitur suka/tidak suka 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; } }; langkah 5 — mengaktifkan komentar pada postingan menambahkan komentar 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; } }; mengambil komentar untuk postingan tertentu 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; } }; langkah 6 — mengelola profil pengguna mengambil profil pengguna 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; } }; memperbarui profil pengguna 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; } }; langkah 7 — menerapkan pesan real time fitur livequery back4app memungkinkan pengiriman pesan real time dan indikator pengetikan membuat percakapan 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; } }; mengirim pesan 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; } }; berlangganan pembaruan pesan melalui 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); } }; mengimplementasikan indikator mengetik 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; } }; langkah 8 — menambahkan kemampuan pencarian pencarian pengguna 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; } }; pencarian post 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; } }; pencarian 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; } }; langkah 9 — memanfaatkan fitur lanjutan back4app back4app menawarkan alat canggih seperti cloud functions, pekerjaan latar belakang, dan hooks untuk memperkaya backend anda cloud functions untuk logika sisi server // 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 }); }); pencarian lanjutan dengan 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; }); pekerjaan latar belakang untuk tugas berulang 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"; }); fitur lanjutan tambahan notifikasi push gunakan layanan push back4app untuk memberi tahu pengguna tentang pesan baru kontrol akses berdasarkan peran definisikan peran untuk mengelola izin untuk tindakan seperti moderasi konten webhooks integrasikan layanan eksternal dengan memicu webhook dari cloud code langkah 10 — optimasi kinerja seiring dengan berkembangnya backend anda, penyetelan kinerja sangat penting pertimbangkan teknik teknik berikut pengindeksan basis data buat indeks pada bidang yang sering ditanyakan (misalnya, penulis , dibuatpada , namapengguna ) untuk meningkatkan kinerja kueri optimasi kueri optimalkan kueri dengan memilih hanya bidang yang diperlukan dan menggunakan paginasi untuk membatasi set hasil operasi batch manfaatkan pembaruan dan penghapusan batch untuk meminimalkan panggilan 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; } }; strategi caching terapkan caching sisi klien untuk data yang sering diakses untuk mengurangi permintaan api penggunaan livequery yang efisien berlangganan hanya pada data yang diperlukan dan selalu berhenti berlangganan ketika data tidak lagi dibutuhkan optimisasi penanganan file ubah ukuran gambar sebelum diunggah untuk menghemat bandwidth dan penyimpanan 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); } }); }; pemantauan dan penskalaan gunakan analitik dan log dasbor back4app untuk memantau penggunaan dan kinerja api pertimbangkan untuk meningkatkan rencana anda atau menerapkan solusi sharding dan cdn seiring pertumbuhan basis pengguna anda kesimpulan dalam tutorial ini, anda belajar bagaimana cara mengatur backend jejaring sosial dengan back4app, mencakup otentikasi pengguna, manajemen pos, pesan waktu nyata, fitur pencarian, dan teknik optimisasi lanjutan silakan juga baca posting blog kami yang merinci cara mengembangkan aplikasi media sosial selamat coding dan semoga sukses dengan backend jejaring sosial anda!