فلٹر سے بیک اینڈ تک: ڈارٹ اور سرور پوڈ کا استعمال کرتے ہوئے پروڈکشن گریڈ REST APIs کیسے بنائیں

Serverpod ڈارٹ پر بنائے گئے سب سے زیادہ پرفارمنس بیک اینڈ فریم ورک میں سے ایک ہے۔ یہ مکمل طور پر ملکیتی بیک اینڈ فریم ورک ہے جو اپنے ORM، اپنے کوڈ جنریشن سسٹم، مائیگریشن ٹولز، تصدیقی ماڈیولز اور تعیناتی پلیٹ فارم کے ساتھ آتا ہے۔

اگر آپ اپنا API بنانے کے لیے شیلف جیسے ٹول کا استعمال کرتے ہیں، تو آپ سب کچھ خود اکٹھا کرتے ہیں۔ ایک پیکیج کا انتخاب کریں، اپنا مڈل ویئر لکھیں، ڈیٹا بیس کنکشن کا نظم کریں، اور تمام ٹکڑوں کو دستی طور پر جوڑیں۔ یہ شیلف طریقہ ہے، اور یہ آپ کو بالکل بتاتا ہے کہ سرور سائیڈ ڈارٹ اندرونی طور پر کیسے کام کرتا ہے۔

سرور پوڈ ایک بالکل مختلف فلسفہ ہے۔

جبکہ شیلف بنیادی عناصر فراہم کرتا ہے، سرور پوڈ ایک مکمل نظام فراہم کرتا ہے۔ YAML میں اپنے ماڈل کی وضاحت کریں، جنریٹر چلائیں، اور مکمل طور پر ٹائپ شدہ ڈیٹا بیس کلاسز، سیریلائزیشن، اور کلائنٹ سائڈ کوڈ خود بخود تیار ہو جاتے ہیں۔

فلٹر انجینئرز کے لیے، یہ فوری طور پر مانوس محسوس ہوگا۔ یہ اسی قسم کی پیداواری صلاحیت ہے جو آپ فلٹر ٹول چین سے حاصل کر سکتے ہیں، جو بیک اینڈ پر لاگو ہوتی ہے۔

اس مضمون میں، ہم سرور پوڈ کا استعمال کرتے ہوئے شروع سے صارف اور پروفائل مینجمنٹ REST API بنائیں گے۔ آپ سیکھیں گے کہ سرور پوڈ کا کوڈ جنریشن، بلٹ ان ORM، اور اینڈ پوائنٹ سسٹم کیسے کام کرتا ہے، اور آخر تک آپ کے پاس مکمل طور پر تعینات بیک اینڈ ہوگا۔

انڈیکس

شرطیں

شروع کرنے سے پہلے، آپ کے پاس ہونا ضروری ہے:

  • ڈارٹ اور فلٹر کی ترقی کا علم

  • REST API کے تصورات، اختتامی نکات، HTTP طریقوں اور اسٹیٹس کوڈز کو سمجھنا۔

  • ڈوکر ڈیسک ٹاپ انسٹال اور چل رہا ہے۔

  • فلٹر SDK انسٹال ہے (سرور پوڈ کو صرف سرور کے پروجیکٹس کے لیے بھی اس کی ضرورت ہوتی ہے)

  • تعیناتی کے لیے Fly.io اکاؤنٹ یا Serverpod Cloud اکاؤنٹ

سرور پوڈ اور شیلف کے درمیان فرق

کوڈ کی ایک لائن لکھنے سے پہلے، شیلف اور سرور پوڈ کے درمیان بنیادی فلسفیانہ فرق کو سمجھنا اچھا خیال ہے۔ اس سے فریم ورک میں ہر ڈیزائن کا فیصلہ من مانی کے بجائے جان بوجھ کر محسوس ہوگا۔

شیلف آپ کو کچھ بھی لکھنے دیتا ہے۔ تجزیہ، رسپانس فارمیٹنگ، ڈیٹا بیس استفسار، منتقلی، تصدیق، اور لاگنگ کی درخواست کریں۔ ہر حصہ واضح کوڈ ہے جسے آپ سمجھ سکتے ہیں کیونکہ آپ نے اسے لکھا ہے۔

سرور پوڈ کے ساتھ، آپ چیزوں کی وضاحت کرتے ہیں اور فریم ورک کوڈ لکھتا ہے۔ YAML میں اپنے ماڈل کی وضاحت کریں، سرور پوڈ جنریٹ چلائیں، اور خودکار طور پر مکمل ڈارٹ کلاس درآمد کریں، بشمول ڈیٹا بیس بائنڈنگ، سیریلائزیشن، اور کلائنٹ سائڈ رسائی۔ آپ اختتامی نقطہ کے طریقہ کار کی وضاحت کرتے ہیں، اور فریم ورک روٹنگ، پیرامیٹر نکالنے، اور رسپانس فارمیٹنگ کا خیال رکھتا ہے۔

یہ وہی سمجھوتہ ہے جو فلٹر مقامی پلیٹ فارم APIs کا استعمال کرکے کرتا ہے۔ فلٹر ایک لے آؤٹ انجن، رینڈرنگ پائپ لائن، اور اشارہ کا نظام لکھتا ہے۔ مصنوعات کی منطق پر توجہ دیں۔ سرور پوڈ بیک اینڈ پر ایک ہی شرط لگاتا ہے۔

اس پیداواری صلاحیت کی قیمت لچک ہے۔ سرور پوڈ کی اس بارے میں مضبوط رائے ہے کہ چیزوں کو کس طرح تشکیل دیا جانا چاہئے۔ اگر آپ کے استعمال کا معاملہ ان تبصروں کو پورا کرتا ہے، تو ترقی بہت تیز ہوگی۔ دوسری صورت میں، آپ ایک فریم ورک کے خلاف کام کر رہے ہیں.

صارف اور پروفائل مینجمنٹ API کے لیے جو ہم یہاں بنا رہے ہیں، Serverpod ایک بہترین فٹ ہے۔

سرور پوڈ کی تنصیب

سرور پوڈ کو صرف سرور کے کاموں کے لیے بھی فلٹر انسٹال کرنے کی ضرورت ہے۔ اس کی وجہ یہ ہے کہ ٹول چین پروجیکٹ کی تخلیق کے دوران سرور پیکجوں کے ساتھ کلائنٹ پیکجز بناتا ہے۔

سرور پوڈ CLI کو عالمی سطح پر انسٹال کریں۔

dart pub global activate serverpod_cli

تنصیب کی تصدیق کریں۔

serverpod
# Should print the Serverpod CLI help

جاری رکھنے سے پہلے یقینی بنائیں کہ ڈوکر ڈیسک ٹاپ چل رہا ہے۔ Serverpod مقامی ترقی کے لیے PostgreSQL اور Redis کو منظم کرنے کے لیے Docker کا استعمال کرتا ہے۔

ایک پروجیکٹ بنائیں

serverpod create user_profile_api
cd user_profile_api

یہ واحد کمانڈ تین ڈارٹ پیکجز بناتی ہے۔

user_profile_api/
  user_profile_api_server/    ← your server code
  user_profile_api_client/    ← auto-generated client (do not edit)
  user_profile_api_flutter/   ← Flutter app pre-configured to connect

اس مضمون میں ہم جو کچھ بھی لکھتے ہیں وہ user_profile_api_server میں ہے۔ کلائنٹ اور فلٹر پیکجز خود بخود بن جاتے ہیں اور استعمال ہوتے ہیں جب فلٹر فرنٹ اینڈ سرور پوڈ بیک اینڈ کے ساتھ بات چیت کرنا چاہتا ہے۔

پروجیکٹ کی ساخت کو سمجھیں۔

user_profile_api_server کے اندر:

user_profile_api_server/
  bin/
    main.dart                  ← entry point
  lib/
    src/
      endpoints/               ← your endpoint classes live here
      generated/               ← auto-generated code (never edit manually)
    user_profile_api_server.dart
  config/
    development.yaml           ← database and server config
    staging.yaml
    production.yaml
    passwords.yaml             ← database passwords
  migrations/                  ← auto-generated migration files
  web/                         ← optional web server files
  Dockerfile
  docker-compose.yaml
  pubspec.yaml

اس ڈھانچے کے بارے میں سمجھنے کے لئے سب سے اہم چیز generate/folder ہے۔ وہاں موجود ہر چیز سرور پوڈ جنریٹ کے ذریعہ تیار کی جاتی ہے اور اسے دستی طور پر ایڈٹ نہیں کیا جانا چاہئے۔ جب آپ جنریٹر چلاتے ہیں جب آپ ماڈل یا اینڈ پوائنٹ کو تبدیل کرتے ہیں تو جنریٹر اس فولڈر کو مکمل طور پر دوبارہ بناتا ہے۔

کنفیگریشن/فولڈر میں ماحول کے لیے مخصوص ترتیب ہوتی ہے۔ develop.yaml فائل کو سرور پوڈ کے لیے پہلے سے ترتیب دیا گیا ہے تاکہ مقامی طور پر اسپن ڈوکر کنٹینر کے ساتھ کام کیا جا سکے۔

سرور پوڈ کے بنیادی تصورات

اختتامی نقطہ اور سیشن آبجیکٹ

سرور پوڈ میں، ایک اختتامی نقطہ ایک کلاس ہے جو اختتامی نقطہ کو بڑھاتا ہے۔ اس کلاس کے تمام عوامی طریقے API کالز بن جاتے ہیں جو کلائنٹ کر سکتے ہیں۔ کوئی روٹنگ کنفیگریشن، ہینڈلر رجسٹریشن، یا مڈل ویئر ماؤنٹنگ نہیں ہے۔ کوڈ جنریشن کے دوران فریم ورک خود بخود اینڈ پوائنٹس کو دریافت اور رجسٹر کرتا ہے۔

import 'package:serverpod/serverpod.dart';

class UserEndpoint extends Endpoint {
  Future greet(Session session, String name) async {
    return 'Hello, $name!';
  }
}

Serverpod میں سیشن آبجیکٹ سب سے اہم پیرامیٹر ہے۔ یہ تمام اختتامی طریقوں کو منتقل کیا جاتا ہے اور اس تک رسائی فراہم کرتا ہے:

  • ڈیٹا بیس آپریشنز کے لیے session.db

  • تصدیق کی معلومات کے لیے session.auth

  • سٹرکچرڈ لاگنگ کے لیے session.log

  • کیشنگ کے لیے session.caches

  • ریئل ٹائم میسجنگ کے لیے session.messages

Flutter’s BuildContext کے Serverpod کے مساوی کے طور پر سیشن کے بارے میں سوچیں۔ یہ ہر چیز کا گیٹ وے ہے جو فریم ورک فراہم کرتا ہے اور ہمیشہ پہلا پیرامیٹر ہوتا ہے۔

ماڈل فائل اور کوڈ جنریشن

یہ وہ جگہ ہے جہاں سرور پوڈ کا نقطہ نظر شیلف سے سب سے زیادہ مختلف ہے۔ ڈارٹ ماڈل کلاسز کو دستی طور پر لکھنے کے بجائے، .spy.yaml فائل میں ڈیٹا ڈھانچے کی وضاحت کریں اور Serverpod کو Dart کلاسز بنانے دیں۔

کمپنی کی ماڈل فائلیں درج ذیل ہیں:

class: Company
table: company
fields:
  name: String
  foundedDate: DateTime?

سرور پوڈ جنریٹ کو چلانے سے ایک مکمل ڈارٹ کلاس تیار ہوگی جس میں:

  • صحیح قسم کے ناقابل تغیر فیلڈز

  • toJson اور fromJson سیریلائزیشن کے لیے

  • ڈی بی سٹیٹک ایکسر کے ذریعے ڈیٹا بیس بائنڈنگ

  • کنسٹرکٹر اور کاپی کے ساتھ طریقہ

  • اسی کلاس کو کلائنٹ پیکج میں بنایا گیا ہے تاکہ آپ اسے براہ راست اپنی Flutter ایپ میں استعمال کر سکیں۔

یہ ایک اہم پیداواری فروغ ہے۔ YAML میں ایک بار اپنی شکل کی وضاحت کریں اور آپ کو ایک مستقل، ٹائپ شدہ ماڈل ملتا ہے جو سرورز، ڈیٹا بیسز اور کلائنٹس میں بغیر نقل کے کام کرتا ہے۔

بلٹ ان ORM

Serverpod کا ORM براہ راست تیار کردہ ماڈل کلاسز کا استعمال کرتا ہے۔ تمام ڈیٹا بیس آپریشنز ماڈل کے سٹیٹک ڈی بی ایکسسرز کے ذریعے انجام پاتے ہیں۔

// Insert a row
var company = Company(name: 'Serverpod Corp', foundedDate: DateTime.now());
company = await Company.db.insertRow(session, company);

// Find by ID
var found = await Company.db.findById(session, company.id!);

// Find with condition
var result = await Company.db.findFirstRow(
  session,
  where: 
);

// Find all with ordering
var all = await Company.db.find(
  session,
  orderBy: 
);

// Update
company = company.copyWith(name: 'New Name');
await Company.db.updateRow(session, company);

// Delete
await Company.db.deleteRow(session, company);

جہاں پیرامیٹر ٹائپ سیف ایکسپریشن بلڈر استعمال کرتا ہے۔ t پیرامیٹر ٹیبل کالموں تک ٹائپ شدہ رسائی فراہم کرتا ہے، جس سے آپ کو خودکار تکمیل اور استفسار کے حالات کی مرتب وقت کی جانچ پڑتال کی اجازت ملتی ہے۔ کوئی خام SQL، کوئی سٹرنگ پر مبنی کالم کے نام نہیں، اور رن ٹائم پر کوئی حیرت نہیں۔

نقل مکانی

جب آپ ماڈل کو تبدیل کرتے ہیں، تو Serverpod خود بخود ہجرت کرتا ہے۔

serverpod create-migration

یہ migrations/ ڈائریکٹری میں ایس کیو ایل مائیگریشن فائل بنائے گا۔ جب آپ سرور شروع کرتے ہیں تو اسے لاگو کریں۔

dart bin/main.dart --apply-migrations

Serverpod لاگو شدہ منتقلی پر نظر رکھتا ہے اور صرف نئی منتقلی چلاتا ہے۔ نقل مکانی کا نظام مکمل طور پر ماڈل سسٹم کے ساتھ مربوط ہے، لہذا آپ کی ڈارٹ کلاسز اور آپ کے ڈیٹا بیس اسکیما کے درمیان کوئی فرق نہیں ہے۔

ڈویلپمنٹ سرور شروع کریں۔

کوئی بھی کوڈ لکھنے سے پہلے اپنے ترقیاتی ماحول کو چلائیں۔

ڈاکر کنٹینرز شروع کریں (PostgreSQL اور Redis)۔

cd user_profile_api_server
docker compose up --build --detach

وہ سرور شروع کریں جس پر منتقلی کا اطلاق کیا گیا ہے۔

dart bin/main.dart --apply-migrations

آپ کو درج ذیل کو چیک کرنا چاہئے:

SERVERPOD version: 2.x.x, mode: development
Insights listening on port 8081
Server default listening on port 8080
Webserver listening on port 8082

3 پورٹس: پورٹ 8080 ڈیفالٹ API سرور ہے۔ پورٹ 8081 نگرانی کے لیے Serverpod Insights ٹول ہے۔ پورٹ 8082 ایک اختیاری ویب سرور ہے۔ یہ مضمون صرف پورٹ 8080 کے ساتھ کام کرتا ہے۔

ماڈل کی تعریف

صارف ماڈل

اپنے سرور پیکج میں lib/src/models/user.spy.yaml بنائیں۔

class: AppUser
table: app_users
fields:
  email: String
  passwordHash: String
  firstName: String
  lastName: String
  isActive: bool, default=true
indexes:
  app_users_email_idx:
    fields: email
    unique: true

یہاں نوٹ کرنے کے لیے چند چیزیں ہیں: توثیق کے ماڈیول میں سرور پوڈ کے اندرونی صارف طبقے کے ساتھ تنازعہ سے بچنے کے لیے، کلاس کا نام User کے بجائے AppUser رکھا گیا ہے۔ ٹیبل کی کلید PostgreSQL ٹیبل کے نام کی وضاحت کرتی ہے۔ انڈیکس بلاک ای میل کالم پر ایک منفرد انڈیکس بنا کر ڈیٹا بیس کی سطح پر انفرادیت کو نافذ کرتا ہے۔

Serverpod int ہے؟ خودکار طور پر قسم کا ایک id فیلڈ شامل کرتا ہے جو ٹیبل کیز والے تمام ماڈلز پر لاگو ہوتا ہے۔ آپ خود اس کا اعلان نہیں کرتے۔

پروفائل ماڈل

lib/src/models/profile.spy.yaml بنائیں:

class: Profile
table: profiles
fields:
  userId: int
  bio: String?
  avatarUrl: String?
  phone: String?
  location: String?
  website: String?
indexes:
  profiles_user_id_idx:
    fields: userId
    unique: true

userId ایک int ہے جو AppUser کی ID کا حوالہ دیتا ہے۔ Serverpod کے ماڈل سسٹم میں ابھی تک YAML میں غیر ملکی کلیدی اعلانیہ نحو موجود نہیں ہے، لہذا حوالہ جاتی سالمیت کو اینڈ پوائنٹ منطق میں ایپلیکیشن لیئر پر ہینڈل کیا جاتا ہے۔

کوڈ جنریشن چلائیں۔

ایک بار جب دونوں ماڈل فائلیں تیار ہوجائیں، جنریٹر چلائیں۔

serverpod generate

یہ lib/src/generated/ میں ڈارٹ کلاسز تیار کرے گا۔ AppUser کے لیے آپ کو ملتا ہے:

// This is auto-generated, never edit directly
class AppUser extends SerializableEntity {
  AppUser({
    this.id,
    required this.email,
    required this.passwordHash,
    required this.firstName,
    required this.lastName,
    this.isActive = true,
  });

  int? id;
  String email;
  String passwordHash;
  String firstName;
  String lastName;
  bool isActive;

  // db accessor for ORM operations
  static final db = AppUserRepository._();

  // Serialization methods
  factory AppUser.fromJson(Map jsonSerialization, ...) { ... }
  Map toJson() { ... }
}

تیار کردہ کوڈ وہی ہے جس کے ساتھ اختتامی نقطہ تعامل کرتا ہے۔ کبھی ہاتھ سے نہ لکھیں۔

ہجرتیں بنائیں اور لاگو کریں۔

ماڈل بننے کے بعد، ایک منتقلی بنائیں.

serverpod create-migration

یہ migrations/ میں ٹائم اسٹیمپڈ SQL فائل بنائے گا۔ درخواست دیں:

dart bin/main.dart --apply-migrations

app_users اور پروفائل ٹیبل اب PostgreSQL میں درست کالموں اور اشاریہ جات کے ساتھ موجود ہیں۔

ایک API بنانا

توثیق کا اختتامی نقطہ

lib/src/endpoints/auth_endpoint.dart بنائیں:

import 'package:serverpod/serverpod.dart';
import 'package:bcrypt/bcrypt.dart';
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import '../generated/protocol.dart';

class AuthEndpoint extends Endpoint {
  Future> register(
    Session session,
    String email,
    String password,
    String firstName,
    String lastName,
  ) async {
    if (email.isEmpty || password.isEmpty || firstName.isEmpty || lastName.isEmpty) {
      throw Exception('All fields are required');
    }

    if (password.length < 8) {
      throw Exception('Password must be at least 8 characters');
    }

    // Check for existing user
    final existing = await AppUser.db.findFirstRow(
      session,
      where: 
    );

    if (existing != null) {
      throw Exception('An account with this email already exists');
    }

    final passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());

    var user = AppUser(
      email: email,
      passwordHash: passwordHash,
      firstName: firstName,
      lastName: lastName,
    );

    user = await AppUser.db.insertRow(session, user);

    final token = _generateToken(user);

    return {
      'user': _sanitizeUser(user),
      'token': token,
    };
  }

  Future> login(
    Session session,
    String email,
    String password,
  ) async {
    if (email.isEmpty || password.isEmpty) {
      throw Exception('Email and password are required');
    }

    final user = await AppUser.db.findFirstRow(
      session,
      where: 
    );

    if (user == null || !BCrypt.checkpw(password, user.passwordHash)) {
      throw Exception('Invalid email or password');
    }

    if (!user.isActive) {
      throw Exception('This account has been deactivated');
    }

    final token = _generateToken(user);

    return {
      'user': _sanitizeUser(user),
      'token': token,
    };
  }

  String _generateToken(AppUser user) {
    final jwt = JWT({'sub': user.id, 'email': user.email});
    return jwt.sign(SecretKey(_jwtSecret), expiresIn: const Duration(hours: 24));
  }

  // Never return the password hash to the client
  Map _sanitizeUser(AppUser user) => {
        'id': user.id,
        'email': user.email,
        'firstName': user.firstName,
        'lastName': user.lastName,
        'isActive': user.isActive,
      };

  // Read from Serverpod's config system
  String get _jwtSecret =>
      Session.serverpod.getPassword('jwtSecret') ?? 'fallback_dev_secret';
}

Serverpod اختتامی نقطہ درج کردہ قدر واپس کرتا ہے۔ نقشہ واپس کرتے وقتسرور پوڈ اسے خود بخود سیریلائز کرتا ہے۔ جب کوئی استثناء ہوتا ہے، سرور پوڈ اسے پکڑتا ہے اور کلائنٹ کو ایک ساختی غلطی کا جواب دیتا ہے۔ عام کیسز کے لیے کوئی دستی جوابی فارمیٹ اور اسٹیٹس کوڈ کا کوئی انتظام نہیں ہے۔

صارف کا اختتامی نقطہ

lib/src/endpoints/user_endpoint.dart بنائیں:

import 'package:serverpod/serverpod.dart';
import '../generated/protocol.dart';

class UserEndpoint extends Endpoint {
  @override
  bool get requireLogin => true;

  Future>> getAll(Session session) async {
    final users = await AppUser.db.find(
      session,
      where: 
      orderBy: 
    );

    return users.map(_sanitizeUser).toList();
  }

  Future> getById(Session session, int userId) async {
    final user = await AppUser.db.findById(session, userId);

    if (user == null || !user.isActive) {
      throw Exception('User not found');
    }

    return _sanitizeUser(user);
  }

  Future> update(
    Session session,
    int userId,
    String? firstName,
    String? lastName,
  ) async {
    final user = await AppUser.db.findById(session, userId);

    if (user == null || !user.isActive) {
      throw Exception('User not found');
    }

    final updated = user.copyWith(
      firstName: firstName ?? user.firstName,
      lastName: lastName ?? user.lastName,
    );

    await AppUser.db.updateRow(session, updated);
    return _sanitizeUser(updated);
  }

  Future delete(Session session, int userId) async {
    final user = await AppUser.db.findById(session, userId);

    if (user == null || !user.isActive) {
      throw Exception('User not found');
    }

    // Soft delete
    final deactivated = user.copyWith(isActive: false);
    await AppUser.db.updateRow(session, deactivated);
  }

  Map _sanitizeUser(AppUser user) => {
        'id': user.id,
        'email': user.email,
        'firstName': user.firstName,
        'lastName': user.lastName,
        'isActive': user.isActive,
      };
}

@override bool کی ضرورت لاگ ان => سچ ہے۔ اختتامی نقطوں کی حفاظت کے لیے یہ سرور پوڈ کا بلٹ ان میکانزم ہے۔ اگر یہ حاصل کرنے والا درست ہو جاتا ہے، تو Serverpod طریقہ کو بلائے جانے سے پہلے اس اختتامی نقطہ پر ہر درخواست پر تصدیقی ٹوکن کی توثیق کرے گا۔ غیر تصدیق شدہ درخواستیں خود بخود فریم ورک کے ذریعہ مسترد کردی جاتی ہیں۔

پروفائل اختتامی نقطہ

lib/src/endpoints/profile_endpoint.dart بنائیں:

import 'package:serverpod/serverpod.dart';
import '../generated/protocol.dart';

class ProfileEndpoint extends Endpoint {
  @override
  bool get requireLogin => true;

  Future> getByUserId(
    Session session,
    int userId,
  ) async {
    final user = await AppUser.db.findById(session, userId);
    if (user == null || !user.isActive) {
      throw Exception('User not found');
    }

    final profile = await Profile.db.findFirstRow(
      session,
      where: 
    );

    if (profile == null) {
      throw Exception('Profile not found');
    }

    return _profileToMap(profile);
  }

  Future> create(
    Session session,
    int userId,
    String? bio,
    String? avatarUrl,
    String? phone,
    String? location,
    String? website,
  ) async {
    final user = await AppUser.db.findById(session, userId);
    if (user == null || !user.isActive) {
      throw Exception('User not found');
    }

    final existing = await Profile.db.findFirstRow(
      session,
      where: 
    );

    if (existing != null) {
      throw Exception('Profile already exists for this user');
    }

    var profile = Profile(
      userId: userId,
      bio: bio,
      avatarUrl: avatarUrl,
      phone: phone,
      location: location,
      website: website,
    );

    profile = await Profile.db.insertRow(session, profile);
    return _profileToMap(profile);
  }

  Future> update(
    Session session,
    int userId,
    String? bio,
    String? avatarUrl,
    String? phone,
    String? location,
    String? website,
  ) async {
    final profile = await Profile.db.findFirstRow(
      session,
      where: 
    );

    if (profile == null) {
      throw Exception('Profile not found');
    }

    final updated = profile.copyWith(
      bio: bio ?? profile.bio,
      avatarUrl: avatarUrl ?? profile.avatarUrl,
      phone: phone ?? profile.phone,
      location: location ?? profile.location,
      website: website ?? profile.website,
    );

    await Profile.db.updateRow(session, updated);
    return _profileToMap(updated);
  }

  Map _profileToMap(Profile profile) => {
        'id': profile.id,
        'userId': profile.userId,
        'bio': profile.bio,
        'avatarUrl': profile.avatarUrl,
        'phone': profile.phone,
        'location': profile.location,
        'website': profile.website,
      };
}

ان اختتامی پوائنٹس کو شامل کرنے کے بعد، جنریٹر کو دوبارہ چلائیں تاکہ سرور پوڈ کو اختتامی پوائنٹس کو رجسٹر کرایا جائے۔

serverpod generate

ثبوت

پاس ورڈ ہیشنگ اور JWT

اپنے سرور پیکج میں pubspec.yaml میں مطلوبہ پیکجز شامل کریں۔

dependencies:
  serverpod: ^2.5.0
  bcrypt: ^1.1.3
  dart_jsonwebtoken: ^2.12.0

پھر چلائیں ڈارٹ پب حاصل کریں۔

تصدیق کے اختتامی نقطہ پر _generateToken اور _sanitizeUser مددگار پاس ورڈ ہیشنگ اور JWT جنریشن کو سنبھالتے ہیں۔ JWT راز کو Serverpod کے بلٹ ان پاس ورڈ مینجمنٹ سسٹم سے Session.serverpod.getPassword(‘jwtSecret’) کے ذریعے پڑھا جاتا ہے۔

config/passwords.yaml میں راز شامل کریں۔

development:
  database: 'dart_password'
  jwtSecret: 'your_development_jwt_secret_here'

یہ فائل آپ کے Serverpod پروجیکٹ میں پہلے سے ہی .gitignore میں موجود ہے۔ سرور پوڈ کلاؤڈ میں ماحولیاتی متغیرات یا رازوں کے انتظام کے ذریعے پیداوار کے راز لگائے جاتے ہیں۔

اختتامی نقطہ تحفظ

سرور پوڈ میں اختتامی نقطہ تحفظ کی دو سطحیں ہیں:

needLogin – خودکار طور پر غیر تصدیق شدہ درخواستوں کو مسترد کریں۔

@override
bool get requireLogin => true;

مطلوبہ دائرہ کار – ایک مخصوص استحقاق کا دائرہ درکار ہے۔

@override
Set get requiredScopes => {Scope.admin};

اس مضمون میں صارف اور پروفائل کے اختتامی نکات کے لیے لاگ ان کی ضرورت کافی ہے۔ لاگ ان کے جواب سے ٹوکن بعد میں آنے والی تمام درخواستوں کے آتھرائزیشن ہیڈر میں پاس کیا جاتا ہے، اور سرور پوڈ اینڈ پوائنٹ کے طریقہ کو بلائے جانے سے پہلے اس کی تصدیق کرتا ہے۔

موجودہ صارف کی ID حاصل کرنے کے لیے اینڈ پوائنٹ کے اندر موجود ٹوکن کو چیک کریں۔

Future someProtectedMethod(Session session) async {
  final authInfo = await session.authenticated;

  if (authInfo == null) {
    throw Exception('Not authenticated');
  }

  final userId = authInfo.userId;
  // proceed with userId
}

سرور پوڈ میں ہینڈلنگ میں خرابی۔

Serverpod اختتامی طریقوں کے ذریعہ اٹھائے گئے استثناء کو ہینڈل کرتا ہے اور خود بخود انہیں ساختی غلطی کے جوابات میں تبدیل کرتا ہے۔ پھینکتے وقت:

throw Exception('User not found');

کلائنٹ کو ایک منظم غلطی کا جواب ملتا ہے۔ مزید دانے دار کنٹرول کے لیے، Serverpod ٹائپ شدہ مستثنیات فراہم کرتا ہے۔

throw ServerpodClientException('User not found', statusCode: 404);

کلائنٹ کو تفصیلات بتائے بغیر سرور سائیڈ لاگنگ کرنے کے لیے:

session.log('Unexpected error during user creation', level: LogLevel.error);
throw Exception('An internal error occurred');

سرور پوڈ کا لاگنگ سسٹم لاگز کو ڈیٹا بیس میں اسٹور کرتا ہے اور پورٹ 8081 پر انسائٹس ڈیش بورڈ کے ذریعے انہیں قابل استفسار بناتا ہے۔ تمام درخواستیں وقت کی معلومات، اختتامی نقطہ کے نام اور نتائج کے ساتھ خود بخود لاگ ان ہوجاتی ہیں۔ کسی اضافی مڈل ویئر کی ضرورت نہیں ہے۔

API ٹیسٹنگ

Serverpod HTTP پر ایک اختتامی نقطہ کو بے نقاب کرتا ہے۔ درخواست کی شکل روایتی REST ڈھانچے کے بجائے Serverpod کے RPC کنونشنوں کی پیروی کرتی ہے، لیکن آپ curl کا استعمال کرکے خود اس کی جانچ کر سکتے ہیں۔

اختتامی نقطہ طریقہ کے لیے تیار کردہ URL پیٹرن یہ ہے:

POST /[endpoint]/[method]

طریقہ پیرامیٹرز پر مشتمل JSON باڈی استعمال کریں۔

صارف کی رجسٹریشن:

curl http://localhost:8080/auth/register \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "email": "seyi@example.com",
    "password": "securepassword",
    "firstName": "Seyi",
    "lastName": "Dev"
  }'

لاگ ان:

curl http://localhost:8080/auth/login \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"email": "seyi@example.com", "password": "securepassword"}'

تمام صارفین حاصل کریں (تصدیق شدہ):

curl http://localhost:8080/user/getAll \
  -X POST \
  -H "Authorization: Bearer eyJhbGci..."

ID کے ذریعے صارفین کو درآمد کریں:

curl http://localhost:8080/user/getById \
  -X POST \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{"userId": 1}'

اپنا پروفائل بنائیں:

curl http://localhost:8080/profile/create \
  -X POST \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 1,
    "bio": "Flutter engineer turned backend developer",
    "location": "Lagos, Nigeria",
    "website": "https://example.com"
  }'

صارف کی تازہ کارییں:

curl http://localhost:8080/user/update \
  -X POST \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{"userId": 1, "firstName": "Oluwaseyi"}'

صارف کو حذف کریں:

curl http://localhost:8080/user/delete \
  -X POST \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{"userId": 1}'

تعیناتی

Docker اور Fly.io کا استعمال کرتے ہوئے تعینات کریں۔

Serverpod پروجیکٹ کی تخلیق کے حصے کے طور پر ایک Dockerfile تیار کرتا ہے۔ یہ user_profile_api_server/Dockerfile میں واقع ہے اور استعمال کے لیے تیار ہے۔

سرور پیکج میں شامل docker-compose.yaml مقامی ترقی کے لیے PostgreSQL اور Redis کا انتظام کرتا ہے۔ Fly.io کی پیداواری تعیناتیوں کے لیے، عمل اسی ڈاکر پر مبنی پیٹرن کی پیروی کرتا ہے جو ذیل میں تعیناتی سیکشن میں شامل ہے۔

مرحلہ 1 – فلائی کے ساتھ تصدیق کریں:

fly auth login

مرحلہ 2 – سرور ڈائرکٹری سے ایپ چلائیں۔

cd user_profile_api_server
fly launch

مرحلہ 3 – پیداوار کا راز مقرر کریں:

fly secrets set JWT_SECRET="your_production_jwt_secret"

مرحلہ 4 – پیداوار کی ترتیب کو اپ ڈیٹ کریں:

Fly کی طرف سے فراہم کردہ ڈیٹا بیس کنکشن کی تفصیلات کے ساتھ config/production.yaml میں ترمیم کریں۔ Fly ایک DATABASE_URL ماحولیاتی متغیر کو انجیکٹ کرتا ہے جو سرور پوڈ کنفیگریشن کی قسم سے نقشہ بناتا ہے۔

مرحلہ 5 – تعینات کریں:

fly deploy

مرحلہ 6 – پہلی تعیناتی پر منتقلی کا اطلاق کریں۔

fly ssh console
dart bin/main.dart --apply-migrations --mode production

سرور پوڈ کلاؤڈ کے ساتھ تعینات کریں۔

Serverpod Cloud ایک مقامی تعیناتی پلیٹ فارم ہے جو خاص طور پر Serverpod ایپلی کیشنز کے لیے بنایا گیا ہے۔ ڈیٹا بیس کی فراہمی، اسکیلنگ، نگرانی، اور تعیناتی کو کم سے کم ترتیب کے ساتھ ہینڈل کرتا ہے۔

سرور پوڈ کلاؤڈ CLI انسٹال کریں۔

dart pub global activate serverpod_cloud_cli

سرٹیفیکیشن:

scloud login

Cloud.serverpod.dev پر Serverpod کلاؤڈ ڈیش بورڈ میں ایک پروجیکٹ بنائیں اور پھر اپنے مقامی پروجیکٹ کو جوڑیں۔

scloud link --project-id your-project-id

تقسیم:

scloud deploy

سرور پوڈ کلاؤڈ پروویژنز پوسٹگری ایس کیو ایل ڈیٹا بیسز کا نظم کرتے ہیں، ہجرت کا اطلاق کرتے ہیں، اور سرورز کو خود بخود تعینات کرتے ہیں۔ ہم مانیٹرنگ کی درخواستوں، لاگز اور پیداوار میں کارکردگی کے لیے انسائٹس ڈیش بورڈز بھی فراہم کرتے ہیں۔

ٹیموں کے لیے جو پہلے سے ہی Serverpod ایکو سسٹم سے وابستہ ہیں، Serverpod Cloud پیداوار کی تعیناتی کا تیز ترین راستہ ہے۔

نتیجہ

سرور پوڈ شیلف سے بنیادی طور پر مختلف نقطہ نظر اختیار کرتا ہے۔ جبکہ شیلف کنٹرول فراہم کرتا ہے، سرور پوڈ رفتار فراہم کرتا ہے۔ YAML میں ماڈلز کی وضاحت کریں، جنریٹر چلائیں، اور خود بخود ڈیٹا بیس کلاسز، سیریلائزیشن، اور کلائنٹ کوڈ تیار کریں۔ آپ اختتامی نقطہ کا طریقہ لکھتے ہیں، اور فریم ورک روٹنگ، پیرامیٹر نکالنے، توثیق، اور غلطی کی فارمیٹنگ کا خیال رکھتا ہے۔

ORM تجربے کا سب سے طاقتور حصہ ہے۔ ٹائپ سیف استفسار کے اظہارات، خودکار منتقلی کی تخلیق، اور کوڈ اور اسکیما کے درمیان کوئی ایس کیو ایل بڑھے ہوئے ڈیٹا بیس کی کارروائیوں کو خام SQL کے مقابلے میں نمایاں طور پر تیز اور محفوظ بناتے ہیں۔

لاگت سختی ہے۔ Serverpod کا URL ڈھانچہ، سیریلائزیشن فارمیٹ، اور تعمیراتی اصول اختیاری نہیں ہیں۔ اگر آپ کے API کو کسی مخصوص REST ڈھانچے پر عمل کرنے کی ضرورت ہے جو Serverpod کے RPC انداز سے مختلف ہے، تو آپ ایک فریم ورک کے خلاف کام کر رہے ہوں گے۔

API کا استعمال کرتے ہوئے ڈارٹ کلائنٹ کے ساتھ گرین فیلڈ فلٹر بیک اینڈ کے لیے، Serverpod کو شکست نہیں دی جا سکتی۔ سرور اور کلائنٹ کے درمیان کوڈ کا اشتراک، خودکار کلائنٹ کی تخلیق، اور سخت ٹول چین انٹیگریشن آپ کو انتہائی پیداواری ڈارٹ بیک اینڈ آپشن استعمال کرنے کے قابل بناتا ہے۔

APIs کے لیے جن کو متعدد کلائنٹس کی خدمت کرنے کی ضرورت ہے، بیرونی REST کنونشنز کی پیروی کریں، یا موجودہ انفراسٹرکچر کے ساتھ انضمام کریں جو Serverpod سے فارمیٹ کی توقع نہیں کرتا ہے، شیلف جیسے نچلے درجے کے ٹول کا استعمال آپ کو زیادہ کنٹرول فراہم کرتا ہے۔ اگر آپ یہ دیکھنا چاہتے ہیں کہ شیلف کے ساتھ ایک ہی صارف اور پروفائل مینجمنٹ API کیسے بنایا گیا ہے اور دونوں طریقوں کا براہ راست موازنہ کریں تو آپ وہ مضمون یہاں تلاش کر سکتے ہیں۔

یہ جاننا کہ کون سا ٹول کس کام کے لیے صحیح ہے وہی ایک ڈویلپر کو الگ کرتا ہے جو فریم ورک کو ایسے ڈویلپر سے جانتا ہے جو بیک اینڈ ڈیولپمنٹ کو سمجھتا ہے۔

مبارک کوڈنگ!

اوپر تک سکرول کریں۔