찾다
웹 프론트엔드JS 튜토리얼Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다.

먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까?

멀티 테넌트 SAAS 애플리케이션을 사용하면 단일 코드베이스에서 여러 고객에게 서비스를 제공 할 수 있습니다. 그러나이를 위해서는 안전하고 세입자 별 액세스를 관리해야하며 수동으로 수행 할 때는 어려울 수 있습니다. 그렇기 때문에이 프로세스를 단순화하는 현대적인 승인 도구 인 허가를 사용하기로 결정했습니다.

이 기사에서는 TENANT INLITATION을 사용하여 SAAS 애플리케이션에 대한 승인을 단순화하는 방법을 보여 드리겠습니다. TENANT DERINATION 및 APPWRITE를 사용하여 테넌트 격리 및 역할 기반 액세스 제어 (RBAC)를 갖춘 데모 앱을 구축하는 단계별 예를 들어 보겠습니다.

다음은 무엇입니까 JS와 AppWrite는 무엇입니까? 왜 우리는 그것들을 필요로합니까?

다음 .js

Next.js는 SSR (Server-Side Rendering), 정적 사이트 생성 (SSG), API 경로 및 성능 최적화를 제공하는 반응 기반 프레임 워크입니다.

이 프로젝트에서는 다음에 다음을 사용했습니다.

  • 페이지를 사전 렌더링하여 성능과 SEO를 향상시킵니다.
  • 내장 라우팅을 통해 페이지 전환 및 동적 컨텐츠를 쉽게 관리 할 수 ​​있습니다.
  • 인증 및 승인을 위해 AppWrite 및 Cermit.io와 같은 백엔드 서비스와 쉽게 통합됩니다.

appwrite

AppWrite는 사용자 인증, 데이터베이스, 스토리지 및 서버리스 기능을 제공하는 BAAAS (Backend-as-A-Service) 플랫폼입니다. AppWrite와 같은 서비스를 사용하면 백엔드를 처음부터 구축 할 필요가 없으므로 백엔드 기능에 액세스하면서 프론트 엔드 개발에 집중할 수 있습니다.

이 프로젝트에서는 AppWrite를 사용했습니다.

  • 사용자 등록, 로그인 및 세션 관리를 처리합니다.
  • 세입자 별 데이터를 저장하기위한 구조화 된 NOSQL 데이터베이스를 제공합니다.

Next.js와 AppWrite를 함께 사용하면 개발 프로세스를 효율적으로 유지하면서 확장 가능한 고성능 다중 테넌트 SaaS 앱을 만들 수있었습니다.

다중 테넌트 SaaS 승인 소개

멀티 테넌트 SAAS 앱은 응용 프로그램의 단일 소프트웨어 인스턴스를 사용하여 여러 사용자 또는 테넌트라고하는 사용자 그룹에 서비스를 제공하는 소프트웨어입니다.

의미는 다중 테넌트 SaaS 아키텍처에서 여러 고객 (테넌트)이 동일한 응용 프로그램 인프라를 공유하거나 동일한 응용 프로그램을 사용하지만 데이터 격리를 유지한다는 것입니다.

이것의 실질적인 예는 Trello와 같은 프로젝트 관리 도구입니다.

  • 공유 서버에서 실행되며 모든 사용자와 동일한 코드베이스를 갖는 단일 인프라입니다.
  • Trello (예 : 회사 A 및 회사 B)를 사용하는 각 회사는 임차인입니다.
  • 데이터를 분리합니다.
    • 회사 A의 직원은 프로젝트, 작업 및 이사회 만 볼 수 있습니다.
    • 회사 B의 직원은 회사 A의 데이터에 액세스하거나 볼 수 없으며 그 반대도 마찬가지입니다.

이를 통해 리소스가 공유되는 동안 각 세입자의 데이터 및 활동은 개인적이고 안전합니다.

다중 테넌트 응용 프로그램에서는 임차인 내에서도 일부 사용자는 일부 정보에 대한 액세스 권한이 높고 일부 회원은 특정 리소스로 제한됩니다.

그러한 신청서에서 승인해야합니다.

  • 사용자가 다른 임차인 또는 고객의 데이터 또는 리소스에 액세스 할 수 없도록합니다. 이것을 격리 세입자라고합니다.
  • 세입자 내의 사용자가 세분화 된 액세스 제어를 제공하여 역할 허가 자원 만 액세스 할 수 있도록하십시오.
  • 성능을 늦추거나 저하시키지 않고 더 많은 사용자, 임차인 및 역할을 처리합니다.

임차인 분리 및 세분화 액세스 제어의 중요성

테넌트 격리는 각 고객의 정보가 비공개로 유지되도록하여 데이터를 안전하게 유지합니다. 세분화 된 액세스 제어는 조직 내 사용자가 필요한 권한 만 받도록합니다.

SaaS 앱에서 권한을 구현하는 것은 복잡하고 까다로울 수 있지만 허가와 같은 인증 도구가있을 때는 필요하지 않습니다.

허가는 무엇이며 그 이점은 무엇입니까?

허가는 멀티 테넌트 앱을 포함하여 모든 응용 프로그램에서 액세스를 관리하기위한 사용하기 쉬운 권한 부여 도구입니다. 애플리케이션에서 permit.io를 사용하면 응용 프로그램 내 액세스 제어에 대한 특정 권한으로 역할을 쉽게 정의하고 할당 할 수 있습니다. 응용 프로그램 내에서 역할을 만드는 것 외에도 사용자 또는 리소스 속성을 기반으로 조건 및 규칙을 추가하여 각 사용자가 할 수있는 것과 수행 할 수없는 것을 지정할 수도 있습니다.

이제 허가와 그 혜택에 대해 알아야 할 대부분의 것을 알았으므로 주요 거래에 참여해 보겠습니다. SaaS 응용 프로그램을 Next.js와 함께 구축하고 승인 허가를 통합하십시오.

허가의 힘을 보여주기 위해, 우리는 멀티 테넌트 Edtech Saas 플랫폼을 구축 할 것입니다.

Edtech SaaS 플랫폼을 구축하려면 사용자 인증, 역할 기반 액세스 제어 (RBAC) 및 다중 테넌시를 포함한 몇 가지 과제가 필요합니다. 우리는 Frontend에 Next.js를 사용하고, 인증 및 데이터베이스 관리를위한 AppWrite 및 세밀한 승인 허가를받을 수 있습니다.

기술 스택 개요

TechnologyPurposenext.jsfrontend frameworkshadcn tailwindcssui 구성 요소 및 스타일 링거 스탠드 스테이트 ManagementAppWriteAuthentication & backendpermit.iorole 기반 액세스 제어

시스템 아키텍처

응용 프로그램은 백엔드 우선 접근 방식을 따릅니다.

  1. 백엔드 (node.js Express)
    • API 요청 및 비즈니스 로직을 처리합니다.
    • 인증 및 데이터베이스 관리에 AppWrite를 사용합니다.
    • 권한 부여 허가, 역할 및 권한을 정의합니다.
    • 데이터 액세스 전에 모든 요청이 검증되도록합니다.
  2. 프론트 엔드 (Next.js)
    • 백엔드에 연결하여 데이터를 단단히 가져옵니다.
    • 역할 기반 UI 렌더링을 사용합니다. 즉, 사용자는 액세스 권한 만 볼 수 있습니다.
    • 권한에 따라 작업 (과제 생성)을 제한합니다.

API 수준에서 승인을 시행함으로써 사용자는 프론트 엔드를 조작하더라도 제한을 우회 할 수 없도록합니다.

이 안내서의 끝에는 완벽하게 기능적인 다중 테넌트 Edtech SaaS 앱이 있습니다.

  • 관리자는 학생들을 추가하고 볼 수 있습니다.
  • 교사는 학생들을 추가하고보고 과제를 만들 수 있습니다.
  • 학생들은 할당 된 교과 과정 만 볼 수 있습니다.

이 기사는이 프로젝트를 구축하기위한 승인을 처리 할 수있는 허가를 구현 한 방법에 대한 단계별 분석을 제공하므로 따라 가서 구축하십시오.

허가를 통한 백엔드 구현

역할 기반 액세스 제어 (RBAC) 및 테넌트 격리를 시행하려면 다음을 수행해야합니다.

  1. 허가를 설정하고 역할, 임차인 및 정책을 정의합니다.
  2. 백엔드 (node.js express)에 허가를 통합하십시오.
  3. 요청을 허용하기 전에 권한을 확인하는 미들웨어를 사용하여 API 경로를 보호하십시오.

단계별로 가자.

1. 허가 설정

코드를 작성하기 전에 필요합니다

  • 허가에 계정을 만듭니다.
Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

온 보딩이 제공되지만 조직 이름을 입력하면 설정을 건너 뛸 수 있습니다.

  • 자원과 행동을 만듭니다

정책 섹션으로 이동하여 해당 리소스에서 수행 할 수있는 리소스 및 조치를 만들 수 있습니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

자원을 만들면 다음과 같이 보일 것입니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축
  • 역할 만들기

리소스를 작성한 후 역할 탭을 사용하여 역할 페이지로 이동하십시오. 일부 역할이 자동으로 할당되었음을 알 수 있습니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

이러한 역할을 삭제하고 새로운 역할을 만듭니다. 각 역할에는 사용자가 할 수 있고 할 수없는 일에 대한 특정 규칙이 있습니다. 나중에 RBAC 조건의 빌딩 블록 역할을하므로 관리자 역할을 먼저 만듭니다. 상단에서 역할 추가 버튼을 클릭하고 역할을 만듭니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

역할을 만들 때 다음과 같이 보일 것입니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

엄청난!

리소스와 역할을 만들었으므로 이제 정책 편집기에서 권한을 구성 할 수 있습니다.

  • 정책 편집기의 권한 구성

정책 편집기로 돌아가서 각 개별 리소스가 정의되고 선택할 수있는 조치와 함께 역할이 어떻게 보이는지입니다. 이제 리소스에서 선택한 작업을 수행하기 위해 역할에 대한 권한을 부여 할 준비가되었습니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

각 역할에 대한 작업을 선택하면 페이지 오른쪽 하단의 변경 사항 저장 버튼을 클릭하십시오.

  • API 키를 복사하십시오

마지막으로, Cloud PDP의 허가증을 사용하려면 현재 환경의 API 키가 필요합니다. 이 프로젝트의 경우 개발 환경 키를 사용할 것입니다. 설정을 진행하고 API 키를 클릭하고 환경 API 키로 스크롤 한 다음 "공개 키"를 클릭 한 다음 복사하십시오.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

허가 대시 보드를 설정 한 후 이제 백엔드로 이동할 수 있습니다.

2. 종속성 설치

시작하려면 컴퓨터에 node.js를 설치해야합니다. Node.js가 시스템에 설치되는지 확인한 후 다음 단계를 따르십시오.

  • 다음 명령을 사용하여 새 프로젝트를 작성하여 시작하십시오.
 mkdir 백엔드
cd backendnpm init -y
  • 그런 다음 다음 패키지를 설치하십시오.
 NPM 설치 Express Dotenv Permitio Cors Appwwrite Axios Jsonwebtoken
  • Express에서 허가를 구성하십시오. .env 파일에서 API 키를 저장하십시오.
 permit_api_key = your-permit-key-you-copied-earlier

3. AppWrite 설정

  • AppWrite로 이동하여 프로젝트 이름을 입력하고 지역을 선택하여 새 프로젝트를 만듭니다. 프로젝트 ID 및 API 엔드 포인트를 기록하십시오. 그것이 .env 파일의 값으로 입력 할 것입니다. ENV 파일은 다음과 같아야합니다.
 permit_api_key = your-permit-key-you-copied-earlier
appwrite_endpoint = https : //cloud.appwrite.io/v1
appwrite_project_id = your-project-id
  • 이제 데이터베이스를 진행하여 데이터베이스를 작성한 다음 데이터베이스 ID를 복사하여 ENV 파일에 붙여 넣습니다.
Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

ENV 파일은 이제 다음과 같습니다.

 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

이제 다음 속성으로 AppWrite 데이터베이스에서 다음 컬렉션을 만듭니다.

  • 프로파일 컬렉션
Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축
  • 학생 컬렉션
Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축
  • 과제 수집
Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

이 시점에서 ENV 파일이 보이는 것 :

 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-s-running // OpenSSL rand -base64 16
포트 = 8080

4. 파일 구조 및 파일을 만듭니다

이제 파일 루트에 SRC 폴더를 만듭니다. 그런 다음 루트 폴더에서 tsconfig.json 파일을 생성하고 다음 코드를 붙여 넣습니다.

 <span>{
</span><span>"컴파일러 옵션": {
</span><span>"대상": "ES6",
</span><span>"모듈": "commonjs",
</span><span>"outdir": "./dist",
</span><span>"EsmoduleInterop": True,
</span><span>"ForCeconsentCasingInfilenames": True,
</span><span>"엄격한": 사실,
</span><span>"SkipLibCheck": True,
</span><span>"ResolveJsonModule": True,
</span><span>"baseurl": "./",
</span><span>"경로": {
</span><span>"@/*": [ "SRC/*"]
</span><span>}
</span><span>},
</span><span>"포함": [ "SRC/**/*"],
</span><span>"제외": [ "node_modules", "dist"]]]
</span><span>}</span>

이 tsconfig.json은 ES6을 대상으로 TypeScript 컴파일러를 구성하고 CommonJS 모듈을 사용하며 파일을 ./dist로 구성합니다. 엄격한 유형 확인을 강화하고 JSON 모듈 해상도를 활성화하고 SRC의 경로 별칭을 설정하며 Node_Modules 및 컴파일에서 DEST를 제외합니다.

SRC 폴더 내부에는 API, 구성, 컨트롤러, 미들웨어, 모델 및 유틸리티의 다음 폴더를 만듭니다.

  • 폴더를 사용합니다
    • 이제 UTILS 폴더 프로젝트에서 새 Cermit.ts 파일을 작성하여 다음 코드를 사용하여 허가를 초기화합니다.
 <span>'permitio'에서 {refit} 가져 오기;
</span><span>'../config/environment'에서 {permit_api_key} 가져 오기;
</span><span>//이 라인은 SDK를 초기화하고 Node.js 앱을 연결합니다.
</span><span>// 이전 단계에서 설정 한 PDP 컨테이너.
</span><span>const 허가 = 새로운 허가 ({
</span><span>// API 키
</span> 토큰 <span>: permit_api_key, // API 키를 .env에 저장
</span><span>// 제작시 배포에 맞게이 URL을 변경해야 할 수도 있습니다.
</span> pdp <span>: 'https://cloudpdp.api.permit.io', // default remit.io pdp url
</span><span>// SDK가 로그를 방출하기를 원한다면 타협 :
</span> 통나무 <span>: {
</span> 레벨 <span>: "디버그",
</span><span>},
</span><span>// 시간 초과 / 네트워크 오류가 발생하면 SDK가 거짓을 반환합니다.
</span><span>// 대신 오류를 던지기를 원한다면이를 처리 할 수 ​​있도록하십시오.
</span><span>// ThrowOnerror : true,
</span><span>});
</span>
<span>수출 기본 허가;</span>

이 파일은 Node.js 용 SDK의 SDK를 초기화하여 환경에 저장된 API 키를 사용하여 허가 PDP 컨테이너에 연결합니다. 디버깅 로그 로그를 구성하고 SDK를 명시 적으로 던지지 않는 한 SDK를 조용히 처리하도록 설정합니다.

  • 다음으로 ErrorHandler.ts라는 파일을 작성하고 다음 코드를 붙여 넣습니다.
 <span>// 유틸리티 함수 (예 : 오류 처리)
</span><span>'Express'에서 {request, response, nextfunction} import;
</span>
<span>내보내기 const errorhandler = (err : one, req : requ : res : responce, nextfunction) => {
</span><span>console.error ( 'error :', err.message || err);
</span> <span>res.status (err.status || 500) .json ({{
</span> 오류 <span>: err.message || '내부 서버 오류',
</span><span>});
</span><span>};</span>

이 파일은 오류를 기록하고 오류 메시지 및 상태 코드로 JSON 응답을 보내는 Express Error 처리 미들웨어를 정의합니다. 특정 상태가 제공되지 않으면 기본적으로 500 상태 코드로 기본적으로 표시됩니다.

  • 모델 폴더
    • profile.ts라는 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>인터페이스 프로필 내보내기 {
</span> 이름 <span>: 문자열;
</span> 이메일 <span>: 문자열;
</span> 역할 <span>: '관리자'| '교사'| '학생';
</span> userID <span>: 문자열;
</span><span>}</span>

이 파일은 이름, 이메일, 역할 및 userID의 속성이있는 TypeScript 프로파일 인터페이스를 정의하며, 여기서 역할은 관리, 교사 또는 학생으로 제한됩니다.

  • 할당을 작성하고 다음 코드를 붙여 넣습니다.
 <span>'../config/appwrite'에서 {database, id} 가져 오기;
</span><span>import {database_id, antsamments_collection_id} from '../config/environment';
</span>
<span>인터페이스 내보내기 할당 data {
</span> 제목 <span>: 문자열;
</span> 제목 <span>: 문자열;
</span> ClassName <span>: 문자열;
</span> 교사 <span>: 문자열;
</span> 빙하 <span>: 문자열;
</span> CreatorEmail <span>: 문자열;
</span><span>}
</span>
<span>// 새 할당을 만듭니다
</span><span>Async 함수 createAssignmentIndb (data : antlodmentData) {
</span><span>return await database.createdocument (
</span><span>database_id,
</span><span>과제 _collection_id,
</span><span>id.unique (),
</span>   데이터
<span>);
</span><span>}
</span>
<span>// 모든 과제를 가져옵니다
</span><span>내보내기 비동기 기능 fetchAssignmentsfromdb () {
</span><span>const response = database.listDocuments (database_id, antlicments_collection_id);
</span><span>반환 응답.
</span><span>}</span>

이 파일은 할당을 관리하기 위해 AppWrite 데이터베이스와 상호 작용하는 기능을 제공합니다. 과제 데이터 인터페이스를 정의하고 새로운 할당을 생성하고 데이터베이스에서 모든 할당을 가져 오는 기능을 포함합니다.

  • Student.ts 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>import {데이터베이스, ID, 권한, 역할, 쿼리}에서 '../config/appwrite';
</span><span>'../config/environment'에서 {database_id, whitsing_collection_id};
</span>
<span>인터페이스 수출 학생 데이터 {
</span> FirstName <span>: 문자열;
</span> 마지막 이름 <span>: 문자열;
</span> 성별 <span>: '여자'| '소년'| '소년'| '소녀';
</span> ClassName <span>: 문자열;
</span> 나이 <span>: 숫자;
</span> CreatorEmail <span>: 문자열;
</span><span>}
</span>
<span>// 새로운 학생을 만듭니다
</span><span>Async 함수 내보내기 createStudentIndb (data : windentdata) {
</span><span>return await database.createdocument (
</span><span>database_id,
</span><span>학생 _collection_id,
</span><span>id.unique (),
</span> 데이터 <span>,
</span><span>[의 뜻
</span> 권한 <span>.Read (역할 .ANY ()), // 공개 읽기 권한
</span><span>]]
</span><span>);
</span><span>}
</span>
<span>// 모든 학생들을 가져 오십시오
</span><span>내보내기 비동기 기능 FetchStudentsfromdb () {
</span><span>const response = database.listdocuments (database_id, whitsing_collection_id);
</span><span>반환 응답.
</span><span>}</span>

이 파일은 AppWrite 데이터베이스에서 학생 데이터를 관리하는 기능을 제공합니다. StudentData 인터페이스를 정의하고 공개 읽기 권한이있는 신입생을 만들고 데이터베이스에서 모든 학생을 가져 오는 기능을 포함합니다.

  • 미들웨어 폴더
    • Auth.ts 파일을 작성하고 다음 코드를 붙여 넣습니다.
 <span>'Express'에서 {request, response, nextfunction} import;
</span><span>'jsonwebtoken'에서 JWT 가져 오기;
</span>
<span>// '사용자'를 포함하도록 요청 유형 확장
</span><span>인터페이스 authenticatedRequest 확장 요청 {
</span> 사용자 <span>? : {
</span>  ID <span>: 문자열;
</span>  역할 <span>: 문자열;
</span><span>};
</span><span>}
</span>
<span>const authmiddleware = (req : authenticatedrequest, res : response, nextfunction) : void => {
</span><span>const token = req.headers.authorization? .split ( '') [1];
</span>
<span>if (! token) {
</span>  <span>res.status (401) .json ({error : '무단. 토큰이 제공되지 않음');
</span><span>반품
</span><span>}
</span>
<span>노력하다 {
</span><span>const decoded = jwt.verify (token, process.env.jwt_secret!) as {id : string; 역할 : 문자열};
</span>  req <span>.user = 디코딩;
</span><span>다음();
</span><span>} catch (오류) {
</span>  <span>res.status (403) .json ({error : 'invalid token'});
</span><span>반품
</span><span>}
</span><span>};
</span>
<span>기본 authmiddleware 내보내기;</span>


이 파일은 JWT 기반 인증을위한 Express Middleware를 정의합니다. 요청 헤더에서 유효한 토큰을 확인하고 비밀 키를 사용하여 확인하고 디코딩 된 사용자 정보 (ID 및 역할)를 요청 객체에 첨부합니다. 토큰이 누락되거나 유효하지 않으면 적절한 오류 응답을 반환합니다.

  • permit.ts를 만들고 다음 코드를 붙여 넣습니다.
 <span>'../utils/permit'의 수입 허가;
</span>
<span>내보내기 const checkusertopermitstudents = async (이메일 : 문자열, 동작 : 문자열, 자원 : 문자열) : 약속 <boolean> => {
</boolean></span><span>노력하다 {
</span><span>const 허가 = 대기 허가. 체크 (이메일, 조치, 자원);
</span><span>Console.log ( "허가", 허용);
</span><span>허용 된 반품;
</span><span>} catch (오류) {
</span><span>console.error ( <span>`erver user <span>$ {email}</span> recmit.io :`</span> , error);
</span><span>거짓을 반환합니다.
</span><span>}
</span><span>};
</span>
<span>내보내기 const checkusertopermitAssignment = async (이메일 : 문자열, 동작 : 문자열, 자원 : 문자열) : 약속 <boolean> => {
</boolean></span><span>노력하다 {
</span><span>const 허가 = 대기 허가. 체크 (이메일, 조치, 자원);
</span><span>Console.log ( "허가", 허용);
</span><span>허용 된 반품;
</span><span>} catch (오류) {
</span><span>console.error ( <span>`erver user <span>$ {email}</span> recmit.io :`</span> , error);
</span><span>거짓을 반환합니다.
</span><span>}
</span><span>};</span>

이 파일은 유틸리티 기능, CheckuserTopermitStudents 및 CheckuserToPermitAssignment를 정의하여 특정 조치 및 리소스에 대한 허가의 사용자 권한을 확인합니다. 두 기능 모두 오류를 우아하게 처리하고 허가 확인이 실패하면 문제를 기록하고 거짓을 반환합니다. 응용 프로그램에서 승인을 집행하는 데 사용됩니다.

  • 컨트롤러 폴더
    • Auth.ts 파일을 작성하고 다음 코드를 붙여 넣습니다.
 <span>'../config/appwrite'에서 {account, id} 가져 오기;
</span><span>'Express'에서 {request, response} 가져 오기;
</span><span>'jsonwebtoken'에서 JWT 가져 오기;
</span>
<span>const jwt_secret = process.env.jwt_secret로 문자열로; // 이것이 .env 파일에 설정되어 있는지 확인하십시오
</span>
<span>// 가입 컨트롤러
</span><span>내보내기 const 가입 = async (req : request, res : response) => {
</span><span>const {이메일, 비밀번호, 이름} = req.body;
</span>
<span>if (! email ||! password ||! name) {
</span><span>return res.status (400) .json ({error : '이름, 이메일 및 비밀번호가 필요합니다.'});
</span><span>}
</span>
<span>노력하다 {
</span><span>const user = await account.create (id.unique (), 이메일, 암호, 이름);
</span><span>// JWT를 생성합니다
</span><span>const token = jwt.sign ({email}, jwt_secret, {expiresin : '8h'});
</span>   res <span>.cookie ( '토큰', 토큰, {
</span>    httponly <span>: 사실,
</span>    Samesite <span>: 'Strict',
</span>    Secure <span>: True,
</span><span>});
</span>
  <span>res.status (201) .json ({성공 : True, User, Token});
</span><span>} catch (오류 : Any) {
</span><span>Console.error ( '서명 오류 :', 오류);
</span>  res <span>.status (500) .json ({성공 : false, message : error.message});
</span><span>}
</span><span>};
</span>
<span>// 로그인 컨트롤러
</span><span>내보내기 const login = async (req : request, res : response) => {
</span><span>const {email, password} = req.body;
</span>
<span>if (! email ||! password) {
</span><span>return res.status (400) .json ({error : '이메일과 암호가 필요합니다.'});
</span><span>}
</span>
<span>노력하다 {
</span><span>const session = await account.createemailpasswordsession (이메일, 암호);
</span>
<span>// 역할없이 JWT를 생성합니다
</span><span>const token = jwt.sign (
</span><span>{userId : session.userid, email}, // 역할이 포함되어 있지 않습니다
</span><span>JWT_SECRET,
</span><span>{expiresin : '8h'}
</span><span>);
</span>
  res <span>.cookie ( '토큰', 토큰, {
</span>   httponly <span>: 사실,
</span>   Samesite <span>: 'Strict',
</span>   Secure <span>: True,
</span><span>});
</span>
  res <span>.Status (200) .json ({성공 : True, Token, Session});
</span><span>} catch (오류 : Any) {
</span><span>console.error ( '로그인 오류 :', 오류);
</span>  <span>res.status (401) .json ({성공 : false, message : error.message});
</span><span>}
</span><span>};
</span>
<span>// 로그 아웃 컨트롤러
</span><span>내보내기 const logout = async (req : request, res : response) => {
</span><span>노력하다 {
</span><span>AWAIT Account.deletesession ( '현재 세션 ID');
</span>  res <span>.clearCookie ( '토큰');
</span>  <span>res.status (200) .json ({성공 : true, 메시지 : '성공적으로 로그 아웃');
</span><span>} catch (오류 : Any) {
</span><span>console.error ( '로그 아웃 오류 :', 오류);
</span>  res <span>.status (500) .json ({성공 : false, message : error.message});
</span><span>}
</span><span>};</span>

이 파일은 가입, 로그인 및 로그 아웃을위한 인증 컨트롤러를 정의하여 사용자 관리 용 AppWrite 및 세션 처리 용 JWT와 통합됩니다. 가입 및 로그인 컨트롤러는 입력을 유효성있게 검증하고 사용자 세션을 작성하며 JWT를 생성하는 반면 로그 아웃 컨트롤러는 세션 및 토큰을 지 웁니다. 모든 컨트롤러는 오류를 처리하고 적절한 응답을 반환합니다.

  • 할당을 작성하고 다음 코드를 붙여 넣습니다.
 <span>'Express'에서 {request, response} 가져 오기;
</span><span>import {createAssignmentIndb, antlasmentData, fetchAssignmentsfromdb}에서 '../models/assignment';
</span><span>import {checkusertoPermitAssignment}에서 '../middleware/permit';
</span>
<span>// 새 할당을 만듭니다
</span><span>내보내기 비동기 함수 createAssignment (req : request , res : response) : 약속 <void> {
</void></span><span>노력하다 {
</span><span>const {제목, 주제, 교사, 클래스 이름, deudate, creatoremail} : antlymentdata = req.body;
</span>
<span>const ispermitted = checkusertopermitAssignment (CreatorEmail, "Create", "할당");
</span><span>if (! IsPermitted) {
</span>      <span>res.status (403) .json ({error : 'Authorized'});
</span><span>반품;
</span><span>}
</span>
<span>const newAssignment = reakeateSignmentIndb ({{
</span>      제목 <span>,
</span>      주제 <span>,
</span>      선생님 <span>,
</span>      Classname <span>,
</span>      악마 <span>,
</span>      CreatorEmail
<span>});
</span>
<span>Console.log ( '새 과제가 만들어졌습니다 :', NewAssignment);
</span>
    res <span>.Status (201) .json (NewAssignment);
</span><span>} catch (오류) {
</span><span>console.error ( '오류 생성 할당 :', 오류);
</span>    <span>res.status (500) .json ({error : (오류) .message});
</span><span>} 
</span><span>}
</span>
<span>// 모든 과제를 가져옵니다
</span><span>Async 함수 내보내기 FetchAssignments (req : request, res : response) : promise <void> {
</void></span><span>노력하다 {
</span><span>const {email} = req.params;
</span> 
<span>const ispermitted = cheeckusertopermitassignment (이메일, "읽기", "과제");
</span><span>if (! IsPermitted) {
</span>      <span>res.status (403) .json ({메시지 : '승인되지 않음'});
</span><span>반품;
</span><span>}
</span>
<span>const 할당 = AwetchAssignmentsFromdb ();
</span>    res <span>.Status (200) .json (과제);
</span><span>} catch (오류) {
</span>    <span>res.status (500) .json ({error : (오류) .message});
</span><span>}
</span><span>}</span>

이 파일은 데이터베이스와 통합하고 권한 확인 수표를 허용하기 위해 할당을 작성하고 가져 오기위한 컨트롤러를 정의합니다. CreateAssignment 컨트롤러는 입력을 확인하고 권한을 확인하며 새 할당을 생성하는 반면 FetchAssignments 컨트롤러는 액세스를 확인한 후 모든 할당을 검색합니다. 두 컨트롤러 모두 오류를 처리하고 적절한 응답을 반환합니다.

  • Student.ts 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>수입 {
</span>  CreateStudentIndb <span>,
</span>  Fetchstudentsfromdb <span>,
</span>  StudentData
<span>} from '../models/student';
</span><span>'Express'에서 {request, response} 가져 오기;
</span><span>'../middleware/permit'에서 {checkusertoPermitStudents} import;
</span>
<span>내보내기 비동기 함수 생성물 (req : request, res : response) : promise <void> {
</void></span><span>노력하다 {
</span><span>const {FirstName, LastName, Gender, ClassName, Age, CreatorEmail} : StudentData = Req.body;
</span>
<span>if (! [ 'girl', 'boy']. 포함 (gender)) {
</span>      <span>res.status (400) .json ({error : 'invalid gender type'});
</span><span>반품;
</span><span>}
</span>
<span>const ispermitted = checkusertopermitstudents (CreatoreMail, "Create", "Student");
</span><span>if (! IsPermitted) {
</span>      <span>res.status (403) .json ({메시지 : '승인되지 않음'});
</span><span>반품;
</span><span>}
</span>
<span>Const Newstudent = Await CreateStudentIndb ({
</span>      FirstName <span>,
</span>      성 <span>,
</span>      성별 <span>,
</span>      Classname <span>,
</span>      나이 <span>,
</span>      CreatorEmail
<span>});
</span>    res <span>.Status (201) .json (Newstudent);
</span><span>} catch (오류) {
</span>    <span>res.status (500) .json ({error : (오류) .message});
</span><span>} 
</span><span>}
</span>
<span>// 모든 학생들을 가져 오십시오
</span><span>비동기 기능 FetchStudents (Req : request, res : response) : 약속 <void> {
</void></span><span>노력하다 {
</span><span>const {email} = req.params;
</span>
<span>const ispermitted = jebusertopermitstudents (이메일, "읽기", "학생");
</span><span>if (! IsPermitted) {
</span>      <span>res.status (403) .json ({메시지 : '승인되지 않음'});
</span><span>반품;
</span><span>}
</span>
<span>Const 학생 = FetchStudentsffromdb ()를 기다립니다.
</span>    res <span>.Status (200) .json (학생);
</span><span>} catch (오류) {
</span>    <span>res.status (500) .json ({error : (오류) .message});
</span><span>}
</span><span>}</span>

이 파일은 학생들을 만들고 가져 오는 컨트롤러를 정의하고 데이터베이스와 통합하고 권한 수표에 대한 허가를받습니다. CreateStudent 컨트롤러는 입력을 검증하고 권한을 확인하며 새로운 학생을 생성하는 반면 FetchStudents 컨트롤러는 액세스를 확인한 후 모든 학생을 검색합니다. 두 컨트롤러 모두 오류를 처리하고 적절한 응답을 반환합니다.

  • profile.ts 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>'@/models/profile'에서 {profile} 가져 오기;
</span><span>'axios'에서 axios 가져 오기;
</span><span>'../config/appwrite'에서 {database, id, query} import;
</span><span>'express'에서 가져 오기 {request, response, nextfunction, requestHandler};
</span><span>'../config/environment'에서 {permit_api_key} 가져 오기;
</span>
<span>const profileid = process.env.appwrite_profile_collection_id로 문자열로; // 이것이 .env에 있는지 확인하십시오
</span><span>const databaseid = process.env.appwrite_database_id로 문자열로; // 이것이 .env에 있는지 확인하십시오
</span><span>const projectid = process.env.permit_project_id로 문자열로
</span><span>const 환경 = process.env.permit_env_id로 문자열로
</span>
<span>const permit_api_url = <span>`https://api.permit.io/v2/facts/ <span>$ {projectId}</span> / <span>$ {환경}</span> /users`</span> ;
</span><span>const permit_auth_header = {
</span> 권한 부여 <span>: <span>`bearer <span>$ {permit_api_key}</span> `</span> ,
</span><span>"Content-Type": "Application/JSON",
</span><span>};
</span>
<span>// 프로필 컨트롤러를 만듭니다
</span><span>consteprofile을 내보내기 : requestHandler = async (req : request, res : response, next : nextfunction) : promise <void> => {
</void></span><span>const {firstName, lastName, 이메일, 역할, userId} = req.body;
</span><span>Console.log (Req.body);
</span>
<span>if (! 이메일 ||! 역할 ||! userId) {
</span>  res <span>.status (400) .json ({error : 'firstName, lastName, 이메일, 역할 및 userID가 필요합니다.'});
</span><span>반품;
</span><span>}
</span>
<span>// 역할을 확인합니다
</span><span>const allendroles : profile [ 'role'] [] = [ 'admin', '교사', '학생'];
</span><span>if (! enlodroles.includes (역할)) {
</span>  res <span>.Status (400) .json ({오류 : '유효하지 않은 역할. 허용 된 역할 : 관리자, 교사, 학생'});
</span><span>반품;
</span><span>}
</span>
<span>노력하다 {
</span><span>const newUser = AWAIT Database.CreatEdocument (
</span>   Databaseid <span>,
</span>   profileId <span>,
</span><span>id.unique (),
</span><span>{FirstName, LastName, 이메일, 역할, userID}
</span><span>);
</span><span>// 2 단계 : 사용자가 sync onecmit.io를 동기화합니다
</span><span>const permitpayload = {
</span>   키 <span>: 이메일,
</span>   <span>이메일
</span>   First_Name <span>: FirstName,
</span>   last_name <span>: lastname,
</span>   role_Assignments <span>: [{역할, 임차인 : "기본값"}],
</span><span>};
</span>
<span>PermitReponse를하자;
</span><span>노력하다 {
</span><span>const response = await ac
</span>   permitResponse <span>= response.data;
</span><span>console.log ( "user synced to permit.io :", permitRepresponse);
</span><span>} catch (permitError) {
</span><span>if (axios.isaxioserror (permiterRor)) {
</span><span>console.error ( "rectim.io를 동기화하지 못했습니다.
</span><span>} 또 다른 {
</span><span>console.error ( "recmit.io를 동기화하지 못했습니다 :", cermiterRor);
</span><span>}
</span>   permitResponse <span>= {error : "permit.io와 동기화하지 못했습니다"};
</span><span>}
</span>
<span>// 3 단계 : 두 응답을 모두 반환합니다
</span>  <span>res.status (201) .json ({{
</span>   메시지 <span>: "사용자 프로필이 성공적으로 생성",
</span>   사용자 <span>: Newuser,
</span>   허가 <span>: CermitRepponse,
</span><span>});
</span><span>반품;
</span><span>} catch (오류 : Any) {
</span>  res <span>.status (500) .json ({성공 : false, message : error.message});
</span><span>반품;
</span><span>}
</span><span>};
</span>
<span>// 이메일로 프로필을 가져옵니다
</span><span>내보내기 const getProfileByemail = async (req : request, res : responce, nextfunction) : 약속 <void> => {
</void></span><span>const {email} = req.params;
</span> 
<span>if (! 이메일) {
</span>  <span>res.status (400) .json ({error : '이메일이 필요합니다.'});
</span><span>반품;
</span><span>}
</span>
<span>노력하다 {
</span><span>const profile = database.listDocuments (
</span>   Databaseid <span>,
</span>   profileId <span>,
</span><span>[query.eartal ( "이메일", 이메일)]
</span><span>);
</span>
<span>if (profile.documents.length === 0) {
</span>   <span>res.status (404) .json ({error : 'profile found'});
</span><span>반품;
</span><span>}
</span>
  <span>res.status (200) .json ({성공 : true, profile : profile.documents [0]});
</span><span>} catch (오류 : Any) {
</span><span>console.error ( '오류 페치 프로파일 :', 오류);
</span>  res <span>.status (500) .json ({성공 : false, message : error.message});
</span><span>}
</span><span>};</span>

이 파일은 데이터베이스 작업을 위해 AppWrite와 통합 및 역할 동기화 허가를 통합하여 사용자 프로파일을 작성하고 가져 오기위한 컨트롤러를 정의합니다. CreateProfile 컨트롤러는 입력을 확인하고 프로필을 작성하며 사용자가 허용하도록 동기화하는 반면 GetProfileByemail 컨트롤러는 이메일로 프로필을 검색합니다. 두 컨트롤러 모두 오류를 처리하고 적절한 응답을 반환합니다.

  • 구성 폴더
    • AppWrite.ts 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>import {클라이언트, 계정, 데이터베이스, 스토리지, ID, 권한, 역할, 쿼리};
</span><span>import {appwrite_endpoint, appwrite_project_id, appwrite_api_key} from './environment';
</span>
<span>// AppWrite 클라이언트를 초기화합니다
</span><span>const client = new Client ()
</span><span>.setendpoint (appwrite_endpoint) // appwrite endpoint
</span><span>.SetProject (AppWrite_Project_id); // AppWrite Project ID
</span>
<span>// 사용 가능한 경우 API 키 추가 (서버 측 작업의 경우)
</span><span>if (appwrite_api_key) {
</span><span>(클라이언트) .config.key = appwrite_api_key; // API 키를 설정하려는 해결 방법
</span><span>}
</span>
<span>// appwrite 서비스 초기화
</span><span>const 계정 = 새 계정 (클라이언트);
</span><span>const 데이터베이스 = 새 데이터베이스 (클라이언트);
</span><span>const Storage = 새로운 스토리지 (클라이언트);
</span>
<span>// appwrite 클라이언트 및 서비스를 내보내십시오
</span><span>내보내기 {클라이언트, 계정, 데이터베이스, 스토리지, ID, 권한, 역할, 쿼리};</span>

이 파일은 Project Endpoint, ID 및 옵션 API 키로 AppWrite 클라이언트를 초기화하고 구성합니다. 또한 ID, 권한, 역할 및 쿼리와 같은 유틸리티 상수와 함께 계정, 데이터베이스 및 스토리지와 같은 AppWrite 서비스를 설정하고 내 보냅니다.

  • Environment.ts 파일을 작성하고 다음 코드를 붙여 넣습니다.
 <span>'dotenv'에서 dotenv 가져 오기;
</span>dotenv <span>.config (); // .env에서 환경 변수를로드합니다
</span>
<span>const appwrite_endpoint = process.env.appwrite_endpoint || 내보내기 '';;
</span><span>내보내기 const permit_api_key = process.env.permit_api_key || '';;
</span><span>내보내기 const permit_project_id = process.env.permit_project_id || '';;
</span><span>내보내기 const permit_env_id = process.env.permit_env_id || '';;
</span><span>내보내기 const appwrite_project_id = process.env.appwrite_project_id || '';;
</span><span>const database_id = process.env.appwrite_database_id || 내보내기 '';;
</span><span>const withy whity_collection_id = process.env.appwrite_students_collection_id ||를 내보내십시오 '';;
</span><span>내보내기 const ansastments_collection_id = process.env.appwrite_assignments_collection_id || '';;
</span>
<span>내보내기 const profile_collection_id = process.env.appwrite_profile_collection_id || '';;</span>

이 파일은 .env 파일에서 환경 변수를로드하고 AppWrite 및 허가 구성, 데이터베이스 ID 및 수집 ID와 같은 응용 프로그램에서 사용하기위한 상수로 내보내립니다. 환경 변수가 설정되지 않은 경우 기본값은 폴백으로 제공됩니다.

  • API 폴더
    • 학생 .ts를 만들고 다음 코드를 붙여 넣습니다.
 <span>'Express'에서 Express 가져 오기;
</span><span>'../controllers/student'에서 {CreateStudent, FetchStudents} import;
</span><span>'../middleware/auth'에서 authmiddleware 가져 오기;
</span>
<span>const router = express.router ();
</span>
<span>// 학생 관련 엔드 포인트를 정의합니다
</span>라우터 <span>.post ( '/student', authmiddleware, createStudent); // 새로운 학생을 만듭니다
</span>라우터 <span>.get ( '/whitsing/: email', authmiddleware, fetchstudents); // 모든 학생들을 가져 오십시오
</span><span>기본 라우터 내보내기; // 라우터 인스턴스를 내보내십시오</span>

이 파일은 학생 데이터를 관리하기위한 엔드 포인트가 포함 된 Express 라우터를 설정합니다. 여기에는 새로운 학생을 만들고 인증 미들웨어 (Authmiddleware)로 보호되는 학생을위한 경로가 포함됩니다. 그런 다음 라우터가 응용 프로그램에 사용하기 위해 내보내립니다.

  • Auth.ts 파일을 작성하고 다음 코드를 붙여 넣습니다.
 <span>// src/loutes/authroutes.ts.ts
</span><span>'Express'에서 Express 가져 오기;
</span><span>'../controllers/auth'에서 가져 오기 {가입, 로그인, 로그 아웃};
</span>
<span>const router = express.router ();
</span>
<span>// 인증 관련 엔드 포인트를 정의합니다
</span>라우터 <span>.post ( '/signup', (req, res, next) => {// 가입 경로
</span><span>가입 (req, res) .then (() => {
</span><span>다음();
</span><span>}). catch ((err) => {
</span><span>다음 (err);
</span><span>});
</span><span>});
</span>라우터 <span>.post ( '/login', (req, res, next) => {// 로그인 경로
</span><span>로그인 (req, res) .then (() => {
</span><span>다음();
</span><span>}). catch ((err) => {
</span><span>다음 (err);
</span><span>});
</span><span>});
</span>라우터 <span>.post ( '/logout', 로그 아웃); // 로그 아웃 경로
</span><span>기본 라우터 내보내기; // 라우터 인스턴스를 내보내십시오</span>

이 파일은 사용자 가입, 로그인 및 로그 아웃을 포함하여 인증 관련 작업을위한 엔드 포인트가있는 Express 라우터를 설정합니다. 가입 및 로그인 경로는 오류 처리로 비동기 작업을 처리하는 반면 로그 아웃 경로는 간단합니다. 라우터는 응용 프로그램에 사용하기 위해 내보내집니다.

  • 할당을 작성하고 다음 코드를 붙여 넣습니다.
 <span>"Express"에서 Express 가져 오기
</span><span>"../controllers/assignment"에서 {createAssignment, fetchassignments} import
</span><span>"../middleware/auth"에서 authmiddleware 가져 오기
</span>
<span>const router = express.router ()
</span>
라우터 <span>.post ( "/create", authmiddleware, createAssignment)
</span>라우터 <span>.get ( "/: email", authmiddleware, fetchassignments)
</span><span>기본 라우터 내보내기</span>

이 파일은 할당을 관리하기위한 엔드 포인트가 포함 된 Express 라우터를 설정합니다. 여기에는 Authentication Middleware (Authmiddleware)에 의해 보호되는 과제 및 가져 오기 할당을위한 경로가 포함됩니다. 라우터는 응용 프로그램에 사용하기 위해 내보내집니다.

  • profile.ts 파일을 만들고 다음 코드를 붙여 넣습니다.
 <span>'Express'에서 Express 가져 오기;
</span><span>'../controllers/profile'에서 {createprofile, getProfileByemail};
</span><span>'../middleware/auth'에서 authmiddleware 가져 오기;
</span>
<span>const router = express.router ();
</span>
<span>// 프로파일 생성 경로
</span>라우터 <span>.post ( '/profile', authmiddleware, createprofile);
</span>
<span>// Route for getting a profile by email
</span>router <span>.get('/profile/:email', authMiddleware, getProfileByEmail);
</span><span>export default router;</span>

This file sets up an Express router with endpoints for managing user profiles. It includes routes for creating a profile and fetching a profile by email, both protected by an authentication middleware (authMiddleware). The router is exported for use in the application.

  • Create index.ts file and paste the following code:
 <span>import express, { Request, Response } from 'express';
</span><span>import dotenv from 'dotenv';
</span><span>import cors from 'cors'; // CORS middleware
</span><span>import authRoutes from './auth'; // Import auth routes
</span><span>import profileRoutes from './profile';
</span><span>import studentRoutes from './student';
</span><span>import assignmentRoutes from './assignment';
</span><span>import { errorHandler } from '../utils/errorHandler'; // Custom error handler middleware
</span>
dotenv <span>.config(); // Load environment variables from .env file
</span>
<span>const app = express();
</span><span>const PORT = process.env.PORT || 8080;
</span>
<span>// Middleware
</span>app <span>.use(cors()); // Handle CORS
</span>app <span>.use(express.json()); /// Parse incoming JSON requests
</span>
<span>// Routes
</span>app <span>.use('/api/auth', authRoutes); // Authentication routes
</span>app <span>.use('/api', profileRoutes); // Profile routes mounted
</span>app <span>.use('/api', studentRoutes); // Student routes mounted
</span>app <span>.use('/api/assignments', assignmentRoutes); // Assignment routes mounted
</span>
<span>// Global Error Handling Middleware
</span>app <span>.use(errorHandler); // Handle errors globally
</span>
<span>// Default Route
</span>app <span>.get('/', (req: Request, res: Response) => {
</span> res <span>.send('Appwrite Express API');
</span><span>});
</span>
<span>// Start Server
</span>app <span>.listen(PORT, () => {
</span><span>console.log( <span>`Server is running on port <span>${PORT}</span> `</span> );
</span><span>});
</span><span>export default app;</span>

This file sets up an Express server, configuring middleware like CORS and JSON parsing, and mounts routes for authentication, profiles, students, and assignments. It includes a global error handler and a default route to confirm the server is running. The server listens on a specified port, logs its status, and exports the app instance for further use.

  • Finally, to run this project, change a part of package.json and install the following packages below so when you run npm run dev, it works.
    • Install packages:
 npm install concurrently ts-node nodemon --save-dev
  • By updating the scripts in the package.json, when you start the server, the typescript files are compiled to JavaScript in a new folder that is automatically created called dist
 "scripts": {
    "dev": "concurrently \"tsc --watch\" \"nodemon -q --watch src --ext ts --exec ts-node src/api/index.ts\"",
    "build": "tsc",
    "start": "node ./dist/api/index.js"
},

Now run npm run dev to start your server. When you see this message, it means that you have successfully implemented the backend.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축

Congratulations, your backend is ready for requests.

Now that our backend is set up, move on to frontend integration, where you'll:

  • Secure API requests from Next.js
  • Dynamically show/hide UI elements based on user permissions.

Reason for creating an extensive backend service using Appwrite

Appwrite is often described as a backend-as-a-service (BaaS) solution, meaning it provides ready-made backend functionality like authentication, database management, and storage without requiring developers to build a traditional backend.

However, for this project, I needed more flexibility and control over how data was processed, secured, and structured, which led me to create an extensive custom backend using Node.js and Express while still leveraging Appwrite's services.

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

  • Implement fine-grained access control with Permit.io before forwarding requests to Appwrite.
  • Structure API endpoints for multi-tenancy to ensure tenant-specific data isolation.
  • Create custom business logic, such as processing role-based actions before committing them to the Appwrite database.
  • Maintain a centralized API layer, making it easier to enforce security policies, log activities, and scale the application.

Appwrite provided the core authentication and database functionality of this application, but this additional backend layer enhanced security, flexibility, and maintainability, to ensure strict access control before any action reached Appwrite.

결론

That's it for part one of this article series. In part 2, we'll handle the frontend integration by setting up API calls with authorization, initializing and installing necessary dependencies, writing out the component file codes, and handling state management & routes.

위 내용은 Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
Python vs. JavaScript : 작업에 적합한 도구 선택Python vs. JavaScript : 작업에 적합한 도구 선택May 08, 2025 am 12:10 AM

Python 또는 JavaScript를 선택할지 여부는 프로젝트 유형에 따라 다릅니다. 1) 데이터 과학 및 자동화 작업을 위해 Python을 선택하십시오. 2) 프론트 엔드 및 풀 스택 개발을 위해 JavaScript를 선택하십시오. Python은 데이터 처리 및 자동화 분야에서 강력한 라이브러리에 선호되는 반면 JavaScript는 웹 상호 작용 및 전체 스택 개발의 장점에 없어서는 안될 필수입니다.

파이썬 및 자바 스크립트 : 각각의 강점을 이해합니다파이썬 및 자바 스크립트 : 각각의 강점을 이해합니다May 06, 2025 am 12:15 AM

파이썬과 자바 스크립트는 각각 고유 한 장점이 있으며 선택은 프로젝트 요구와 개인 선호도에 따라 다릅니다. 1. Python은 간결한 구문으로 데이터 과학 및 백엔드 개발에 적합하지만 실행 속도가 느립니다. 2. JavaScript는 프론트 엔드 개발의 모든 곳에 있으며 강력한 비동기 프로그래밍 기능을 가지고 있습니다. node.js는 풀 스택 개발에 적합하지만 구문은 복잡하고 오류가 발생할 수 있습니다.

JavaScript의 핵심 : C 또는 C에 구축 되었습니까?JavaScript의 핵심 : C 또는 C에 구축 되었습니까?May 05, 2025 am 12:07 AM

javaScriptisNotBuiltoncorc; it'SangretedLanguageThatrunsonOngineStenWrittenInc .1) javaScriptWasDesignEdasAlightweight, 해석 hanguageforwebbrowsers.2) Endinesevolvedfromsimpleplemporectreterstoccilpilers, 전기적으로 개선된다.

JavaScript 응용 프로그램 : 프론트 엔드에서 백엔드까지JavaScript 응용 프로그램 : 프론트 엔드에서 백엔드까지May 04, 2025 am 12:12 AM

JavaScript는 프론트 엔드 및 백엔드 개발에 사용할 수 있습니다. 프론트 엔드는 DOM 작업을 통해 사용자 경험을 향상시키고 백엔드는 Node.js를 통해 서버 작업을 처리합니다. 1. 프론트 엔드 예 : 웹 페이지 텍스트의 내용을 변경하십시오. 2. 백엔드 예제 : node.js 서버를 만듭니다.

Python vs. JavaScript : 어떤 언어를 배워야합니까?Python vs. JavaScript : 어떤 언어를 배워야합니까?May 03, 2025 am 12:10 AM

Python 또는 JavaScript는 경력 개발, 학습 곡선 및 생태계를 기반으로해야합니다. 1) 경력 개발 : Python은 데이터 과학 및 백엔드 개발에 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 적합합니다. 2) 학습 곡선 : Python 구문은 간결하며 초보자에게 적합합니다. JavaScript Syntax는 유연합니다. 3) 생태계 : Python에는 풍부한 과학 컴퓨팅 라이브러리가 있으며 JavaScript는 강력한 프론트 엔드 프레임 워크를 가지고 있습니다.

JavaScript 프레임 워크 : 현대적인 웹 개발 파워JavaScript 프레임 워크 : 현대적인 웹 개발 파워May 02, 2025 am 12:04 AM

JavaScript 프레임 워크의 힘은 개발 단순화, 사용자 경험 및 응용 프로그램 성능을 향상시키는 데 있습니다. 프레임 워크를 선택할 때 : 1. 프로젝트 규모와 복잡성, 2. 팀 경험, 3. 생태계 및 커뮤니티 지원.

JavaScript, C 및 브라우저의 관계JavaScript, C 및 브라우저의 관계May 01, 2025 am 12:06 AM

서론 나는 당신이 이상하다는 것을 알고 있습니다. JavaScript, C 및 Browser는 정확히 무엇을해야합니까? 그들은 관련이없는 것처럼 보이지만 실제로는 현대 웹 개발에서 매우 중요한 역할을합니다. 오늘 우리는이 세 가지 사이의 밀접한 관계에 대해 논의 할 것입니다. 이 기사를 통해 브라우저에서 JavaScript가 어떻게 실행되는지, 브라우저 엔진의 C 역할 및 웹 페이지의 렌더링 및 상호 작용을 유도하기 위해 함께 작동하는 방법을 알게됩니다. 우리는 모두 JavaScript와 브라우저의 관계를 알고 있습니다. JavaScript는 프론트 엔드 개발의 핵심 언어입니다. 브라우저에서 직접 실행되므로 웹 페이지를 생생하고 흥미롭게 만듭니다. 왜 Javascr

Node.js는 TypeScript가있는 스트림입니다Node.js는 TypeScript가있는 스트림입니다Apr 30, 2025 am 08:22 AM

Node.js는 크림 덕분에 효율적인 I/O에서 탁월합니다. 스트림은 메모리 오버로드를 피하고 큰 파일, 네트워크 작업 및 실시간 애플리케이션을위한 메모리 과부하를 피하기 위해 데이터를 점차적으로 처리합니다. 스트림을 TypeScript의 유형 안전과 결합하면 Powe가 생성됩니다

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기