A production-ready, secure backend template for MERN stack applications with complete authentication flow and enterprise-level security protocols.
Features β’ Quick Start β’ Next.js Integration β’ API Reference β’ Security β’ Deployment
- β¨ Features
- π Quick Start
- π Next.js Integration
- π‘ API Reference
- π‘οΈ Security Features
- π§ Customization
- π Deployment
- π Support
|
|
|
|
π¦ backend_template/
βββ π config/
β βββ ποΈ database.js # MongoDB connection
β βββ β
envValidation.js # Environment validation
βββ π controllers/
β βββ π authController.js # Authentication logic
β βββ π€ userController.js # User management
βββ π middleware/
β βββ π auth.js # JWT verification
β βββ π¦ rateLimiter.js # Rate limiting configs
β βββ β
validation.js # Input validation
βββ π models/
β βββ π€ User.js # User schema & methods
βββ π routes/
β βββ π authRoutes.js # Authentication endpoints
β βββ π€ userRoutes.js # User endpoints
βββ π utils/
β βββ π§ email.js # Email utilities
β βββ π« jwt.js # JWT utilities
βββ π app.js # Express app setup
βββ π¦ package.json # Dependencies
βββ π§ .env.example # Environment template
# π₯ Clone or download this template
git clone <your-repo-url>
cd backend_template
# π¦ Install dependencies
npm install# π Copy environment template
cp .env.example .env
# βοΈ Edit .env with your configurationsπ Required Environment Variables
# π₯οΈ Server Configuration
NODE_ENV=development
PORT=5000
# ποΈ Database
MONGODB_URI=mongodb://localhost:27017/your_database_name
# π« JWT Configuration (Generate secure 32+ character secret)
JWT_SECRET=your_super_secure_jwt_secret_key_here_minimum_32_chars
JWT_EXPIRES_IN=7d
COOKIE_EXPIRES_IN=7
# π Password Reset
RESET_TOKEN_EXPIRE=10
# π·οΈ App Configuration
APP_NAME=Your App Name
# π§ Email Configuration
EMAIL_FROM=noreply@yourdomain.com
EMAIL_USER=your_email@gmail.com
EMAIL_PASSWORD=your_app_password_here
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
# π Frontend URL
FRONTEND_URL=http://localhost:3000# ποΈ Local MongoDB
mongod
# βοΈ Or use MongoDB Atlas (cloud)
# Update MONGODB_URI in .env with your Atlas connection string# π₯ Start with nodemon (auto-restart)
npm run dev
# π Or start normally
node app.jsπ Server will run on http://localhost:5000
1οΈβ£ Install Required Packages
# In your Next.js project
npm install axios zustandπ Note: This backend uses httpOnly cookies for security, so no manual token storage needed!
2οΈβ£ Create useApi Hook (Copy & Paste Ready)
Create hooks/useApi.js in your Next.js project:
// hooks/useApi.js
import { useState, useCallback } from 'react';
import axios from 'axios';
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api',
withCredentials: true, // πͺ Sends httpOnly cookies automatically
headers: {
'Content-Type': 'application/json',
},
});
// Global error handling
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
window.location.href = '/login'; // Redirect to login on unauthorized
}
return Promise.reject(error);
}
);
// π Main API Hook
export const useApi = (route, body = {}, onSuccess = () => {}, method = 'POST') => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const execute = useCallback(async (customBody = body) => {
try {
setLoading(true);
setError(null);
let response;
switch (method.toUpperCase()) {
case 'GET':
response = await api.get(route);
break;
case 'POST':
response = await api.post(route, customBody);
break;
case 'PUT':
response = await api.put(route, customBody);
break;
case 'PATCH':
response = await api.patch(route, customBody);
break;
case 'DELETE':
response = await api.delete(route);
break;
default:
throw new Error(`Unsupported method: ${method}`);
}
setData(response.data);
onSuccess(response.data);
return { success: true, data: response.data };
} catch (err) {
const errorMessage = err.response?.data?.message || 'Something went wrong';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setLoading(false);
}
}, [route, body, onSuccess, method]);
return {
execute,
loading,
error,
data,
post: useCallback((postBody = body) => execute(postBody), [execute, body]),
get: useCallback(() => execute(), [execute]),
put: useCallback((putBody = body) => execute(putBody), [execute, body]),
patch: useCallback((patchBody = body) => execute(patchBody), [execute, body]),
};
};
export default api;3οΈβ£ Create State Store (Copy & Paste Ready)
Create store/authStore.js in your Next.js project:
// store/authStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import api from '../hooks/useApi';
export const useAuthStore = create(
persist(
(set) => ({
// State
user: null,
isAuthenticated: false,
loading: false,
error: null,
// Actions
setUser: (user) => set({ user, isAuthenticated: !!user, error: null }),
setLoading: (loading) => set({ loading }),
setError: (error) => set({ error }),
clearError: () => set({ error: null }),
// Check if user is logged in
checkAuth: async () => {
set({ loading: true });
try {
const response = await api.get('/user/profile');
const { user } = response.data;
set({ user, isAuthenticated: true, loading: false });
return true;
} catch (error) {
set({ user: null, isAuthenticated: false, loading: false });
return false;
}
},
// Clear all data
clear: () => set({
user: null,
isAuthenticated: false,
loading: false,
error: null
}),
}),
{
name: 'auth-storage',
partialize: (state) => ({
user: state.user,
isAuthenticated: state.isAuthenticated,
}),
}
)
);4οΈβ£ Setup App with Auth Check (Copy & Paste Ready)
Update your pages/_app.js:
// pages/_app.js
import { useEffect } from 'react';
import { useAuthStore } from '../store/authStore';
function AuthProvider({ children }) {
const checkAuth = useAuthStore((state) => state.checkAuth);
useEffect(() => {
// Check if user is logged in when app starts
checkAuth();
}, [checkAuth]);
return children;
}
export default function App({ Component, pageProps }) {
return (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
);
}Create your .env.local file:
# .env.local
NEXT_PUBLIC_API_URL=http://localhost:5000/apiComplete Login Implementation
1. Create Login Page (pages/login.js):
// pages/login.js
import { useState } from 'react';
import { useRouter } from 'next/router';
import { useApi } from '../hooks/useApi';
import { useAuthStore } from '../store/authStore';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const setUser = useAuthStore((state) => state.setUser);
// π Login API call with success callback
const { post: login, loading, error } = useApi('/auth/login', {}, (data) => {
setUser(data.user); // Save user to store
router.push('/dashboard'); // Redirect to dashboard
});
const handleSubmit = async (e) => {
e.preventDefault();
await login({ email, password });
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="text-center text-3xl font-extrabold text-gray-900">
Sign in to your account
</h2>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="sr-only">Email address</label>
<input
id="email"
name="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Email address"
/>
</div>
<div>
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
name="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Password"
/>
</div>
{error && (
<div className="bg-red-50 border border-red-300 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
{loading ? 'Signing in...' : 'Sign in'}
</button>
</div>
</form>
</div>
</div>
);
}That's it! Your login is working. Test it by:
- Going to
/login - Entering valid credentials
- Getting redirected to
/dashboard - User data saved in Zustand store
Complete Signup Implementation
1. Create Signup Page (pages/signup.js):
// pages/signup.js
import { useState } from 'react';
import { useRouter } from 'next/router';
import { useApi } from '../hooks/useApi';
import { useAuthStore } from '../store/authStore';
export default function SignupPage() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const setUser = useAuthStore((state) => state.setUser);
// π Signup API call with success callback
const { post: signup, loading, error } = useApi('/auth/signup', {}, (data) => {
setUser(data.user); // Save user to store
router.push('/dashboard'); // Redirect to dashboard
});
const handleSubmit = async (e) => {
e.preventDefault();
await signup({ name, email, password });
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="text-center text-3xl font-extrabold text-gray-900">
Create your account
</h2>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="sr-only">Full name</label>
<input
id="name"
name="name"
type="text"
required
value={name}
onChange={(e) => setName(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Full name"
/>
</div>
<div>
<label htmlFor="email" className="sr-only">Email address</label>
<input
id="email"
name="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Email address"
/>
</div>
<div>
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
name="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Password (min 6 chars, uppercase, lowercase, number)"
/>
</div>
{error && (
<div className="bg-red-50 border border-red-300 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
{loading ? 'Creating account...' : 'Sign up'}
</button>
</div>
</form>
</div>
</div>
);
}Test signup by:
- Going to
/signup - Entering name, email, password (must have uppercase, lowercase, number)
- Getting automatically logged in and redirected
Complete Logout Implementation
1. Create Logout Component (components/LogoutButton.js):
// components/LogoutButton.js
import { useRouter } from 'next/router';
import { useApi } from '../hooks/useApi';
import { useAuthStore } from '../store/authStore';
export default function LogoutButton({ className = "" }) {
const router = useRouter();
const { clear } = useAuthStore();
// π Logout API call with success callback
const { post: logout, loading } = useApi('/auth/logout', {}, () => {
clear(); // Clear user from store
router.push('/login'); // Redirect to login
});
const handleLogout = async () => {
await logout(); // No body needed for logout
};
return (
<button
onClick={handleLogout}
disabled={loading}
className={`bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded disabled:opacity-50 ${className}`}
>
{loading ? 'Logging out...' : 'Logout'}
</button>
);
}2. Use in any component:
// pages/dashboard.js
import LogoutButton from '../components/LogoutButton';
import { useAuthStore } from '../store/authStore';
export default function Dashboard() {
const user = useAuthStore((state) => state.user);
return (
<div className="min-h-screen bg-gray-100">
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-4">
<h1 className="text-xl font-semibold">Dashboard</h1>
<LogoutButton />
</div>
</div>
</nav>
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div className="px-4 py-6 sm:px-0">
<h2 className="text-2xl font-bold text-gray-900">
Welcome, {user?.name}!
</h2>
<p className="mt-1 text-sm text-gray-600">
Email: {user?.email}
</p>
</div>
</main>
</div>
);
}Complete Forgot Password Implementation
1. Create Forgot Password Page (pages/forgot-password.js):
// pages/forgot-password.js
import { useState } from 'react';
import Link from 'next/link';
import { useApi } from '../hooks/useApi';
export default function ForgotPasswordPage() {
const [email, setEmail] = useState('');
const [success, setSuccess] = useState(false);
// π Forgot password API call with success callback
const { post: forgotPassword, loading, error } = useApi('/auth/forgot-password', {}, () => {
setSuccess(true);
});
const handleSubmit = async (e) => {
e.preventDefault();
await forgotPassword({ email });
};
if (success) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div className="text-center">
<h2 className="text-3xl font-extrabold text-gray-900">Check your email</h2>
<p className="mt-2 text-sm text-gray-600">
We've sent a password reset link to your email address.
</p>
<div className="mt-4">
<Link href="/login" className="text-indigo-600 hover:text-indigo-500">
Back to login
</Link>
</div>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="text-center text-3xl font-extrabold text-gray-900">
Forgot your password?
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Enter your email address and we'll send you a link to reset your password.
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="sr-only">Email address</label>
<input
id="email"
name="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Email address"
/>
</div>
{error && (
<div className="bg-red-50 border border-red-300 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
{loading ? 'Sending...' : 'Send reset link'}
</button>
</div>
<div className="text-center">
<Link href="/login" className="text-indigo-600 hover:text-indigo-500">
Back to login
</Link>
</div>
</form>
</div>
</div>
);
}2. Create Reset Password Page (pages/reset-password.js):
// pages/reset-password.js
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useApi } from '../hooks/useApi';
import { useAuthStore } from '../store/authStore';
export default function ResetPasswordPage() {
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [localError, setLocalError] = useState('');
const router = useRouter();
const { token } = router.query;
const setUser = useAuthStore((state) => state.setUser);
// π Reset password API call with success callback
const { patch: resetPassword, loading, error } = useApi(
`/auth/reset-password/${token}`,
{},
(data) => {
setUser(data.user); // Log user in automatically
router.push('/dashboard');
},
'PATCH'
);
const handleSubmit = async (e) => {
e.preventDefault();
setLocalError('');
if (password !== confirmPassword) {
setLocalError('Passwords do not match');
return;
}
if (password.length < 6) {
setLocalError('Password must be at least 6 characters');
return;
}
await resetPassword({ password });
};
if (!token) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<h2 className="text-2xl font-bold text-gray-900">Invalid reset link</h2>
<p className="mt-2 text-gray-600">This password reset link is invalid or expired.</p>
</div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="text-center text-3xl font-extrabold text-gray-900">
Reset your password
</h2>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="password" className="sr-only">New Password</label>
<input
id="password"
name="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="New password"
/>
</div>
<div>
<label htmlFor="confirmPassword" className="sr-only">Confirm Password</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Confirm new password"
/>
</div>
{(error || localError) && (
<div className="bg-red-50 border border-red-300 text-red-700 px-4 py-3 rounded">
{error || localError}
</div>
)}
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
{loading ? 'Resetting...' : 'Reset password'}
</button>
</div>
</form>
</div>
</div>
);
}Using Zustand Store in Components
1. Access user data anywhere:
// Any component
import { useAuthStore } from '../store/authStore';
function MyComponent() {
const user = useAuthStore((state) => state.user);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const loading = useAuthStore((state) => state.loading);
if (loading) return <div>Loading...</div>;
if (!isAuthenticated) return <div>Please log in</div>;
return (
<div>
<h1>Welcome {user.name}!</h1>
<p>Email: {user.email}</p>
</div>
);
}2. Update user data:
// Update profile component
import { useAuthStore } from '../store/authStore';
import { useApi } from '../hooks/useApi';
function UpdateProfile() {
const user = useAuthStore((state) => state.user);
const setUser = useAuthStore((state) => state.setUser);
const { put: updateProfile, loading } = useApi('/user/profile', {}, (data) => {
setUser(data.user); // Updates store automatically
}, 'PUT');
const handleUpdate = async (newData) => {
await updateProfile(newData);
};
// Component JSX here...
}Complete Protected Routes Implementation
1. Create Protected Route Component (components/ProtectedRoute.js):
// components/ProtectedRoute.js
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { useAuthStore } from '../store/authStore';
export default function ProtectedRoute({ children }) {
const { user, isAuthenticated, loading, checkAuth } = useAuthStore();
const router = useRouter();
useEffect(() => {
if (!loading && !isAuthenticated) {
checkAuth().then((isAuth) => {
if (!isAuth) {
router.push('/login');
}
});
}
}, [isAuthenticated, loading, checkAuth, router]);
// Show loading while checking authentication
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
);
}
// Don't render anything if not authenticated
if (!isAuthenticated) {
return null;
}
return children;
}2. Protect any page:
// pages/dashboard.js
import ProtectedRoute from '../components/ProtectedRoute';
import LogoutButton from '../components/LogoutButton';
import { useAuthStore } from '../store/authStore';
export default function Dashboard() {
const user = useAuthStore((state) => state.user);
return (
<ProtectedRoute>
<div className="min-h-screen bg-gray-100">
<nav className="bg-white shadow">
<div className="max-w-7xl mx-auto px-4">
<div className="flex justify-between items-center py-4">
<h1 className="text-xl font-semibold">Dashboard</h1>
<LogoutButton />
</div>
</div>
</nav>
<main className="max-w-7xl mx-auto py-6 px-4">
<h2 className="text-2xl font-bold text-gray-900">
Welcome back, {user?.name}!
</h2>
<p className="mt-2 text-gray-600">{user?.email}</p>
{/* Your dashboard content here */}
</main>
</div>
</ProtectedRoute>
);
}3. Or use HOC pattern:
// components/withAuth.js
import ProtectedRoute from './ProtectedRoute';
export function withAuth(Component) {
return function AuthenticatedComponent(props) {
return (
<ProtectedRoute>
<Component {...props} />
</ProtectedRoute>
);
};
}
// Usage:
// export default withAuth(Dashboard);| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/signup |
Register a new user |
POST |
/api/auth/login |
Login user |
POST |
/api/auth/logout |
Logout user (clears cookies) |
POST |
/api/auth/forgot-password |
Send password reset email |
PATCH |
/api/auth/reset-password/:token |
Reset password with token |
π Request/Response Examples
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass123"
}{
"email": "john@example.com",
"password": "SecurePass123"
}{
"email": "john@example.com"
}{
"password": "NewSecurePass123"
}| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
GET |
/api/user/profile |
Get current user profile | β |
PUT |
/api/user/profile |
Update user profile | β |
| π Authentication & Authorization | π Injection Protection |
|---|---|
|
|
| π Network Security | π§ Email Security |
|---|---|
|
|
Step-by-step guide
1. Create Controller:
// controllers/newController.js
const newFeature = async (req, res) => {
try {
// Your logic here
res.status(200).json({
success: true,
data: result
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message
});
}
};
module.exports = { newFeature };2. Create Route:
// routes/newRoutes.js
const express = require('express');
const { authenticate } = require('../middleware/auth');
const { newFeature } = require('../controllers/newController');
const router = express.Router();
router.get('/feature', authenticate, newFeature);
module.exports = router;3. Add to App:
// app.js
const newRoutes = require('./routes/newRoutes');
app.use('/api/new', newRoutes);Gmail Setup
- β Enable 2-Factor Authentication
- π Generate App Password
- π§ Use App Password in
EMAIL_PASSWORD
Other Providers
- π§ Update
EMAIL_HOSTandEMAIL_PORT - π Check provider-specific settings
- π Configure authentication method
NODE_ENV=production
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/database
JWT_SECRET=your_super_secure_production_jwt_secret_minimum_32_characters
FRONTEND_URL=https://yourdomain.com
APP_NAME=Your Production App
|
|
Contributions are welcome!
- π΄ Fork the repository
- πΏ Create your feature branch
- πΎ Commit your changes
- π€ Push to the branch
- π Create a Pull Request
Need help?
If you have questions or issues:
- π Check existing issues
- π Create a new issue with details
- π Include error logs and environment
β If this template helped you, please star the repository! β
Happy Coding! π