• About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us
AimactGrow
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
AimactGrow
No Result
View All Result

Constructing a Multi-Tenant SaaS Software with Subsequent.js (Backend Integration) — SitePoint

Admin by Admin
April 12, 2025
Home Coding
Share on FacebookShare on Twitter


I constructed a practical multi-tenant SaaS utility (an EdTech app) together with your on a regular basis tech software and you are able to do the identical.

First, what’s a multi-tenant SaaS utility?

Multi-tenant SaaS purposes allow you to serve a number of prospects from a single codebase. However to do that, you’ll must handle safe and tenant-specific entry, and this may be difficult when achieved manually. That’s why I made a decision to make use of Allow, a contemporary authorization software that simplifies this course of.

On this article, I’ll present you the best way to simplify authorization on your SaaS purposes utilizing Allow, with a step-by-step instance of constructing a demo app that includes tenant isolation and role-based entry management (RBAC) with Subsequent.js and Appwrite.

What are Subsequent.js and Appwrite, and why do we want them?

Subsequent.js

Subsequent.js is a React-based framework that gives server-side rendering (SSR), static website technology (SSG), API routes, and efficiency optimizations out of the field.

For this challenge, I used Subsequent.js as a result of:

  • It permits pre-rendering of pages, which improves efficiency and web optimization.
  • Its built-in routing makes it simple to handle web page transitions and dynamic content material.
  • It integrates simply with backend providers like Appwrite and Allow.io for authentication and authorization.

Appwrite

Appwrite is a backend-as-a-service (BaaS) platform that gives person authentication, databases, storage, and serverless features. Utilizing a service like Appwrite eliminates the necessity to construct a backend from scratch, so you’ll be able to concentrate on frontend improvement whereas accessing backend capabilities.

For this challenge, I used Appwrite:

  • To deal with person registration, login, and session administration.
  • To offer a structured NoSQL database to retailer tenant-specific information.

Utilizing Subsequent.js and Appwrite collectively allowed me to create a scalable, high-performance multi-tenant SaaS app whereas retaining the event course of environment friendly.

Introduction to Multi-Tenant SaaS Authorization

A multi-tenant SaaS app is software program that serves a number of customers or teams of customers, known as tenants, utilizing a single software program occasion of the applying.

What it means is that in a multi-tenant SaaS structure, a number of prospects (tenants) share the identical utility infrastructure or use the identical utility however preserve information isolation.

A sensible instance of it is a challenge administration software like Trello.

  • It’s a single infrastructure that runs on shared servers and has the identical codebase for all its customers.
  • Every firm utilizing Trello (e.g., Firm A and Firm B) is a tenant.
  • It isolates information:
    • Staff of Firm A can solely see their initiatives, duties, and boards.
    • Staff of Firm B can’t entry or view Firm A’s information, and vice versa.

This ensures that whereas assets are shared, every tenant’s information and actions are non-public and safe.

In a multi-tenant utility, even inside a tenant, some customers may have increased entry to some data, whereas some members can be restricted to sure assets.

Authorization in such purposes should:

  • Guarantee customers can’t entry different tenants’ or prospects’ information or assets. That is known as isolating tenants.
  • Guarantee customers inside a tenant can entry solely assets their roles allow by offering granular entry management.
  • Deal with extra customers, tenants, and roles with out slowing down or degrading efficiency.

Significance of tenant isolation and granular entry management

Tenant isolation retains information safe by making certain that every buyer’s data stays non-public. Whereas granular entry management ensures customers inside a company solely get the permissions they want.

Implementing authorization in your SaaS apps might be advanced and tough, however it doesn’t should be when you have got an authorization software like Allow.

What’s Allow, and what are its advantages?

Allow is an easy-to-use authorization software for managing entry in any utility, together with multi-tenant apps. Utilizing Allow.io in your utility lets you simply outline and assign roles with particular permissions for entry management inside your utility. Other than creating roles throughout the utility, it’s also possible to add circumstances and guidelines based mostly on person or useful resource attributes to specify what every person can and can’t do.

Now that most of what you have to find out about Allow and its advantages, let’s get into the primary deal—constructing a SaaS utility with Subsequent.js and integrating Allow for authorization.

To show the ability of Allow, we’ll be constructing a multi-tenant Edtech SaaS platform.

Constructing an EdTech SaaS platform entails a number of challenges, together with person authentication, role-based entry management (RBAC), and multi-tenancy. We’ll use Subsequent.js for the frontend, Appwrite for authentication and database administration, and Allow for fine-grained authorization.

Tech Stack Overview

Know-how Objective
Subsequent.js Frontend framework
ShadCN + Tailwindcss UI Parts and styling
Zustand State administration
Appwrite Authentication & backend
Allow.io Position-based entry management

System Structure

The appliance follows a backend-first method:

  1. Backend (Node.js + Specific)
    • Handles API requests and enterprise logic.
    • Makes use of Appwrite for authentication and database administration.
    • Implements Allow for authorization, defining roles and permissions.
    • Ensures each request is validated earlier than information entry.
  2. Frontend (Subsequent.js)
    • Connects to the backend to fetch information securely.
    • Makes use of role-based UI rendering, which means customers solely see what they’re approved to entry.
    • Restricts actions (like creating assignments) based mostly on permissions.

By imposing authorization on the API stage, we be certain that customers can’t bypass restrictions, even when they manipulate the frontend.

On the finish of this information, you’ll have a totally practical multi-tenant EdTech SaaS app, the place:

  • Admins can add and look at college students.
  • Lecturers can add and look at college students, in addition to create assignments.
  • College students can solely view their assigned coursework.

This text offers a step-by-step breakdown of how I applied Allow to deal with authorization to construct this challenge, so comply with alongside and construct yours.

Backend Implementation with Allow

To implement role-based entry management (RBAC) and tenant isolation, we have to:

  1. Arrange Allow and outline roles, tenants, and insurance policies.
  2. Combine Allow within the backend (Node.js + Specific).
  3. Shield API routes utilizing middleware that checks permissions earlier than permitting requests.

Let’s go step-by-step.

1. Organising Allow

Earlier than writing any code, you have to

Permit login screen

You can be offered with the onboarding, however when you enter your group title, you’ll be able to simply skip the setup.

  • Create a useful resource and actions

Navigate to the coverage part, the place you’ll create a useful resource and actions that you may carry out on that useful resource.

Permit authentication settings

As soon as you’re achieved creating your assets, it ought to seem like this:

Permit authentication settings

After creating the assets, navigate to the Roles web page utilizing the Roles tab. You’ll see that some roles have mechanically been assigned.

Creating roles in Permit

Delete these roles and create new roles. Every position may have particular guidelines related to it, about what a person can and can’t do. Create the Admin position first, as it can later function a constructing block for the RBAC circumstances. Click on the Add Position button on the high and create the roles.

Configuring roles in Permit

When you’re achieved creating your roles, it ought to seem like this:

Configured roles in Permit

Nice!

Now that you’ve created your assets and roles, now you can configure permissions within the coverage editor.

  • Configuring permissions within the coverage editor

Return to the Coverage Editor and that is what the roles will seem like now, with every particular person useful resource outlined and the actions that you may choose. You’re now prepared to present permissions to the roles to carry out the chosen actions on the useful resource.

Configuring permissions in the policy editor

When you’re achieved deciding on the actions for every position, click on the Save adjustments button on the backside proper of the web page.

Lastly, to make use of the cloud PDP of Allow, you’ll want the API key of your present setting. For this challenge you’ll be utilizing the event setting key. Proceed to Settings and click on API Keys, scroll right down to Atmosphere API keys, click on “Reveal Key,” then copy it.

Configuring  API keys

After organising your Allow dashboard, now you can transfer on to your backend.

2. Putting in dependencies

To get began, you’ll must have Node.js put in in your laptop. After making certain Node.js is put in in your system, comply with these steps:

  • Begin by creating a brand new challenge utilizing the next instructions:
mkdir backend
cd backendNpm init -y
  • Then, set up the next packages:
npm set up categorical dotenv permitio cors appwwrite axios jsonwebtoken
  • Configure Allow in Specific. In your .env file, retailer your API key:
PERMIT_API_KEY=your-permit-key-you-copied-earlier

3. Organising Appwrite

  • Go to Appwrite and create a brand new challenge by inputting a challenge title and deciding on a area. Observe down your Challenge ID and API Endpoint; that’s what you’ll enter because the values in your .env file. Your ENV file must be trying like this:
PERMIT_API_KEY=your-permit-key-you-copied-earlier
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=your-project-id
  • Now proceed to databases to create your database, then copy your database ID to stick it into your ENV file.
Creating database

Your ENV file ought to now be trying like this:

PERMIT_API_KEY=your-permit-key-you-copied-earlier
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=your-project-id
APPWRITE_DATABASE_ID=your-database-id

Now create the next collections within the Appwrite Database with the next attributes:

Profiles collection
Students collection
Assignments collection

What your ENV file must be trying like at this level:

PERMIT_API_KEY=your-permit-key-you-copied-earlier
PERMIT_PROJECT_ID=copy-from-dashboard
PERMIT_ENV_ID=copy-from-dashboard
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=your-project-id
APPWRITE_DATABASE_ID=your-database-id
APPWRITE_PROFILE_COLLECTION_ID=your-id
APPWRITE_ASSIGNMENTS_COLLECTION_ID=your-id
APPWRITE_STUDENTS_COLLECTION_ID=your-id
JWT_SECRET=generate-this-by-running//openssl rand -base64 16
PORT=8080

4. Create file construction and recordsdata

Now create a src folder within the root of the file. Then generate the tsconfig.json file within the root folder and paste the next code into it:

{
    "compilerOptions": {
      "goal": "ES6",
      "module": "commonjs",
      "outDir": "./dist",
      "esModuleInterop": true,
      "forceConsistentCasingInFileNames": true,
      "strict": true,
      "skipLibCheck": true,
      "resolveJsonModule": true,
      "baseUrl": "./",
      "paths": {
        "@/*": ["src/*"]
      }
    },
    "embrace": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
  }

This tsconfig.json configures the TypeScript compiler to focus on ES6, use CommonJS modules, and output recordsdata to ./dist. It enforces strict type-checking, permits JSON module decision, units up path aliases for src, and excludes node_modules and dist from compilation.

Within the src folder, create the next folders: api, config, controllers, middleware, fashions, and utils.

  • Utils folder
    • Now, create a brand new allow.ts file within the utils folder challenge to initialize Allow utilizing the next code:
import { Allow } from 'permitio';
import { PERMIT_API_KEY } from '../config/setting';


const allow = new Allow({
  
  token: PERMIT_API_KEY, 
  
  pdp: 'https://cloudpdp.api.allow.io', 
  
  log: {
    stage: "debug",
  },
  
  
  
});

export default allow;

This file initializes Allow’s SDK for Node.js, connecting it to the Allow PDP container utilizing an API key saved within the setting. It configures logging for debugging and units up the SDK to deal with errors silently except explicitly configured to throw them.

  • Subsequent, create a file known as errorHandler.ts and paste the next code:

import { Request, Response, NextFunction } from 'categorical';

export const errorHandler = (err: any, req: Request, res: Response, subsequent: NextFunction) =>  err);
  res.standing(err.standing ;

This file defines an Specific error-handling middleware that logs errors and sends a JSON response with the error message and standing code. It defaults to a 500 standing code if no particular standing is supplied.

  • Fashions folder
    • Create a file known as profile.ts and paste the next code:
export interface Profile  'Scholar';
    userId: string;

This file defines a TypeScript Profile interface with properties for title, e-mail, position, and userId, the place position is restricted to particular values: Admin, Instructor, or Scholar.

  • Create task.ts file and paste the next code:
import { database, ID } from '../config/appwrite';
import { DATABASE_ID, ASSIGNMENTS_COLLECTION_ID } from '../config/setting';

export interface AssignmentData {
  title: string;
  topic: string;
  className: string;
  instructor: string;
  dueDate: string;
  creatorEmail: string;
}


export async operate createAssignmentInDB(information: AssignmentData) {
    return await database.createDocument(
      DATABASE_ID,
      ASSIGNMENTS_COLLECTION_ID,
      ID.distinctive(),
      information
    );
}


export async operate fetchAssignmentsFromDB() {
  const response = await database.listDocuments(DATABASE_ID, ASSIGNMENTS_COLLECTION_ID);
  return response.paperwork;
}

This file offers features to work together with an Appwrite database for managing assignments. It defines an AssignmentData interface and contains features to create a brand new task and fetch all assignments from the database.

  • Create a scholar.ts file and paste the next code:
import { database, ID, Permission, Position, Question } from '../config/appwrite';
import { DATABASE_ID, STUDENTS_COLLECTION_ID } from '../config/setting';

export interface StudentData  'Boy' 


export async operate createStudentInDB(information: StudentData) {
    return await database.createDocument(
      DATABASE_ID,
      STUDENTS_COLLECTION_ID,
      ID.distinctive(),
      information,
      [
        Permission.read(Role.any()),  
      ]
    );
}


export async operate fetchStudentsFromDB() {
  const response = await database.listDocuments(DATABASE_ID, STUDENTS_COLLECTION_ID);
  return response.paperwork;
}

This file offers features to handle scholar information in an Appwrite database. It defines a StudentData interface and contains features to create a brand new scholar with public learn permissions and fetch all college students from the database.

  • Middleware folder
    • Create auth.ts file and paste the next code:
import { Request, Response, NextFunction } from 'categorical';
import jwt from 'jsonwebtoken';


interface AuthenticatedRequest extends Request {
  person?: {
    id: string;
    position: string;
  };
}

const authMiddleware = (req: AuthenticatedRequest, res: Response, subsequent: NextFunction): void => {
  const token = req.headers.authorization?.cut up(' ')[1];

  if (!token) {
    res.standing(401).json({ error: 'Unauthorized. No token supplied' });
    return
  }

  attempt {
    const decoded = jwt.confirm(token, course of.env.JWT_SECRET!) as { id: string; position: string };
    req.person = decoded;
    subsequent();
  } catch (error) {
    res.standing(403).json({ error: 'Invalid token' });
    return
  }
};

export default authMiddleware;

This file defines an Specific middleware for JWT-based authentication. It checks for a sound token within the request header, verifies it utilizing a secret key, and attaches the decoded person data (ID and position) to the request object. If the token is lacking or invalid, it returns an applicable error response.

  • Create allow.ts and paste the next code:
import allow from '../utils/allow';

export const checkUsertoPermitStudents = async (e-mail: string, motion: string, useful resource: string): Promise<boolean> => {
  attempt {
    const permitted = await allow.examine(e-mail, motion, useful resource);
    console.log("Permitted", permitted);
    return permitted;
  } catch (error) {
    console.error(`Error syncing person ${e-mail} to Allow.io:`, error);
    return false;
  }
};

export const checkUserToPermitAssignment = async (e-mail: string, motion: string, useful resource: string): Promise<boolean> => {
  attempt {
    const permitted = await allow.examine(e-mail, motion, useful resource);
    console.log("Permitted", permitted);
    return permitted;
  } catch (error) {
    console.error(`Error syncing person ${e-mail} to Allow.io:`, error);
    return false;
  }
};

This file defines utility features, checkUsertoPermitStudents and checkUserToPermitAssignment, to examine person permissions in Allow for particular actions and assets. Each features deal with errors gracefully, logging points and returning false if the permission examine fails. They’re used to implement authorization within the utility.

  • Controllers folder
    • Create auth.ts file and paste the next code:
import { account, ID } from '../config/appwrite';
import { Request, Response } from 'categorical';
import jwt from 'jsonwebtoken';

const JWT_SECRET = course of.env.JWT_SECRET as string; 


export const signUp = async (req: Request, res: Response) => {
  const { e-mail, password, title } = req.physique;

  if (!e-mail || !password || !title) {
    return res.standing(400).json({ error: 'Title, e-mail, and password are required.' });
  }

  attempt {
    const person = await account.create(ID.distinctive(), e-mail, password, title);
    
    const token = jwt.signal({ e-mail }, JWT_SECRET, { expiresIn: '8h' });
      res.cookie('token', token, {
        httpOnly: true,
        sameSite: 'strict',
        safe: true,
      });

    res.standing(201).json({ success: true, person, token });
  } catch (error: any) {
    console.error('Signal-up Error:', error);
    res.standing(500).json({ success: false, message: error.message });
  }
};


export const login = async (req: Request, res: Response) => {
  const { e-mail, password } = req.physique;

  if (!e-mail || !password) {
    return res.standing(400).json({ error: 'E mail and password are required.' });
  }

  attempt {
    const session = await account.createEmailPasswordSession(e-mail, password);

    
    const token = jwt.signal(
      { userId: session.userId, e-mail }, 
      JWT_SECRET,
      { expiresIn: '8h' }
    );

    res.cookie('token', token, {
      httpOnly: true,
      sameSite: 'strict',
      safe: true,
    });

    res.standing(200).json({ success: true, token, session });
  } catch (error: any) {
    console.error('Login Error:', error);
    res.standing(401).json({ success: false, message: error.message });
  }
};


export const logout = async (req: Request, res: Response) => {
  attempt {
    await account.deleteSession('Present Session ID');
    res.clearCookie('token');
    res.standing(200).json({ success: true, message: 'Logged out efficiently' });
  } catch (error: any) {
    console.error('Logout Error:', error);
    res.standing(500).json({ success: false, message: error.message });
  }
};

This file defines authentication controllers for sign-up, login, and logout, integrating with Appwrite for person administration and JWT for session dealing with. The signUp and login controllers validate enter, create person classes, and generate JWTs, whereas the logout controller clears the session and token. All controllers deal with errors and return applicable responses.

  • Create task.ts file and paste the next code:
import { Request, Response } from 'categorical';
import { createAssignmentInDB, AssignmentData, fetchAssignmentsFromDB } from '../fashions/task';
import { checkUserToPermitAssignment } from '../middleware/allow';


export async operate createAssignment(req: Request<{}, {}, AssignmentData>, res: Response): Promise<void> {
    attempt {
        const { title, topic, instructor, className, dueDate, creatorEmail }: AssignmentData = req.physique;

        const isPermitted = await checkUserToPermitAssignment(creatorEmail, "create", "assignments");
        if (!isPermitted) {
            res.standing(403).json({ error: 'Not approved' });
            return;
        }

        const newAssignment = await createAssignmentInDB({
            title,
            topic,
            instructor,
            className,
            dueDate,
            creatorEmail
        });

        console.log('New task created:', newAssignment);

        res.standing(201).json(newAssignment);
    } catch (error) {
        console.error('Error creating task:', error);
        res.standing(500).json({ error: (error as any).message });
    }  
}


export async operate fetchAssignments(req: Request, res: Response): Promise<void> {
    attempt {
        const { e-mail } = req.params;
       
        const isPermitted = await checkUserToPermitAssignment(e-mail, "learn", "assignments");
        if (!isPermitted) {
            res.standing(403).json({ message: 'Not approved' });
            return;
        }

        const assignments = await fetchAssignmentsFromDB();
        res.standing(200).json(assignments);
    } catch (error) {
        res.standing(500).json({ error: (error as any).message });
    }
}

This file defines controllers for creating and fetching assignments to combine with a database and Allow for authorization checks. The createAssignment controller validates enter, checks permissions, and creates a brand new task, whereas the fetchAssignments controller retrieves all assignments after verifying entry. Each controllers deal with errors and return applicable responses.

  • Create a scholar.ts file and paste the next code:
import {
    createStudentInDB,
    fetchStudentsFromDB,
    StudentData
} from '../fashions/scholar';
import { Request, Response } from 'categorical';
import { checkUsertoPermitStudents } from '../middleware/allow';

export async operate createStudent(req: Request, res: Response): Promise<void> {
    attempt {
        const { firstName, lastName, gender, className, age, creatorEmail }: StudentData = req.physique;

        if (!['girl', 'boy'].contains(gender)) {
            res.standing(400).json({ error: 'Invalid gender sort' });
            return;
        }

        const isPermitted = await checkUsertoPermitStudents(creatorEmail, "create", "college students");
        if (!isPermitted) {
            res.standing(403).json({ message: 'Not approved' });
            return;
        }

        const newStudent = await createStudentInDB({
            firstName,
            lastName,
            gender,
            className,
            age,
            creatorEmail
        });
        res.standing(201).json(newStudent);
    } catch (error) {
        res.standing(500).json({ error: (error as any).message });
    }  
}


export async operate fetchStudents(req: Request, res: Response): Promise<void> {
    attempt {
        const { e-mail } = req.params;

        const isPermitted = await checkUsertoPermitStudents(e-mail, "learn", "college students");
        if (!isPermitted) {
            res.standing(403).json({ message: 'Not approved' });
            return;
        }

        const college students = await fetchStudentsFromDB();
        res.standing(200).json(college students);
    } catch (error) {
        res.standing(500).json({ error: (error as any).message });
    }
}

This file defines controllers for creating and fetching college students, integrating with a database and Allow for authorization checks. The createStudent controller validates enter, checks permissions, and creates a brand new scholar, whereas the fetchStudents controller retrieves all college students after verifying entry. Each controllers deal with errors and return applicable responses.

  • Create a profile.ts file and paste the next code:
import { Profile } from '@/fashions/profile';
import axios from 'axios';
import { database, ID, Question } from '../config/appwrite';
import { Request, Response, NextFunction, RequestHandler } from 'categorical';
import { PERMIT_API_KEY } from '../config/setting';

const profileId = course of.env.APPWRITE_PROFILE_COLLECTION_ID as string; 
const databaseId = course of.env.APPWRITE_DATABASE_ID as string; 
const projectId = course of.env.PERMIT_PROJECT_ID as string
const environmentId = course of.env.PERMIT_ENV_ID as string

const PERMIT_API_URL = `https://api.allow.io/v2/information/${projectId}/${environmentId}/customers`;
const PERMIT_AUTH_HEADER = {
  Authorization: `Bearer ${PERMIT_API_KEY}`,
  "Content material-Kind": "utility/json",
};


export const createProfile: RequestHandler = async (req: Request, res: Response, subsequent: NextFunction): Promise<void> => {
  const { firstName, lastName, e-mail, position, userId } = req.physique;
  console.log(req.physique);

  if (!e-mail || !position || !userId) {
    res.standing(400).json({ error: 'FirstName, lastName, e-mail, position, and userId are required.' });
    return;
  }

  
  const allowedRoles: Profile['role'][] = ['Admin', 'Teacher', 'Student'];
  if (!allowedRoles.contains(position)) {
    res.standing(400).json({ error: 'Invalid position. Allowed roles: admin, instructor, scholar' });
    return;
  }

  attempt {
    const newUser = await database.createDocument(
      databaseId,
      profileId,
      ID.distinctive(),
      { firstName, lastName, e-mail, position, userId }
    );
    
    const permitPayload = {
      key: e-mail,
      e-mail,
      first_name: firstName,
      last_name: lastName,
      role_assignments: [{ role, tenant: "default" }],
    };

    let permitResponse;
    attempt {
      const response = await axios.put up(PERMIT_API_URL, permitPayload, { headers: PERMIT_AUTH_HEADER });
      permitResponse = response.information;
      console.log("Person synced to Allow.io:", permitResponse);
    } catch (permitError) {
      if (axios.isAxiosError(permitError))  else {
        console.error("Didn't sync person to Allow.io:", permitError);
      }
      permitResponse = { error: "Didn't sync with Allow.io" };
    }

    
    res.standing(201).json({
      message: "Person profile created efficiently",
      person: newUser,
      allow: permitResponse,
    });
    return;
  } catch (error: any) {
    res.standing(500).json({ success: false, message: error.message });
    return;
  }
};


export const getProfileByEmail = async (req: Request, res: Response, subsequent: NextFunction): Promise<void> => {
  const { e-mail } = req.params;
   
  if (!e-mail) {
    res.standing(400).json({ error: 'E mail is required.' });
    return;
  }

  attempt {
    const profile = await database.listDocuments(
      databaseId,
      profileId,
      [Query.equal("email", email)]
    );

    if (profile.paperwork.size === 0) {
      res.standing(404).json({ error: 'Profile not discovered' });
      return;
    }

    res.standing(200).json({ success: true, profile: profile.paperwork[0] });
  } catch (error: any) {
    console.error('Error fetching profile:', error);
    res.standing(500).json({ success: false, message: error.message });
  }
};

This file defines controllers for creating and fetching person profiles, integrating with Appwrite for database operations and Allow for position synchronization. The createProfile controller validates enter, creates a profile, and syncs the person to Allow, whereas the getProfileByEmail controller retrieves a profile by e-mail. Each controllers deal with errors and return applicable responses.

  • Config Folder
    • Create appwrite.ts file and paste the next code:
import { Shopper, Account, Databases, Storage, ID, Permission, Position, Question } from 'appwrite';
import { APPWRITE_ENDPOINT, APPWRITE_PROJECT_ID, APPWRITE_API_KEY } from './setting';


const consumer = new Shopper()
  .setEndpoint(APPWRITE_ENDPOINT) 
  .setProject(APPWRITE_PROJECT_ID); 


if (APPWRITE_API_KEY) {
  (consumer as any).config.key = APPWRITE_API_KEY;  
}


const account = new Account(consumer);
const database = new Databases(consumer);
const storage = new Storage(consumer);


export { consumer, account, database, storage, ID, Permission, Position, Question };

This file initializes and configures the Appwrite consumer with the challenge endpoint, ID, and elective API key. It additionally units up and exports Appwrite providers like Account, Databases, and Storage, together with utility constants like ID, Permission, Position, and Question.

  • Create setting.ts file and paste the next code:
import dotenv from 'dotenv';
dotenv.config();  

export const APPWRITE_ENDPOINT = course of.env.APPWRITE_ENDPOINT || '';
export const PERMIT_API_KEY = course of.env.PERMIT_API_KEY || '';
export const PERMIT_PROJECT_ID = course of.env.PERMIT_PROJECT_ID || '';
export const PERMIT_ENV_ID = course of.env.PERMIT_ENV_ID || '';
export const APPWRITE_PROJECT_ID = course of.env.APPWRITE_PROJECT_ID || '';
export const DATABASE_ID = course of.env.APPWRITE_DATABASE_ID || '';
export const STUDENTS_COLLECTION_ID = course of.env.APPWRITE_STUDENTS_COLLECTION_ID || '';
export const ASSIGNMENTS_COLLECTION_ID = course of.env.APPWRITE_ASSIGNMENTS_COLLECTION_ID || '';

export const PROFILE_COLLECTION_ID = course of.env.APPWRITE_PROFILE_COLLECTION_ID || '';

This file hundreds setting variables from a .env file and exports them as constants to be used within the utility, resembling Appwrite and Allow configurations, database IDs, and assortment IDs. Default values are supplied as fallbacks if the setting variables will not be set.

  • API folder
    • Create scholar.ts and paste the next code:
import categorical from 'categorical';
import { createStudent, fetchStudents } from '../controllers/scholar';
import authMiddleware from '../middleware/auth';

const router = categorical.Router();


router.put up('/college students', authMiddleware, createStudent); 
router.get('/college students/:e-mail', authMiddleware, fetchStudents); 
export default router; 

This file units up an Specific router with endpoints for managing scholar information. It contains routes for creating a brand new scholar and fetching college students, each protected by an authentication middleware (authMiddleware). The router is then exported to be used within the utility.

  • Create auth.ts file and paste the next code:

import categorical from 'categorical';
import { signUp, login, logout } from '../controllers/auth';

const router = categorical.Router();


router.put up('/signup', (req, res, subsequent) => { 
    signUp(req, res).then(() => {
      subsequent();
    }).catch((err) => {
      subsequent(err);
    });
});
router.put up('/login', (req, res, subsequent) => { 
    login(req, res).then(() => {
      subsequent();
    }).catch((err) => {
      subsequent(err);
    });
});
router.put up('/logout', logout); 
export default router; 

This file units up an Specific router with endpoints for authentication-related actions, together with person signup, login, and logout. The signup and login routes deal with asynchronous operations with error dealing with, whereas the logout route is easy. The router is exported to be used within the utility.

  • Create task.ts file and paste the next code:
import categorical from "categorical"
import { createAssignment, fetchAssignments } from "../controllers/task"
import authMiddleware from "../middleware/auth"

const router = categorical.Router()

router.put up("/create", authMiddleware, createAssignment)
router.get("/:e-mail", authMiddleware, fetchAssignments)
export default router

This file units up an Specific router with endpoints for managing assignments. It contains routes for creating an task and fetching assignments, each protected by an authentication middleware (authMiddleware). The router is exported to be used within the utility.

  • Create profile.ts file and paste the next code:
import categorical from 'categorical';
import { createProfile, getProfileByEmail } from '../controllers/profile';
import authMiddleware from '../middleware/auth';

const router = categorical.Router();


router.put up('/profile', authMiddleware, createProfile);


router.get('/profile/:e-mail', authMiddleware, getProfileByEmail);
export default router;

This file units up an Specific router with endpoints for managing person profiles. It contains routes for making a profile and fetching a profile by e-mail, each protected by an authentication middleware (authMiddleware). The router is exported to be used within the utility.

  • Create index.ts file and paste the next code:
import categorical, { Request, Response } from 'categorical';
import dotenv from 'dotenv';
import cors from 'cors';  
import authRoutes from './auth';  
import profileRoutes from './profile';
import studentRoutes from './scholar';
import assignmentRoutes from './task';
import { errorHandler } from '../utils/errorHandler';  

dotenv.config();  

const app = categorical();
const PORT = course of.env.PORT || 8080;


app.use(cors());  
app.use(categorical.json());  


app.use('/api/auth', authRoutes);  
app.use('/api', profileRoutes); 
app.use('/api', studentRoutes); 
app.use('/api/assignments', assignmentRoutes); 


app.use(errorHandler);  


app.get("https://www.sitepoint.com/", (req: Request, res: Response) => {
  res.ship('Appwrite Specific API');
});


app.pay attention(PORT, () => {
  console.log(`Server is operating on port ${PORT}`);
});
export default app;

This file units up an Specific server, configuring middleware like CORS and JSON parsing, and mounts routes for authentication, profiles, college students, and assignments. It features a world error handler and a default route to verify the server is operating. The server listens on a specified port, logs its standing, and exports the app occasion for additional use.

  • Lastly, to run this challenge, change part of package deal.json and set up the next packages beneath so once you run npm run dev, it really works.
npm set up concurrently ts-node nodemon --save-dev
  • By updating the scripts within the package deal.json, once you begin the server, the typescript recordsdata are compiled to JavaScript in a brand new folder that’s mechanically created known as dist
"scripts": {
    "dev": "concurrently "tsc --watch" "nodemon -q --watch src --ext ts --exec ts-node src/api/index.ts"",
    "construct": "tsc",
    "begin": "node ./dist/api/index.js"
},

Now run npm run dev to begin your server. If you see this message, it means that you’ve efficiently applied the backend.

Congratulations, your backend is prepared for requests.

Now that our backend is ready up, transfer on to frontend integration, the place you’ll:

  • Safe API requests from Subsequent.js
  • Dynamically present/cover UI components based mostly on person permissions.

Purpose for creating an intensive backend service utilizing Appwrite

Appwrite is commonly described as a backend-as-a-service (BaaS) answer, which means it offers ready-made backend performance like authentication, database administration, and storage with out requiring builders to construct a standard backend.

Nonetheless, for this challenge, I wanted extra flexibility and management over how information was processed, secured, and structured, which led me to create an intensive customized backend utilizing Node.js and Specific whereas nonetheless leveraging Appwrite’s providers.

As a substitute of relying solely on Appwrite’s built-in API calls from the frontend, I designed a Node.js backend that acted as an middleman between the frontend and Appwrite. This allowed me to:

  • Implement fine-grained entry management with Allow.io earlier than forwarding requests to Appwrite.
  • Construction API endpoints for multi-tenancy to make sure tenant-specific information isolation.
  • Create customized enterprise logic, resembling processing role-based actions earlier than committing them to the Appwrite database.
  • Preserve a centralized API layer, making it simpler to implement safety insurance policies, log actions, and scale the applying.

Appwrite supplied the core authentication and database performance of this utility, however this extra backend layer enhanced safety, flexibility, and maintainability, to make sure strict entry management earlier than any motion reached Appwrite.

Conclusion

That’s it for half one among this text collection. Partially 2, we’ll deal with the frontend integration by organising API calls with authorization, initializing and putting in essential dependencies, writing out the element file codes, and dealing with state administration & routes.

Tags: ApplicationBackendBuildingIntegrationMultiTenantNext.jsSaaSSitePoint
Admin

Admin

Next Post
Monster Hunter Wilds: Trophy & Achievement Information

Monster Hunter Wilds: Trophy & Achievement Information

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recommended.

Revisiting Picture Maps | CSS-Methods

Revisiting Picture Maps | CSS-Methods

May 4, 2025
The Evolution of AI Boyfriend Apps in NSFW Mode

The Evolution of AI Boyfriend Apps in NSFW Mode

June 4, 2025

Trending.

Industrial-strength April Patch Tuesday covers 135 CVEs – Sophos Information

Industrial-strength April Patch Tuesday covers 135 CVEs – Sophos Information

April 10, 2025
Expedition 33 Guides, Codex, and Construct Planner

Expedition 33 Guides, Codex, and Construct Planner

April 26, 2025
How you can open the Antechamber and all lever places in Blue Prince

How you can open the Antechamber and all lever places in Blue Prince

April 14, 2025
Important SAP Exploit, AI-Powered Phishing, Main Breaches, New CVEs & Extra

Important SAP Exploit, AI-Powered Phishing, Main Breaches, New CVEs & Extra

April 28, 2025
Wormable AirPlay Flaws Allow Zero-Click on RCE on Apple Units by way of Public Wi-Fi

Wormable AirPlay Flaws Allow Zero-Click on RCE on Apple Units by way of Public Wi-Fi

May 5, 2025

AimactGrow

Welcome to AimactGrow, your ultimate source for all things technology! Our mission is to provide insightful, up-to-date content on the latest advancements in technology, coding, gaming, digital marketing, SEO, cybersecurity, and artificial intelligence (AI).

Categories

  • AI
  • Coding
  • Cybersecurity
  • Digital marketing
  • Gaming
  • SEO
  • Technology

Recent News

What’s going to influencer advertising and marketing appear to be in 2025? Knowledgeable predictions + new knowledge

What’s going to influencer advertising and marketing appear to be in 2025? Knowledgeable predictions + new knowledge

June 18, 2025
Yoast AI Optimize now out there for Basic Editor • Yoast

Replace on Yoast AI Optimize for Traditional Editor  • Yoast

June 18, 2025
  • About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved

No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved