NestJS 11 Enterprise API Patterns

Master NestJS 11 enterprise patterns: guards, interceptors, pipes, multi-tenancy, and production-ready API design for scalable backend systems.

E
ECOSIRE Research and Development Team
|19 مارس 202612 دقائق قراءة2.7k كلمات|

أنماط NestJS 11 Enterprise API

لقد برز NestJS 11 باعتباره إطار العمل الأمثل لبناء واجهات برمجة التطبيقات على مستوى الإنتاج في TypeScript، حيث يجمع بين البنية المستوحاة من Angular والقوة الأولية لـ Node.js. عندما تعمل على مستوى المؤسسة - تتعامل مع ملايين الطلبات، وتدير بيانات المستأجرين المتعددين، وتنسق العشرات من الوحدات - تحدد الأنماط التي تختارها في اليوم الأول ما إذا كانت قاعدة التعليمات البرمجية الخاصة بك تتوسع بسلاسة أو تنهار تحت ثقلها.

يستخلص هذا الدليل الدروس التي تم الحصول عليها بشق الأنفس من إنشاء واجهة خلفية NestJS 11 مكونة من 56 وحدة مع أكثر من 310 ملفات TypeScript، تغطي كل شيء بدءًا من تنظيم الوحدة وتكوين الحماية إلى أنماط الاستئجار المتعددة التي تصمد فعليًا تحت الحمل.

الوجبات الرئيسية

  • استخدم forRoutes('*path') وليس forRoutes('*') - قام NestJS 11 بتغيير مطابقة مسار البدل
  • يجب تسجيل عوامل تصفية الاستثناءات العامة في main.ts، وليس من خلال APP_FILTER
  • يتطلب الإيجار المتعدد تصفية organizationId في كل طبقة استعلام، وليس فقط البرامج الوسيطة
  • يعد نمط الديكور @Public() أكثر أمانًا من تعطيل الحراس بالكامل للطرق المفتوحة
  • لا تستخدم مطلقًا sql.raw() في استعلامات Drizzle - دائمًا ما تكون القيم الحرفية لقالب sql ذات معلمات
  • يجب أن يكون EmailModule @Global() لتجنب إعادة الاستيراد في كل وحدة ميزات
  • يجب تحميل Dotenv مسبقًا في main.ts قبل تشغيل NestJS لتجنب مشكلات دقة البيئة
  • تحديد المعدل إلزامي على جميع نقاط النهاية العامة - استخدم @nestjs/throttler

هيكل المشروع على نطاق واسع

القرار المعماري الأكثر أهمية في NestJS هو كيفية تنظيم الوحدات. على مستوى المؤسسة، تصبح قائمة الوحدات المسطحة في app.module.ts غير قابلة للإدارة. النمط الذي يعمل هو تجميع الوحدات النمطية المستندة إلى المجال مع إعلانات التبعية الصريحة.

apps/api/src/
  modules/
    auth/
      auth.module.ts
      auth.controller.ts
      auth.service.ts
      guards/
        jwt.guard.ts
        roles.guard.ts
      decorators/
        public.decorator.ts
        roles.decorator.ts
    contacts/
      contacts.module.ts
      contacts.controller.ts
      contacts.service.ts
      contacts.spec.ts
      dto/
        create-contact.dto.ts
        update-contact.dto.ts
    billing/
      billing.module.ts
      billing.service.ts
      webhook.controller.ts
  shared/
    filters/
      global-exception.filter.ts
    interceptors/
      transform.interceptor.ts
    pipes/
      validation.pipe.ts
  health/
    health.controller.ts
    indicators/
  main.ts
  app.module.ts

كل وحدة مجال مستقلة بذاتها. يحتوي الدليل shared/ على مخاوف شاملة. يتيح لك هذا الفصل إضافة مجالات جديدة دون لمس الكود الموجود.


نمط الديكور @Public()

يطبق NestJS 11 حراس JWT عالميًا، لكنك تحتاج إلى نقاط نهاية محددة مفتوحة - فحوصات السلامة، وعمليات رد اتصال المصادقة، وأجهزة استقبال الخطاف على الويب. يعد نمط الديكور @Public() أفضل بكثير من تعطيل الحراس لكل مسار.

// decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// guards/jwt-auth.guard.ts
import { Injectable, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (isPublic) return true;
    return super.canActivate(context);
  }
}
// app.module.ts
import { APP_GUARD } from '@nestjs/core';

@Module({
  providers: [
    { provide: APP_GUARD, useClass: JwtAuthGuard },
    { provide: APP_GUARD, useClass: RolesGuard },
  ],
})
export class AppModule {}

الآن أي طريقة تحكم مزينة بـ @Public() تتجاوز التحقق من صحة JWT بالكامل. تستخدم وحدات تحكم Webhook ونقاط النهاية الصحية ومسارات المصادقة هذا النمط.


متعدد الإيجار مع معرف المنظمة

القاعدة الأساسية لواجهات برمجة التطبيقات متعددة المستأجرين: يجب تصفية كل استعلام قاعدة بيانات حسب organizationId. البرامج الوسيطة التي تحدد req.organizationId ليست كافية - حيث إن نسيان المطور لتطبيق عامل التصفية يعرض البيانات بين المستأجرين.

يتمثل النمط في استخراج organizationId من حمولة JWT وإرفاقه بواجهة الطلب المكتوبة:

// types/authenticated-request.interface.ts
import { Request } from 'express';

export interface AuthenticatedRequest extends Request {
  user: {
    sub: string;
    email: string;
    name: string;
    role: 'admin' | 'support' | 'user';
    organizationId: string;
  };
}
// contacts/contacts.controller.ts
import { Controller, Get, Post, Body, Req } from '@nestjs/common';
import { AuthenticatedRequest } from '../../types/authenticated-request.interface';

@Controller('contacts')
export class ContactsController {
  constructor(private contactsService: ContactsService) {}

  @Get()
  findAll(@Req() req: AuthenticatedRequest) {
    return this.contactsService.findAll(req.user.organizationId);
  }

  @Post()
  create(@Body() dto: CreateContactDto, @Req() req: AuthenticatedRequest) {
    return this.contactsService.create(dto, req.user.organizationId);
  }
}
// contacts/contacts.service.ts
import { db } from '@ecosire/db';
import { contacts } from '@ecosire/db/schema';
import { eq, and } from 'drizzle-orm';

@Injectable()
export class ContactsService {
  async findAll(organizationId: string) {
    return db
      .select()
      .from(contacts)
      .where(eq(contacts.organizationId, organizationId))
      .limit(100);
  }
}

تفرض طبقة الخدمة organizationId على مستوى الاستعلام. لا يمكن لأي قدر من فشل البرامج الوسيطة أو الديكور المنسي أن يتسبب في تسرب بيانات المستأجرين المتبادلين لأن SQL نفسه يفرض العزل.


مرشح الاستثناء العالمي

تتطلب استجابات الأخطاء المتسقة عبر كافة نقاط النهاية عامل تصفية استثناءات عمومي. يتعامل التسلسل الهرمي HttpException الخاص بـ NestJS 11 مع معظم الحالات، ولكنك تحتاج أيضًا إلى اكتشاف الأخطاء غير المتوقعة وعدم الكشف أبدًا عن آثار المكدس في الإنتاج.

// filters/global-exception.filter.ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(GlobalExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let message = 'Internal server error';
    let errors: Record<string, string[]> | undefined;

    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const exceptionResponse = exception.getResponse();

      if (typeof exceptionResponse === 'string') {
        message = exceptionResponse;
      } else if (typeof exceptionResponse === 'object') {
        const resp = exceptionResponse as Record<string, unknown>;
        message = (resp.message as string) || message;
        if (Array.isArray(resp.message)) {
          // Validation errors from class-validator
          errors = this.formatValidationErrors(resp.message as string[]);
          message = 'Validation failed';
        }
      }
    } else if (exception instanceof Error) {
      this.logger.error(exception.message, exception.stack);
      // Never expose stack traces in production
      if (process.env.NODE_ENV !== 'production') {
        message = exception.message;
      }
    }

    response.status(status).json({
      statusCode: status,
      message,
      errors,
      path: request.url,
      timestamp: new Date().toISOString(),
    });
  }

  private formatValidationErrors(messages: string[]): Record<string, string[]> {
    const errors: Record<string, string[]> = {};
    for (const msg of messages) {
      const [field, ...rest] = msg.split(' ');
      if (!errors[field]) errors[field] = [];
      errors[field].push(rest.join(' '));
    }
    return errors;
  }
}

سجله في main.ts:

// main.ts
import { NestFactory } from '@nestjs/core';
import { GlobalExceptionFilter } from './shared/filters/global-exception.filter';

async function bootstrap() {
  // CRITICAL: Load env vars before NestJS bootstraps
  require('dotenv').config({ path: join(__dirname, '..', '..', '..', '.env.local') });

  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new GlobalExceptionFilter());
  app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
  await app.listen(3001);
}

تغيير حرف البدل لطريق NestJS 11

أحد أهم التغييرات في NestJS 11 هو مطابقة مسار حرف البدل. إذا كنت تقوم بالترحيل من NestJS 10، فسيؤدي ذلك إلى كسر البرامج الوسيطة بصمت:

// NestJS 10 — works
consumer.apply(LoggerMiddleware).forRoutes('*');

// NestJS 11 — use '*path' instead
consumer.apply(LoggerMiddleware).forRoutes('*path');

الأمر نفسه ينطبق على إعداد Swagger وأي أنماط مسار تعتمد على السلسلة. يؤثر هذا التغيير على تسجيل البرامج الوسيطة واستثناءات المسار وأي مكان تستخدم فيه مطابقة المسار بنمط الكرة الأرضية.


التحكم في الوصول على أساس الأدوار

بخلاف مصادقة JWT، تحتاج واجهات برمجة التطبيقات (APIs) الخاصة بالمؤسسة إلى ترخيص يستند إلى الدور. يحافظ نمط الديكور بالإضافة إلى الحماية على نظافة وحدات التحكم:

// decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';

export type Role = 'admin' | 'support' | 'user';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
// guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY, Role } from '../decorators/roles.decorator';
import { AuthenticatedRequest } from '../../types/authenticated-request.interface';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRoles) return true;

    const { user } = context.switchToHttp().getRequest<AuthenticatedRequest>();
    const hasRole = requiredRoles.some((role) => user.role === role);

    if (!hasRole) {
      throw new ForbiddenException('Insufficient permissions');
    }

    return true;
  }
}

الاستخدام في وحدات التحكم:

// For top-level module controllers — one `..`
import { Roles } from '../auth/guards/roles.guard';

// For nested sub-module controllers — two `../..`
import { Roles } from '../../auth/guards/roles.guard';

@Controller('admin/contacts')
@Roles('admin', 'support')
export class AdminContactsController {}

يعد عمق مسار الاستيراد مصدرًا شائعًا للأخطاء. تستخدم وحدات تحكم الوحدة النمطية ذات المستوى الأعلى واحدًا ..؛ تستخدم وحدات التحكم بالوحدة الفرعية المتداخلة اثنين من ../...


نمط وحدة البريد الإلكتروني العالمية

يعمل مصمم الديكور @Global() على حل مشكلة الاستيراد المتكرر للخدمات المستخدمة في كل مكان. البريد الإلكتروني هو حالة الاستخدام الأساسية — فأنت لا تريد استيراد EmailModule في كل وحدة ميزات ترسل إشعارات.

// email/email.module.ts
import { Global, Module } from '@nestjs/common';
import { EmailService } from './email.service';

@Global()
@Module({
  providers: [EmailService],
  exports: [EmailService],
})
export class EmailModule {}

سجله مرة واحدة في AppModule:

@Module({
  imports: [
    EmailModule, // Global — available everywhere
    ContactsModule,
    BillingModule,
    // ...
  ],
})
export class AppModule {}

الآن يمكن لأي خدمة إدخال EmailService دون لمس عمليات استيراد الوحدة النمطية. يعمل نفس النمط مع Redis وEventBus وأي خدمة بنية تحتية شاملة.


الحد من نقاط النهاية العامة

تواجه واجهات برمجة التطبيقات الخاصة بالمؤسسات محاولات إساءة الاستخدام يوميًا. يتكامل @nestjs/throttler الخاص بـ NestJS بشكل نظيف مع نظام الحماية:

// app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot([
      { name: 'short', ttl: 1000, limit: 10 },
      { name: 'medium', ttl: 60000, limit: 100 },
    ]),
  ],
  providers: [
    { provide: APP_GUARD, useClass: ThrottlerGuard },
  ],
})

تجاوز كل نقطة نهاية باستخدام @Throttle() للحصول على حدود أكثر صرامة على المسارات الحساسة:

import { Throttle } from '@nestjs/throttler';

@Controller('auth')
export class AuthController {
  @Post('exchange')
  @Public()
  @Throttle({ short: { ttl: 60000, limit: 5 } }) // 5 per minute
  async exchangeCode(@Body() dto: ExchangeCodeDto) {
    return this.authService.exchangeCode(dto.code);
  }
}

الفحوصات الصحية باستخدام @nestjs/terminus

تحتاج واجهات برمجة تطبيقات الإنتاج إلى نقاط نهاية صحية لفحص موازن التحميل ومراقبته. يوفر @nestjs/terminus نظامًا تعريفيًا يتضمن مؤشرات مدمجة:

// health/health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus';
import { Public } from '../auth/decorators/public.decorator';
import { DatabaseHealthIndicator } from './indicators/database.indicator';
import { RedisHealthIndicator } from './indicators/redis.indicator';

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private db: DatabaseHealthIndicator,
    private redis: RedisHealthIndicator,
  ) {}

  @Get()
  @Public()
  @HealthCheck()
  check() {
    return this.health.check([
      () => this.db.isHealthy('database'),
      () => this.redis.isHealthy('redis'),
    ]);
  }
}

مؤشر مخصص لـ Drizzle ORM (نظرًا لأن المحطة لا تحتوي على مؤشر مدمج):

// health/indicators/database.indicator.ts
import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';
import { db } from '@ecosire/db';
import { sql } from 'drizzle-orm';

@Injectable()
export class DatabaseHealthIndicator extends HealthIndicator {
  async isHealthy(key: string): Promise<HealthIndicatorResult> {
    try {
      await db.execute(sql`SELECT 1`);
      return this.getStatus(key, true);
    } catch (error) {
      throw new HealthCheckError('Database check failed', this.getStatus(key, false));
    }
  }
}

أنماط التحقق من صحة DTO

يقوم ValidationPipe مع whitelist: true بإزالة الخصائص غير المعروفة قبل أن تصل إلى طبقة الخدمة الخاصة بك. بالدمج مع class-transformer، تصبح DTOs خط دفاعك الأول:

// contacts/dto/create-contact.dto.ts
import { IsString, IsEmail, IsOptional, IsEnum, MinLength, MaxLength } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer';

export enum ContactType {
  INDIVIDUAL = 'individual',
  COMPANY = 'company',
}

export class CreateContactDto {
  @ApiProperty({ example: 'Acme Corp' })
  @IsString()
  @MinLength(2)
  @MaxLength(255)
  @Transform(({ value }) => value?.trim())
  name: string;

  @ApiProperty({ example: '[email protected]' })
  @IsEmail()
  @Transform(({ value }) => value?.toLowerCase().trim())
  email: string;

  @ApiPropertyOptional({ enum: ContactType })
  @IsOptional()
  @IsEnum(ContactType)
  type?: ContactType;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(500)
  notes?: string;
}

تقوم أدوات الديكور @Transform بتسوية البيانات في طبقة التحقق من الصحة. يؤدي قطع المسافات البيضاء وأحرف البريد الإلكتروني الصغيرة إلى منع السجلات المكررة من اختلافات حالة الأحرف.


التكامل التباهي

تحتاج واجهات برمجة تطبيقات المؤسسة إلى وثائق. تقوم وحدة Swagger الخاصة بـ NestJS بإنشاء مواصفات OpenAPI من أدوات الديكور، ولكن يجب أن يكون الإعداد متعمدًا:

// main.ts
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('ECOSIRE API')
    .setDescription('Enterprise API for ECOSIRE platform')
    .setVersion('1.0')
    .addBearerAuth()
    .addTag('auth', 'Authentication endpoints')
    .addTag('contacts', 'Contact management')
    .build();

  const document = SwaggerModule.createDocument(app, config);

  // Only expose in non-production environments
  if (process.env.NODE_ENV !== 'production') {
    SwaggerModule.setup('api/docs', app, document);
  }

  await app.listen(3001);
}

تحتاج كل طريقة تحكم إلى @ApiOperation و@ApiResponse:

@Get(':id')
@ApiOperation({ summary: 'Get contact by ID' })
@ApiResponse({ status: 200, description: 'Contact found', type: ContactResponseDto })
@ApiResponse({ status: 404, description: 'Contact not found' })
async findOne(@Param('id') id: string, @Req() req: AuthenticatedRequest) {
  return this.contactsService.findOne(id, req.user.organizationId);
}

المخاطر والحلول الشائعة

المأزق 1: التبعيات الدائرية بين الوحدات

تؤدي الواردات الدائرية إلى تعطل NestJS عند بدء التشغيل بسبب خطأ خفي. الإصلاح هو forwardRef():

@Module({
  imports: [forwardRef(() => BillingModule)],
})
export class LicenseModule {}

الحل الأفضل: استخراج المنطق المشترك إلى وحدة نمطية ثالثة يتم استيرادها بدون تبعية دائرية.

المأزق 2: متغيرات Env غير متاحة أثناء تهيئة الوحدة

إذا قمت بالوصول إلى process.env.DATABASE_URL في مُنشئ فئة أو مصنع موفر قبل تحميل dotenv، فستحصل على undefined. الحل: قم بتحميل dotenv في أعلى main.ts، قبل أي استيراد لـ NestJS.

// main.ts — must be FIRST
import * as path from 'path';
require('dotenv').config({ path: path.join(__dirname, '..', '..', '..', '.env.local') });

// Then NestJS imports
import { NestFactory } from '@nestjs/core';

المأزق 3: الانتظار المفقود للعمليات غير المتزامنة في خطافات دورة الحياة

// Wrong — database connection might not be ready
@Injectable()
export class AppService implements OnModuleInit {
  onModuleInit() {
    this.seedDatabase(); // Not awaited!
  }
}

// Correct
async onModuleInit() {
  await this.seedDatabase();
}

المأزق 4: استخدام sql.raw() في استعلامات Drizzle

sql.raw() يتجاوز المعلمات ويفتح متجهات حقن SQL. استخدم دائمًا قالب sql الحرفي:

// Dangerous — never do this
const result = await db.execute(sql.raw(`SELECT * FROM contacts WHERE id = '${id}'`));

// Safe — parameterized
const result = await db.execute(sql`SELECT * FROM contacts WHERE id = ${id}`);

الأسئلة المتداولة

كيف أتعامل مع الإصدار في NestJS 11 APIs؟

يدعم NestJS 11 إصدار URI، وإصدار الرأس، وإصدار نوع الوسائط خارج الصندوق. قم بتمكينه في main.ts مع app.enableVersioning({ type: VersioningType.URI })، ثم قم بتزيين وحدات التحكم بـ @Version('1'). بالنسبة لواجهات برمجة تطبيقات المؤسسات، يعد إصدار URI (/v1/contacts) هو الأسلوب الأكثر وضوحًا وسهولة في التخزين المؤقت.

ما هي أفضل طريقة للتعامل مع تحميل الملفات في NestJS؟

استخدم @nestjs/platform-express مع multer لتحميل الملفات. بالنسبة لعمليات التحميل في S3، قم بتكوين محرك تخزين مخصص عبر multer-s3. تحقق دائمًا من صحة أنواع الملفات وأحجامها على مستوى توجيه الإخراج قبل تشغيل المعالج، ولا تثق أبدًا في أنواع MIME المقدمة من العميل — تحقق من صحة البايتات السحرية بدلاً من ذلك.

كيف يمكنني تنظيم معاملات قاعدة البيانات في NestJS؟

يدعم Drizzle ORM المعاملات باستخدام db.transaction(async (tx) => { ... }). قم بتمرير كائن المعاملة إلى أساليب الخدمة بدلاً من مثيل db العمومي. بالنسبة لمنطق الأعمال متعدد العمليات (إنشاء طلب + خصم المخزون + إرسال بريد إلكتروني)، قم بتغليف كل شيء في معاملة وجعل إرسال البريد الإلكتروني غير محظور باستخدام .catch() بعد الالتزام.

متى يجب علي استخدام الحراس مقابل البرامج الوسيطة مقابل المعترضات؟

يتعامل الحراس مع التفويض (هل يمكن لهذا المستخدم الوصول إلى هذا المورد؟). تتعامل البرامج الوسيطة مع تحويل الطلبات الشاملة (التسجيل، ومعرفات الارتباط، والتحليل). تقوم المعترضات بتغليف دورة الطلب والاستجابة للتحويل والتخزين المؤقت والمقاييس. أمر التنفيذ هو: البرامج الوسيطة → الحراس → المعترضون (قبل) → الأنابيب → المعالج → المعترضات (بعد) → مرشحات الاستثناء.

كيف يمكنني اختبار وحدات NestJS بشكل منفصل؟

استخدم Test.createTestingModule() لإنشاء بيئة اختبار مع تبعيات مستهزئة. سخر من أساليب الخدمة الخاصة بك باستخدام jest.fn() أو vi.fn()، واختبر سلوك وحدة التحكم بشكل مستقل عن قاعدة البيانات الخاصة بك. بالنسبة لاختبارات التكامل، استخدم @nestjs/testing مع اتصال قاعدة بيانات حقيقي (قاعدة بيانات اختبارية منفصلة) واستعادة المعاملات بعد كل اختبار.

ما هو تأثير أداء الحراس العالميين؟

يعمل الحراس العالميون على تلبية كل طلب، لذا ابقِهم سريعين. عادةً ما يكون التحقق من JWT من 1 إلى 5 مللي ثانية. تجنب عمليات البحث في قاعدة البيانات في الحراس - قم بتحميل الأذونات أثناء إنشاء الرمز المميز وقم بتضمينها في حمولة JWT. إذا كنت بحاجة إلى أذونات جديدة لكل طلب، فاستخدم Redis مع TTL قصير بدلاً من الوصول إلى قاعدة البيانات.


الخطوات التالية

يتطلب إنشاء واجهات برمجة تطبيقات المؤسسة على نطاق واسع البنية الصحيحة من اليوم الأول. قام الفريق الهندسي في ECOSIRE ببناء وتشغيل واجهة خلفية NestJS 11 مكونة من 56 وحدة للتعامل مع سير العمل المعقد متعدد المستأجرين، وفواتير الشريط، وإدارة التراخيص، والتحليلات التي تدعم الذكاء الاصطناعي.

سواء كنت بحاجة إلى واجهة برمجة تطبيقات NestJS مخصصة، أو تكامل Odoo ERP، أو نظام أساسي مؤسسي متكامل، فإن فريقنا يقدم أنماطًا أثبتت كفاءتها في الإنتاج لمشروعك. استكشف خدمات التطوير لدينا لمعرفة كيف يمكننا تسريع الإصدار التالي.

مشاركة:
E

بقلم

ECOSIRE Research and Development Team

بناء منتجات رقمية بمستوى المؤسسات في ECOSIRE. مشاركة رؤى حول تكاملات Odoo وأتمتة التجارة الإلكترونية وحلول الأعمال المدعومة بالذكاء الاصطناعي.

الدردشة على الواتساب