Backend Security Use Firestore Rules to ensure features are secure across the entire stack This lesson is available for PRO members or as a single course purchase. Sign-in and choose a plan below. SignUp for Unlimited PRO Access OR *Enrollment provides full access to this course (and updates) for life. đŧ Login to Watch đ Hearts, Likes, Claps Vercel Deployment đ Complete Firestore Rules file_type_firebase firestore.rules rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { // Required for collection group queries match /posts/{postId} { allow read; } match /users/{userId} { allow read; allow create: if isValidUser(userId); } match /usernames/{username} { allow read; allow create: if isValidUsername(username); } match /users/{userId}/posts/{postId} { allow read; allow create: if canCreatePost(userId); allow update: if canUpdatePost(userId) || canIncrementHearts(userId, postId); allow delete: if request.auth.uid == userId; } match /users/{userId}/posts/{postId}/hearts/{heartId} { allow read; allow write: if request.auth.uid == heartId; } function canCreatePost(userId) { let isOwner = request.auth.uid == userId; let isNow = request.time == request.resource.data.createdAt; let isValidContent = request.resource.data.content.size() < 20000 && request.resource.data.heartCount == 0; let username = get(/databases/$(database)/documents/users/$(request.auth.uid)).data.username; let usernameMatches = username == request.resource.data.username; return isOwner && isNow && isValidContent && usernameMatches; } function canUpdatePost(userId) { let isOwner = request.auth.uid == userId; let isNow = request.time == request.resource.data.updatedAt; let isValidContent = request.resource.data.content.size() < 20000; let doesNotUpdateForbidden = !request.resource.data.diff(resource.data).affectedKeys().hasAny(['uid', 'username', 'heartCount']); return isOwner && isNow && isValidContent && doesNotUpdateForbidden; } function canIncrementHearts(userId, postId) { let hasValidFields = request.resource.data.diff(resource.data).affectedKeys().hasOnly(['heartCount']); let currentUser = request.auth.uid; let heartDocExistsAfter = existsAfter(/databases/$(database)/documents/users/$(userId)/posts/$(postId)/hearts/$(currentUser)); let heartDocExists = exists(/databases/$(database)/documents/users/$(userId)/posts/$(postId)/hearts/$(currentUser)); let heartDocAdded= !heartDocExists && heartDocExistsAfter; let heartDocRemoved = heartDocExists && !heartDocExistsAfter; let countChange = request.resource.data.heartCount - resource.data.heartCount; let validChange = countChange == 1 || countChange == -1; return hasValidFields && validChange && ( countChange == 1 ? heartDocAdded : heartDocRemoved ); } function isValidUser(userId) { let isOwner = request.auth.uid == userId; let username = request.resource.data.username; let createdValidUsername = existsAfter(/databases/$(database)/documents/usernames/$(username)); return isOwner && createdValidUsername; } function isValidUsername(username) { let isOwner = request.auth.uid == request.resource.data.uid; let isValidLength = username.size() >= 3 && username.size() <= 15; let isValidUserDoc = getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.username == username; return isOwner && isValidLength && isValidUserDoc; } } } } Chapters Prerequisites đ¨ 1 README Watch this video before starting the course! free 2:12 âī¸ 2 React Basics Learn the fundamentals of React.js and reactive UI development free 13:15 đĨ 3 Firebase Basics Learn the fundamentals of Firebase Authentication, Firestore, and Storage free 25:22 đšī¸ 4 Next.js Basics Learn the fundamentals of Next.js and server-side rendering free 11:50 App đ§Ŧ 5 Technical Overview Blueprint and technical decisions behind the app free 4:47 đģ 6 Next.js Setup Setup a Next.js app and explore the file system 5:41 đ¤ 7 TypeScript Setup Next.js with TypeScript (optional) 2:07 đĨ 8 Firebase Setup Install and configure Firebase in a Next.js project 5:09 đ 9 Routing Manage dynamic routing and links in Next.js 4:54 đ 10 Loader Create a loading spinner to manage loading states across the app 1:30 đĢ 11 Navbar Create a dynamic navigation bar with React 3:24 đ§ 12 Toast Use react-hot-toast to trigger animated toast messages 1:30 Users đ§đģâđ¤âđ§đŋ 13 Auth Intro Technical overview of Firebase Auth with custom usernames 2:10 đ¤ 14 Google SignIn Authenticate via OAuth with Google SignIn 2:51 đ 15 Auth Context Manage the global auth state with the React Context API 3:07 đŖ 16 Auth Hook Join Firestore data to the current user with a custom react hook 3:57 đ¨âđ¤ 17 Custom Usernames Add custom usernames to Firebase users and asynchronously validate uniqueness 7:02 SSR đ 18 SSR & SEO in Next Technical overview of server-side rendering and SEO free 4:27 đž 19 Data Model How to model data relationships between users, posts, and hearts 2:04 đ 20 SSR User Profile Page Implement server-side rendering to fetch data on the server 5:43 đ 21 SSR + Paginated Home Page Feed Render a feed of the latest posts with a collectionGroup query 3:40 đĻž 22 ISR Incremental Static Regeneration Use incremental static regeneration (ISR) to rebuild pages on the fly 3:45 đ 23 Realtime Data Hydration Transition or hydrate server-rendered content to a realtime stream of data from Firestore 2:30 đ 24 Custom 404 Page Render a custom 404 page for dynamic pages that do not exist. 1:17 đˇī¸ 25 Metatags for SEO Generate dynamic metatags for search engine optimization and social linkbots 2:01 CRUD Features đ 26 Admin Pages Create an AuthCheck component to render content for signed-in users 1:50 đ° 27 Create Data with Firestore Create a new post document in Firestore with a custom slug ID 5:20 âī¸ 28 Post Editing Form Use react-hook-forms to create a form to edit posts in markdown 5:33 âī¸ 29 Form Validation Add form validation with react-hook-forms 2:02 đ 30 Image Uploads Create an image file uploader with Firebase storage 4:55 Hearts đ 31 Hearts, Likes, Claps Create a many-to-many relationship where users can heart many posts 4:12 Deploy đ 32 Backend Security Use Firestore Rules to ensure features are secure across the entire stack 6:30 đ 33 Vercel Deployment Deploy the app with continuous integration on Vercel 2:01 đ 34 Firebase Deployment Deploy the app to Firebase hosting 0:00