Project Templates
Social Network
How to Create a Social Media Platform: Step-by-Step Guide (2025)
69 mnt
pendahuluan dalam tutorial ini, anda akan belajar bagaimana membangun platform jejaring sosial seperti instagram menggunakan back4app sebagai layanan backend anda back4app menyediakan backend parse server yang dikelola yang menyederhanakan otentikasi pengguna, penyimpanan data, unggahan file, dan fitur real time tanpa memerlukan infrastruktur server yang kompleks dengan menyelesaikan tutorial ini, anda akan membangun jejaring sosial yang lengkap dengan otentikasi pengguna (daftar, masuk, reset kata sandi) manajemen profil pembuatan postingan dengan unggahan gambar interaksi sosial (suka, komentar) pesan real time dengan indikator pengetikan fungsi pencarian konten prabaca jejaring sosial antarmuka obrolan profil pengguna halaman umpan di mana saja anda dapat mengakses kode lengkap di github prasyarat untuk menyelesaikan tutorial ini, anda akan membutuhkan akun back4app daftar untuk akun gratis di back4app com https //www back4app com/ node js dan npm terinstal di mesin lokal anda instal node js (versi 14 x atau lebih baru) dan npm dari nodejs org https //nodejs org/ pemahaman dasar tentang javascript dan react editor kode editor kode modern seperti visual studio code atau sublime text langkah 1 — menyiapkan backend back4app anda pertama, mari kita buat proyek back4app baru dan menyiapkan skema basis data untuk jejaring sosial kita membuat proyek back4app baru masuk ke akun back4app anda dan navigasikan ke dasbor klik pada "buat aplikasi baru" masukkan "back4gram" sebagai nama aplikasi anda, pilih wilayah server terdekat, dan klik "buat" memahami skema database jaringan sosial kami memerlukan kelas kelas berikut di back4app pengguna (sudah ada secara default di parse) akan diperluas dengan tambahan field seperti bio dan avatar postingan menyimpan postingan pengguna termasuk konten teks dan gambar komentar menyimpan komentar pada postingan percakapan mewakili percakapan chat antara pengguna pesan pesan individu dalam sebuah percakapan statusmengetik melacak ketika pengguna sedang mengetik dalam sebuah percakapan membuat kelas database mari kita buat kelas kelas ini di database back4app anda navigasikan ke bagian "database" di dasbor back4app anda memperluas kelas pengguna klik pada kelas "user" yang sudah ada tambahkan kolom berikut bio (tipe string) avatar (tipe berkas) pengikut (tipe angka, default 0) berikut (tipe angka, default 0) membuat kelas post klik "buat kelas" masukkan "post" sebagai nama kelas dan pilih "buat kelas kosong" tambahkan kolom berikut konten (tipe string) penulis (tipe pointer ke user) gambar (tipe file) suka (tipe angka, default 0) disukaioleh (tipe array) dibuatpada (tipe tanggal, ditambahkan secara otomatis) membuat kelas komentar buat kelas baru bernama "komentar" dengan kolom kolom ini konten (tipe string) penulis (tipe pointer ke pengguna) pos (tipe pointer ke pos) dibuatpada (tipe tanggal, ditambahkan secara otomatis) membuat kelas percakapan buat kelas baru bernama "percakapan" dengan kolom kolom ini peserta (tipe array) pesanterakhir (tipe string) diperbaruipada (tipe tanggal, ditambahkan secara otomatis) membuat kelas pesan buat kelas baru bernama "pesan" dengan kolom kolom ini teks (tipe string) pengirim (tipe pointer ke user) percakapan (tipe pointer ke percakapan) dibuatpada (tipe tanggal, ditambahkan secara otomatis) membuat kelas typingstatus buat kelas baru bernama "typingstatus" dengan kolom kolom ini pengguna (tipe pointer ke pengguna) percakapan (tipe pointer ke percakapan) sedangmengetik (tipe boolean) mengatur izin kelas untuk mengamankan data aplikasi anda, konfigurasikan daftar kontrol akses (acl) yang sesuai untuk setiap kelas navigasikan ke bagian "keamanan & kunci" di dasbor back4app di bawah "keamanan tingkat kelas", atur izin untuk setiap kelas misalnya, pada kelas post akses baca publik diaktifkan (semua orang dapat melihat pos) akses tulis publik diaktifkan (pengguna yang terautentikasi dapat membuat pos) tambahkan clp untuk pembaruan/penghapusan agar dibatasi hanya untuk penulis mengatur livequery untuk fitur waktu nyata untuk mengaktifkan fitur waktu nyata seperti pesan dan indikator pengetikan navigasikan ke "pengaturan server" di dasbor back4app di bawah "parse server", temukan "livequery" dan aktifkan tambahkan kelas kelas ini untuk dipantau oleh livequery pesan status mengetik pos (untuk pembaruan waktu nyata pada suka dan komentar) mendapatkan kunci aplikasi anda anda akan memerlukan kunci aplikasi back4app anda untuk menghubungkan frontend anda navigasikan ke "pengaturan aplikasi" > "keamanan & kunci" catat kunci berikut id aplikasi kunci javascript url server url server livequery kunci keamanan langkah 2 — menghubungkan frontend anda ke back4app mari kita atur koneksi antara frontend anda dan backend back4app membuat variabel lingkungan buat sebuah env local file di root proyek anda untuk menyimpan kredensial back4app 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 nilai placeholder dengan kredensial back4app anda yang sebenarnya mengonfigurasi parse sdk dengan back4app buat file konfigurasi untuk menginisialisasi parse dengan kredensial back4app anda // 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; kemudian impor konfigurasi ini di titik masuk aplikasi anda // 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> ); langkah 3 — mengimplementasikan autentikasi dengan back4app server parse back4app menyediakan sistem manajemen pengguna yang komprehensif melalui kelas parse user memahami autentikasi pengguna parse kelas parse user dirancang khusus untuk manajemen pengguna menyimpan kredensial pengguna (nama pengguna, email, kata sandi) mengelola status autentikasi menangani token sesi secara otomatis mengimplementasikan pendaftaran pengguna berikut cara mengimplementasikan pendaftaran pengguna dengan 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'}); } } }; mengimplementasikan login pengguna berikut cara mengimplementasikan login pengguna dengan 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); } }; memeriksa pengguna saat ini parse secara otomatis menyimpan token sesi, memungkinkan anda untuk memeriksa apakah pengguna sudah masuk // 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; } }; mengimplementasikan reset kata sandi back4app menyediakan alur reset kata sandi bawaan // 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); } }; langkah 4 — membuat dan menampilkan postingan sekarang mari kita implementasikan pembuatan dan pengambilan postingan menggunakan back4app membuat postingan berikut cara untuk membuat pos baru dengan 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; } }; mekanisme kunci back4app parse object extend('post') mengacu pada kelas post di back4app new post() membuat instance baru dari kelas post parsefile save() mengunggah file ke penyimpanan back4app newpost save() menyimpan objek pos ke back4app mengambil pos berikut cara untuk mengambil pos dari 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; } }; mekanisme back4app kunci new parse query(post) membuat kueri pada kelas post query include('author') melakukan operasi mirip join untuk mengambil objek terkait query descending('createdat') mengurutkan hasil berdasarkan tanggal pembuatan query limit() dan query skip() menerapkan paginasi post get('image') url() mendapatkan url dari objek parse file mengimplementasikan fungsionalitas suka berikut cara mengimplementasikan suka pada pos // 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; } }; langkah 5 — mengimplementasikan komentar pada pos mari kita implementasikan fungsionalitas komentar menggunakan back4app membuat komentar berikut cara menambahkan komentar ke sebuah pos // 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; } }; mengambil komentar untuk sebuah pos berikut cara mengambil komentar untuk pos tertentu // 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; } }; langkah 6 — mengimplementasikan profil pengguna sekarang mari kita implementasikan profil pengguna menggunakan back4app mengambil data pengguna berikut adalah cara untuk mengambil data profil pengguna // 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; } }; memperbarui profil pengguna berikut cara memperbarui profil pengguna // 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; } }; langkah 7 — mengimplementasikan pesan waktu nyata dengan livequery sekarang mari kita implementasikan pesan waktu nyata menggunakan fitur livequery dari back4app membuat percakapan berikut cara membuat percakapan baru // 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; } }; mengirim pesan berikut cara mengirim pesan dalam sebuah percakapan // 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; } }; mengatur livequery untuk pesan berikut cara berlangganan pembaruan pesan secara real time // 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); } }; mengimplementasikan indikator pengetikan berikut adalah cara untuk mengimplementasikan indikator pengetikan dengan 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; } }; langkah 8 — mengimplementasikan fungsionalitas pencarian mari kita implementasikan fungsionalitas pencarian menggunakan sistem query back4app mencari pengguna berikut adalah cara untuk mencari pengguna // 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; } }; mencari hashtag berikut cara mencari pos dengan hashtag tertentu // 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; } }; langkah 9 — mengimplementasikan fitur back4app yang lanjutan mari kita eksplorasi beberapa fitur back4app yang lanjutan yang dapat meningkatkan aplikasi jejaring sosial anda fungsi cloud back4app memungkinkan anda untuk menerapkan logika sisi server menggunakan fungsi cloud ini adalah fungsi javascript yang berjalan di server dan dapat dipanggil dari aplikasi klien anda berikut adalah contoh fungsi cloud untuk melacak notifikasi komentar // 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 }); }); untuk menerapkan ini pergi ke dasbor back4app anda navigasi ke "kode cloud" > "fungsi cloud" buat fungsi baru dengan kode di atas terapkan fungsi tersebut kode cloud untuk pencarian lanjutan untuk fungsi pencarian yang lebih kompleks, anda dapat membuat cloud function // in your back4app cloud code section parse cloud define("advancedsearch", async (request) => { const { query, type, limit = 20 } = request params; if (!query) { throw new error("search query is required"); } let results = \[]; switch (type) { case 'users' const userquery = new parse query(parse user); userquery matches('username', new regexp(query, 'i')); userquery limit(limit); results = await userquery find({ usemasterkey true }); break; case 'posts' const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(query, 'i')); postquery include('author'); postquery limit(limit); results = await postquery find({ usemasterkey true }); break; default throw new error("invalid search type"); } return results; }); untuk memanggil fungsi ini dari klien anda // 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; } }; mengimplementasikan pekerjaan latar belakang untuk tugas berulang seperti perhitungan topik yang sedang tren, anda dapat menggunakan pekerjaan latar belakang // 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"; }); untuk mengatur pekerjaan ini pergi ke dasbor back4app anda navigasi ke "cloud code" > "background jobs" buat pekerjaan baru dengan kode di atas jadwalkan untuk dijalankan setiap hari atau mingguan menggunakan parse server hooks back4app memungkinkan anda untuk menerapkan hook sisi server yang secara otomatis dijalankan sebelum atau setelah operasi tertentu ini berguna untuk validasi data, modifikasi, atau memicu efek samping // 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}`); }); mengimplementasikan notifikasi push back4app menyediakan sistem notifikasi push yang kuat yang memungkinkan anda mengirim notifikasi ke perangkat pengguna anda berikut cara mengimplementasikan notifikasi push pertama, atur konfigurasi push di dasbor back4app anda pergi ke "pengaturan aplikasi" > "push" konfigurasikan kredensial ios dan/atau android anda kirim notifikasi push ketika pengguna menerima pesan baru // 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 }); } } }); menggunakan kontrol akses berbasis peran back4app menyediakan sistem kontrol akses berbasis peran yang memungkinkan anda mengelola izin pada tingkat yang lebih rinci // 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; } }; mengimplementasikan webhook back4app memungkinkan anda untuk mengatur webhook untuk mengintegrasikan dengan layanan eksternal // 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); } }); langkah 10 — mengoptimalkan kinerja back4app seiring pertumbuhan jejaring sosial anda, anda perlu mengoptimalkan backend back4app anda untuk kinerja dan skalabilitas bagian ini mencakup strategi penting untuk memastikan aplikasi anda tetap cepat dan responsif bahkan saat basis pengguna anda berkembang optimasi database desain dan kueri database yang efisien sangat penting untuk kinerja aplikasi membuat indeks indeks secara dramatis meningkatkan kinerja kueri pada bidang yang sering dicari pergi ke dasbor back4app anda navigasi ke "database browser" > pilih kelas (misalnya, post) klik pada tab "indexes" buat indeks untuk bidang yang sering dicari // 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); } }; bidang kunci untuk diindeks penulis di kelas post untuk kueri spesifik pengguna yang lebih cepat createdat untuk pemuatan timeline yang lebih cepat username di kelas user untuk pencarian pengguna yang lebih cepat peserta di kelas conversation untuk penyaringan pesan yang lebih cepat optimasi kuery optimalkan kuery anda untuk mengurangi beban server dan waktu respons // 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; }; operasi batch untuk operasi yang melibatkan beberapa objek, gunakan operasi batch untuk mengurangi panggilan 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; } }; mengimplementasikan cache terapkan caching sisi klien untuk data yang sering diakses untuk mengurangi panggilan 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); }; menggunakan livequery secara efisien livequery sangat kuat untuk fitur real time tetapi harus digunakan secara efisien untuk menghindari masalah kinerja // 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(); } }; praktik terbaik untuk livequery hanya berlangganan data yang memerlukan pembaruan waktu nyata batasi bidang yang anda minta dengan select() selalu berhenti berlangganan ketika data tidak lagi diperlukan gunakan batasan untuk mempersempit cakupan langganan pertimbangkan untuk menggabungkan livequery dengan kueri reguler untuk pemuatan data awal mengoptimalkan penanganan file penanganan file yang efisien sangat penting untuk jaringan sosial dengan unggahan gambar // 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 }; }; memantau aplikasi back4app anda back4app menyediakan beberapa alat untuk memantau kinerja aplikasi anda analitik dasbor memberikan statistik penggunaan tingkat tinggi memantau permintaan api, penggunaan penyimpanan, dan operasi file mengidentifikasi pola penggunaan dan waktu puncak log menyediakan informasi rinci tentang operasi tinjau log server untuk kesalahan dan masalah kinerja saring log berdasarkan jenis untuk mengisolasi masalah metrik kinerja melacak waktu respons dan kesehatan sistem memantau kinerja kueri mengidentifikasi operasi yang lambat konfigurasikan peringatan untuk diberitahu tentang masalah potensial pergi ke "pengaturan aplikasi" > "peringatan" di dasbor back4app atur peringatan untuk penggunaan api tinggi (mendekati batas rencana anda) lonjakan tingkat kesalahan (menunjukkan masalah aplikasi) waktu henti server (mempengaruhi semua pengguna) penggunaan penyimpanan (mendekati kapasitas) // 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; } }; strategi skala seiring pertumbuhan aplikasi anda, pertimbangkan strategi skala berikut tingkatkan rencana back4app anda pindah ke tingkat yang lebih tinggi saat basis pengguna anda berkembang pantau penggunaan dan tingkatkan sebelum anda mencapai batas terapkan sharding untuk koleksi besar pecah data di berbagai kelas untuk dataset yang sangat besar misalnya, pisahkan pesan berdasarkan tahun atau bulan // 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; } }; implementasi server cache gunakan redis atau solusi caching lainnya untuk data yang sering diakses implementasikan melalui cloud code dan layanan eksternal gunakan jaringan pengiriman konten (cdn) konfigurasi cdn untuk pengiriman file yang lebih cepat terutama penting untuk gambar dan file media optimasi keamanan keamanan dan kinerja saling terkait erat optimasi ini meningkatkan keduanya terapkan izin pointer gunakan izin pointer untuk membatasi akses ke objek terkait ini memungkinkan kueri yang lebih efisien tanpa mengorbankan keamanan // 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); } }; gunakan acl parse secara efisien acl memberikan keamanan tetapi dapat mempengaruhi kinerja jika digunakan secara berlebihan pertimbangkan untuk menggunakan izin tingkat kelas untuk kasus umum dan acl untuk pengecualian // 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; }; optimasi server back4app optimalkan konfigurasi parse server anda di back4app konfigurasi parameter server pergi ke "pengaturan aplikasi" > "pengaturan server" sesuaikan pengaturan timeout untuk operasi yang berjalan lama konfigurasi batas maksimum permintaan atur pembatasan laju terapkan pembatasan laju untuk mencegah penyalahgunaan dan memastikan penggunaan yang adil konfigurasi melalui cloud code seperti yang ditunjukkan sebelumnya dengan menerapkan strategi optimasi kinerja ini, aplikasi jejaring sosial anda akan siap untuk menangani pertumbuhan dan mempertahankan kinerja yang sangat baik, bahkan saat basis pengguna anda berkembang ingatlah untuk terus memantau kinerja aplikasi anda dan melakukan perbaikan bertahap sesuai kebutuhan kesimpulan dalam tutorial ini, anda telah belajar bagaimana membangun aplikasi jejaring sosial yang lengkap menggunakan back4app sebagai layanan backend anda anda telah mengimplementasikan fitur inti seperti autentikasi pengguna dan manajemen profil pembuatan pos dan interaksi sosial pesan waktu nyata dengan livequery fungsi pencarian fitur lanjutan seperti cloud functions dan background jobs parse server back4app menyediakan solusi backend yang kuat dan dapat diskalakan yang memungkinkan anda fokus pada membangun pengalaman pengguna yang hebat tanpa khawatir tentang infrastruktur server yang kompleks langkah selanjutnya untuk lebih meningkatkan aplikasi jejaring sosial anda, pertimbangkan mengimplementasikan notifikasi push menggunakan layanan push back4app menambahkan analitik untuk melacak keterlibatan pengguna mengatur opsi penyimpanan file untuk file media yang lebih besar mengimplementasikan moderasi konten menggunakan cloud functions membuat aplikasi seluler menggunakan react native dengan backend back4app yang sama dengan memanfaatkan fitur back4app dan mengikuti strategi optimasi yang dijelaskan dalam tutorial ini, anda dapat membangun jejaring sosial yang dapat diskalakan untuk menangani ribuan pengguna sambil mempertahankan kinerja tinggi sumber daya tambahan untuk terus memperluas pengetahuan anda tentang back4app, berikut adalah beberapa sumber daya yang berharga dokumentasi back4app dokumentasi back4app https //www back4app com/docs/get started/welcome panduan javascript parse https //docs parseplatform org/js/guide/ saluran youtube back4app https //www youtube com/c/back4app parse server dan livequery dokumentasi parse server https //docs parseplatform org/parse server/guide/ panduan livequery https //docs parseplatform org/parse server/guide/#live queries keamanan dan optimasi panduan izin tingkat kelas https //docs parseplatform org/rest/guide/#class level permissions panduan keamanan acl https //docs parseplatform org/js/guide/#security optimisasi kinerja https //docs parseplatform org/js/guide/#performance fitur lanjutan panduan kode cloud https //docs parseplatform org/cloudcode/guide/ pekerjaan latar belakang https //docs parseplatform org/cloudcode/guide/#cloud jobs notifikasi push https //docs parseplatform org/js/guide/#push notifications ingatlah bahwa membangun jaringan sosial yang sukses adalah proses iteratif mulailah dengan fondasi yang solid (yang sekarang anda miliki), kumpulkan umpan balik pengguna, dan terus tingkatkan aplikasi anda berdasarkan pola penggunaan di dunia nyata kami berharap tutorial ini telah memberikan anda pengetahuan dan kepercayaan diri untuk membangun aplikasi yang luar biasa dengan back4app selamat coding!