Leveraging React and Firebase for Efficient CRUD Operations in a College Management System

A Secure React-based Web application with Firebase Authentication and Scalable Document Database.

Prabir Kalwani
7 min readMar 24, 2024

Today, we( Prabir Kalwani , Snehil Sinha, Aayush Shah, Akshad Gawde, Karan Dwari, and ShashankShekhar) will be guiding you through the process of building a Secure React-based Web application with Firebase Authentication and Scalable Document Database.

Introduction

This idea came to fruition by being frustrated by the college portal’s sluggish updates for assignments and attendance tracking. Waiting for changes felt like an eternity, and seeing attendance promptly? Forget about it! That’s when our engineering spirit started to work: why not create a prototype Firebase and Cloud Firestore integration with React?

Before we begin lets understand why are we using React / Firebase in the first case ?

Firebase offers scalable backend services, Firestore provides advanced NoSQL querying, and React excels in creating dynamic user interfaces. Together, they enable efficient development of high-performance and real-time applications such as a college portal.

Installation

Begin by initialising a React app using pnpm create vite.

Follow the provided video guide to set up your Firebase console.

Once you've set up your Firebase project, add a new application to Firebase, selecting a web app. Within your project's src folder, create a firebase.js file and an .env file to store your Firebase configuration.

firebase project setup steps

In the firebase.js file, import the necessary Firebase SDK functions, and configure Firebase using the environment variables specified in your .env file.

touch .env // fancy way just to create the file
VITE_APIKEY= YOUR_KEY_VALUE
VITE_AUTHDOMAIN= YOUR_KEY_VALUE
VITE_PROJECTID= YOUR_KEY_VALUE
VITE_STORAGEBUCKET= YOUR_KEY_VALUE
VITE_MESSAGESENDERID= YOUR_KEY_VALUE
VITE_APPID= YOUR_KEY_VALUE
VITE_MEASUREMENTID= YOUR_KEY_VALUE
touch Firebase.js  // fancy way just to create the file
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth } from "firebase/auth";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
// im using an env file here
const firebaseConfig = {
apiKey: import.meta.env.VITE_APIKEY,
authDomain: import.meta.env.VITE_AUTHDOMAIN,
projectId: import.meta.env.VITE_PROJECTID,
storageBucket: import.meta.env.VITE_STORAGEBUCKET,
messagingSenderId: import.meta.env.VITE_MESSAGESENDERID,
appId: import.meta.env.VITE_APPID,
measurementId: import.meta.env.VITE_MEASUREMENTID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
const analytics = getAnalytics(app);

export default app;
npm install firebase // installing dependencies for firebase

In your Firebase project, navigate to the Firestore Database section. Initialise the database, then proceed to the rules section.Replace the existing rules with the provided code snippet.

// The provided code snippet sets your Firestore rules to allow unrestricted read and write access

rules_version = '2';

service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}

Congratulations! You’ve successfully set up Firebase Authentication and Cloud Firestore for your React application. Now, you’re ready to start writing code to leverage these services in your project.

Authentication & Database

Before authentication we need to store the user data in a context file you can use redux or any other service for this Im Using createContext& useContext Hook by React

1.AuthContextProvider:

  • Sets up a React context to manage user authentication state in the application.
  • Provides functions like createUser, signIn, and logout to interact with Firebase Authentication services.
  • Utilises Firebase’s onAuthStateChanged method to keep track of the current user and update the context accordingly.
  • Exposes the user object and authentication functions via the context provider for use throughout the application.
// Auth Context File 
import { createContext, useContext, useEffect, useState } from "react";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
} from "firebase/auth";
import { auth } from "../firebase";

const UserContext = createContext();

export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});

const createUser = (email, password) => {
return createUserWithEmailAndPassword(auth, email, password);
};

const signIn = (email, password) => {
return signInWithEmailAndPassword(auth, email, password);
};

const logout = () => {
return signOut(auth);
};

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
return () => {
unsubscribe();
};
}, []);

return (
<UserContext.Provider value={{ createUser, user, logout, signIn }}>
{children}
</UserContext.Provider>
);
};

export const UserAuth = () => {
return useContext(UserContext);
};

Lets a more detailed understanding of CRUD level Operations using Signup And signIN

handleSubmit Function:

  • Triggers upon form submission, preventing default behavior.
  • Attempts to create a new user account using Firebase’s createUser function.
  • If successful, links user UID to Firestore data, navigates to account page, and displays success notification.

linkUidToFirestore Function:

  • The linkUidToFirestore function serves the purpose of associating the user's unique identifier (UID) with Firestore documents containing additional user information.
  • It ensures that user data is properly structured and stored in Firestore for further use and retrieval within the application.
  • It accesses the Firestore database instance (db) previously initialized within the component.
  • Documents are organized within collections, with each document containing specific user-related data.
  • Upon invocation, the function creates or updates a document within the “Users” collection in Firestore.
  • This document typically contains essential user information such as UID, email, user type, and course.
  • Additionally, the function establishes subcollections within the user document for specific purposes, such as attendance, assignments, and ID card details.
  • Within these subcollections, default or empty documents are created to accommodate data related to attendance records, assignments, and ID card information.

we use aysnc and await in these functions for better handling of promises

// SignUp Logic 
const { theme } = useTheme();
const { createUser } = UserAuth();
const [placeholder, setPlaceholder] = useState({
email: "",
password: "",
});
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [course, setCourse] = useState("");
const navigate = useNavigate();
const db = getFirestore();

const courses = ["BTECH", "MBATECH", "BTI", "MCA"];

const linkUidToFirestore = async (uid, email, course) => {
const val = doc(db, "Users", uid);
await setDoc(
val,
{ uid, email, type: "student", course: course },
{ merge: true }
);
const attendanceCollection = collection(val, "attendance");
await setDoc(doc(attendanceCollection, "day1"), {
present: true,
});
const assignmentCollection = collection(val, "assignment");
await setDoc(doc(assignmentCollection, "assign1"), {
present: true,
});
const idCollection = collection(val ,"ID_card");
await setDoc(doc(idCollection,"Card") , {
Name : "",
DOB : "",
BloodGroup : "",
Department : "",
Course : "",
Email : "",
SAP_ID : "",
Year : "",
Address :"",
ClgEmail : "",
});
};

const handleSubmit = async (e) => {
e.preventDefault();
setError("");
try {
const response = await createUser(email, password);
if (response.user) {
await linkUidToFirestore(response.user.uid, email, course);
navigate("/account");
}
toast.success("Account Created Successfully");
} catch (error) {
setError(error.message);
toast.error(error.message);
}
};

useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "";
};
}, []);

If your code is functioning properly, your database should exhibit the following structure.

Protective Routing

To safeguard against potential security breaches from curious engineers within our college, we’ll implement protective routes to prevent backtracking through different links. This measure aims to ensure our system’s security and protect sensitive information from unauthorized access.

// protectedRoutesByType.js
import React, { useEffect } from "react";
import { Navigate } from "react-router-dom";
import { getDoc, doc, getFirestore } from "firebase/firestore";
import { UserAuth } from "../Context/AuthContext";

const ProtectedRoutesByType = ({ children }) => {
const { user, loading } = UserAuth();
const db = getFirestore();

useEffect(() => {
const fetchUserType = async () => {
if (user) {
try {
const userDocRef = doc(db, 'Users', user.uid);
const userDocSnapshot = await getDoc(userDocRef);
if (userDocSnapshot.exists()) {
const userData = userDocSnapshot.data();
const userType = userData.type;
if (userType !== "XYZ") {
window.location.href = "/account";
}
}
} catch (error) {
console.error("Error fetching user type:", error);
}
}
};

fetchUserType();
}, [user, db]);

if (loading) {
return <div>Loading...</div>;
}

if (!user) {
return <Navigate to="/" />;
}

return children;
};

export default ProtectedRoutesByType;

Authentication and User Type Retrieval: The ProtectedRoutesByType component first checks the user’s authentication status and loading state using the UserAuth hook in the React application.

User Type Validation: Upon successful authentication, the component retrieves the user’s document from Firebase Firestore, extracting their user type. It then compares this user type with an expected value (e.g., “XYZ”) to determine access to protected routes.

Routing and Redirection: If the user is authenticated and their type matches the expected value, the protected routes are rendered seamlessly using React Router’s Navigate component. However, if there’s a mismatch in user type, the user is redirected to the account page using window.location.href. In the case of authentication failure, users are redirected to the home page.

To use the above protected Routes :-

<Route path="/fac/home" element={<ProtectedRoutesByType><Home /></ProtectedRoutesByType>} />

Conclusion

This guide offers a thorough walkthrough of integrating React with Firebase to build a robust college management system and a scalable database using Firestore Cloud.

Thank you for following along, and feel free to refer to the provided resources or reach out for further clarification or assistance.

references:-

--

--

Prabir Kalwani

Curious scholar pursuing Tech and MBA. Committed to innovation, sustainability, adept in advanced web tech like ReactJS, MERN, MEAN.