المفاهيم
هنفهم إيه؟ لما موبايل أو موقع يتعامل مع backend، العقد بينهما لازم يوضح شكل الطلب والنتيجة والتعامل مع الخطأ. هنصمم resources و methods وstatus codes، ثم نغطي retries وpagination وversioning والاتصال اللحظي، ونوازن سهولة الاستخدام مع التوافق والأمان.
قبل ما تبدأ: client هو البرنامج الذي يطلب الخدمة، وserver هو البرنامج الذي يستقبل الطلب. HTTP method مثل GET أو POST يحدد نوع الفعل، وstatus code يلخص النتيجة. لو مفاهيم HTTP جديدة، ابدأ بكارت ثم تابع الأمثلة.
المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.
#REST وتصميم الـ resourcesREST constraints and resource design
لما كذا client يتعاملوا مع نفس الخدمة، محتاجين interface مفهوم وثابت. architectural style بيخلي التعامل مع resources وليها representations، مش استدعاء methods بأسماء عشوائية.
زي كتالوج المحل: كل منتج ليه رقم، وتقدر تجيب بياناته من عنوان معروف، من غير ما تعرف المخزن شغال إزاي.
- Client-server: نفصل واجهة الاستخدام عن مسؤولية الخدمة.
- Stateless: كل request فيه سياق التعامل المطلوب؛ السيرفر ممكن يخزن resources، بس مش بيعتمد على conversation state مخفي بين requests.
- Cacheable: نحدد صلاحية الـ cache بـ Cache-Control و validators زي ETag.
- Uniform interface: تعريف resources، والتعديل عن طريق representations، و messages بتوصف نفسها، و hypermedia للتحرك بين الحالات.
- Layered system: وجود gateway أو proxy مايفرضش تغيير الـ client.
- Code on demand: إرسال كود قابل للتنفيذ اختياري.
GET /api/orders?status=paid&sort=-createdAt&limit=20 HTTP/1.1
GET /api/orders/42 HTTP/1.1
GET /api/customers/7/orders HTTP/1.1
POST /api/orders HTTP/1.1
POST /api/orders/42/cancellations HTTP/1.1
استخدم nouns بالجمع، و nesting محدود لعلاقة واضحة. آخر request بيحوّل الإلغاء لـ resource؛ action زي /orders/42/cancel برضه اختيار عملي لو موثّق. لوحده مش معناه REST، وماتحشرش كل business operation في بالعافية.
#HTTP methods: safe و idempotentHTTP methods: safety and idempotency
الـ client والـ proxy محتاجين يعرفوا إمتى يقروا وإمتى إعادة الطلب تبقى آمنة. Safe يعني العميل مش طالب تغيير state؛ logging جانبي عادي. يعني تكرار نفس الطلب له نفس الأثر المقصود على السيرفر، مش لازم نفس الرد.
| Method | Safe | Idempotent | الاستخدام |
|---|---|---|---|
| GET / HEAD | أيوه | أيوه | representation أو headers بس |
| OPTIONS | أيوه | أيوه | إمكانيات الاتصال |
| POST | لأ | مش مضمون | إنشاء أو process |
| PUT | لأ | أيوه | استبدال عند معلوم |
| PATCH | لأ | حسب العملية | تعديل جزئي |
| DELETE | لأ | أيوه | إزالة العلاقة بالـ resource |
PUT /api/orders/42 HTTP/1.1
If-Match: "version-7"
Content-Type: application/json
{ "status": "confirmed", "deliveryNote": "Leave at reception" }
الـ PUT هنا بيستبدل الـ writable representation المتفق عليها، مش لازم يكتب server-generated fields. الـ If-Match بيمنع الكتابة فوق version اتغير؛ لو مش مطابق نرجّع 412. PATCH بيضبط status ثابت ممكن يبقى idempotent، لكن increment للـ quantity مش كده. DELETE ممكن يرجّع 204 أول مرة و404 بعد كده ويظل idempotent.
#Status codes و ProblemDetailsStatus codes and ProblemDetails
رجوع 200 لكل حاجة بيجبر كل client يخمّن اللي حصل. اتفق على status codes وعلى شكل ثابت للأخطاء.
| Code | المعنى العملي |
|---|---|
| 200 / 201 / 202 / 204 | نجاح مع body؛ إنشاء ومعاه Location؛ قبول processing لسه مخلصش؛ نجاح من غير body |
| 304 | الـ cached representation لسه صالح بعد conditional GET |
| 400 / 422 | طلب غير صالح؛ أو محتوى مفهوم لكن غير قابل للتنفيذ. وثّق اختيار validation عندك |
| 401 / 403 / 404 | محتاج authentication صالح؛ مرفوض بالصلاحيات؛ غير موجود أو إخفاء وجوده |
| 409 / 412 | تعارض مع الحالة؛ precondition زي If-Match فشل |
| 429 / 500 / 503 | rate limit؛ خطأ غير متوقع؛ عدم إتاحة مؤقتة. Retry-After مفيد مع 429 و503 |
builder.Services.AddProblemDetails(); // (1)
var app = builder.Build();
app.UseExceptionHandler(); // (2)
app.MapGet("/example-conflict", () => Results.Problem(
statusCode: 409, title: "Order state conflict",
type: "https://api.example.com/problems/order-conflict",
extensions: new Dictionary<string, object?>
{
["code"] = "order.conflict" // (3)
}));
- (1) بيسجّل خدمة ProblemDetails؛ المعيار 9457 و media type هو application/problem+json.
- (2) بيلقط exceptions غير المتوقعة. زوّد mapping للحالات المعروفة، و traceId للربط بالـ logs.
- (3) code ثابت للـ client؛ title و detail للبني آدم، وممنوع stack أو secrets في الرد.
202 محتاج وسيلة متابعة زي Location لـ operation resource. 204 و304 مفيهمش response content. 401 في authentication بيكون معاه WWW-Authenticate challenge مناسب.
#Idempotency keys للدفع والـ retriesIdempotency keys for payments and retries
- العميل بعت طلب دفع، والسيرفر خصم المبلغ، لكن الرد ضاع في الشبكة. العميل لا يعرف إن الدفع نجح، فيعيد الطلب.
- العميل يرسل نفس مفتاح العملية في الإعادة. السيرفر يربط المفتاح بطلب محدد ونتيجته بشكل متين، ويعيد النتيجة السابقة بدل تنفيذ خصم جديد.
- لازم تتعامل كمان مع وصول نسختين معًا ونفس المفتاح ببيانات مختلفة؛ مجرد تخزين النتيجة في Dictionary داخل سيرفر واحد لا يحل المشكلة عبر نسخ السيرفر أو إعادة تشغيله.
الـ payment ممكن ينجح والرد يضيع. الـ retry لازم يستخدم نفس key للعملية المنطقية، علشان مايتخصمش مرتين.
-- SQL Server: one durable claim per caller, operation and key. (1)
CREATE UNIQUE INDEX UX_Idempotency_Claim
ON IdempotencyRecords (CustomerId, Operation, IdempotencyKey);
-- Store RequestHash, State, StatusCode, ResponseBody and ExpiresAt. (2)
-- Commit the claim, order change and stored response atomically. (3)
- (1) الـ unique constraint هو اللي بيحسم race بين requestين؛ SELECT قبل INSERT لوحده مش حماية.
- (2) نفس key مع body مختلف يترد بتعارض. completed يرجّع الرد المتخزّن؛ in-progress له سياسة انتظار أو رد موثّق.
- (3) التغييرات المحلية في واحدة. لو الدفع خارجي، استخدم key ثابت عند الـ provider مع و reconciliation؛ database transaction مش بتضم call.
#Pagination و filtering و sortingPagination, filtering and sorting
قائمة orders من غير حدود بتضغط الـ database والـ network. Offset سهل لصفحة برقم، لكن بيعدّي على صفوف كتير في الصفحات البعيدة. Cursor بيكمل بعد آخر sort key شافه العميل.
var page = await db.Orders.AsNoTracking()
.Where(o => o.CustomerId == customerId && o.Status == "Paid") // (1)
.Where(o => o.CreatedAt < afterDate
|| (o.CreatedAt == afterDate && o.Id < afterId)) // (2)
.OrderByDescending(o => o.CreatedAt).ThenByDescending(o => o.Id)
.Select(o => new { o.Id, o.CreatedAt, o.Total })
.Take(limit + 1).ToListAsync(ct); // (3)
- (1) authorization scope قبل paging. whitelist للـ filter والـ sort fields وحد أقصى للـ limit.
- (2) شرط الصفحة التالية للترتيب التنازلي؛ Id هنا رقم وبيكسر تعادل الوقت. أول صفحة من غير شرط after.
- (3) صف زيادة يحدد hasMore؛ رجّع limit بس واعمل cursor من آخر صف رجّعته.
اعمل index مناسب للـ filters والترتيب. الـ cursor يكون opaque ويتحقق من صلاحيته ويرتبط بنفس filters. مش snapshot ثابت لو الداتا نفسها بتتعدل. Offset مناسب لقوائم صغيرة أو قفز لصفحة، و exact total count تكلفة منفصلة.
#Versioning والتغييرات اللي بتكسر clientsVersioning and compatibility
client قديم لازم يفضل شغال بعد deploy جديد. اختار versioning policy قبل ما تغيّر العقد.
| الطريقة | مثال | المقايضة |
|---|---|---|
| Path | //v2/orders | واضح في docs والـ routing، URLs متعددة |
| Query | ?api-version=2 | سهل؛ لازم cache key يشمله |
| Header | Api-Version: 2 | ثابت؛ الـ gateway والـ cache لازم يفهموه |
| Media type | Accept: application/vnd.store.v2+json | content negotiation؛ tooling أعقد و Vary مناسب |
| Breaking عادةً | متوافق بشرط |
|---|---|
| حذف field، أو تغيير type أو معنى | إضافة response field لو client بيتجاهل المجهول |
| تحويل input اختياري لإجباري | input جديد اختياري ب default متوافق |
| تغيير error contract أو guarantees | endpoint جديد من غير تغيير القديم |
| enum value جديدة مع exhaustive clients | enum جديدة لو unknown values مدعومة صراحةً |
اعمل deprecation period وراقب استخدام القديم قبل إزالته. Sunset يعلن ميعاد الإيقاف؛ تغيير السلوك ممكن يكسر client حتى لو ثابت.
#OpenAPI و contract-first و SDDOpenAPI, contract-first and spec-driven development
لو frontend و backend فهموا الطلب بشكل مختلف، الكود الصح لوحده مش كفاية. Contract-first يبدأ بعقد متراجع قبل التنفيذ. SDD أوسع: يحدد المطلوب، والـ acceptance criteria، والفشل المتوقع، وبعدها الخطة والـ tests.
openapi: 3.1.0
info: { title: Orders API, version: 1.0.0 }
paths:
/orders/{id}:
get:
operationId: getOrder
parameters:
- in: path
name: id
required: true
schema: { type: integer }
responses:
'200': { description: Order found }
'404': { description: Order not found }
ده عقد صغير صالح؛ العقد الحقيقي يضيف schemas والـ security والـ errors. راجعوه، اعملوا mocks و typed clients، و يعمل diff و behavioral contract tests. OpenAPI مولّد من implementation مختلف عن إن العقد هو اللي قاد التصميم.
// Requires Microsoft.AspNetCore.OpenApi, version 10.x.
builder.Services.AddOpenApi(); // (1)
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.MapOpenApi(); // (2)
- (1) built-in document generation من 9؛ .NET 10 افتراضيه OpenAPI 3.1.
- (2) بيعرض document endpoint، مش . لو محتاج production access حدد authorization policy واضحة.
| القديم الشائع | النهارده |
|---|---|
| Swashbuckle في templates القديمة | built-in generation؛ Swagger UI أو Scalar اختيار منفصل. Swashbuckle لسه ممكن تستخدمه |
للـ events، AsyncAPI بيوصف channels والـ messages والـ send والـ receive والـ protocol bindings. مايدّيش delivery guarantees لوحده؛ لازم توثّق ordering والـ retries والـ deduplication.
#HATEOAS والـ conditional requestsBonusHATEOAS and conditional requests
اختياري بس بيفرق: مش هيتسأل فيه في كل انترفيو، بس معرفته بتعمّق فهمك وبتميّزك عن غيرك.
الـ client اللي حافظ transitions كلها ممكن يعرض زر إلغاء لطلب اتشحن. Hypermedia يقدّم links للأفعال المتاحة في الحالة الحالية، بعلاقات مفهومة ومتفق عليها.
{
"id": 42,
"status": "pending",
"links": [
{ "rel": "self", "href": "/api/orders/42" },
{ "rel": "cancel", "href": "/api/orders/42/cancellations", "method": "POST" }
]
}
ده شكل توضيحي، مش media type قياسي. العميل محتاج يفهم rel والـ payload؛ links مش authorization ولا guarantee إن الحالة لسه زي ما هي. يقلل coupling لكنه يزود التصميم والـ tooling، فكتير من بتطبّق جزء من بس. ETag مع If-None-Match يقلل نقل الداتا ويرجع 304، ومع If-Match يحمي update من lost update.
#Webhooks: توقيع وتسليم موثوقWebhooks: signatures and reliable delivery
لو متجر مستني payment event، polling المستمر مكلف. الـ webhook هو callback من provider للـ receiver، بس ممكن يتكرر أو يوصل متأخر أو بترتيب مختلف.
using System.Security.Cryptography;
using System.Text;
// rawBody is the exact received byte array; secret is loaded securely.
byte[] prefix = Encoding.UTF8.GetBytes(timestamp + ".");
byte[] signed = new byte[prefix.Length + rawBody.Length];
prefix.CopyTo(signed, 0);
rawBody.CopyTo(signed, prefix.Length);
byte[] expected = HMACSHA256.HashData(secret, signed); // (1)
bool valid = CryptographicOperations.FixedTimeEquals(
expected, receivedSignature); // (2)
- (1) مثال protocol متفق عليه بيوقّع timestamp و raw bytes. ماتعملش deserialize و serialize قبل verification. اتبع بالظبط signature format بتاع provider.
- (2) comparison ثابت الوقت؛ اتحقق من encoding والطول وارفض timestamp قديم و event ID متكرر. signature صحيحة لوحدها ماتمنعش replay.
سجّل event في inbox durable وبعدين رجّع 2xx بسرعة واعمل processing في worker. sender يستخدم exponential backoff مع jitter وحد للمحاولات و dead-letter وإعادة إرسال يدوية. receiver يطبق ويواجه out-of-order events. و secret rotation أساسيين؛ تحقق من URLs لمنع لما المستخدم يحدد callback.
#SSE و WebSockets و SignalR و long pollingSSE, WebSockets, SignalR and long polling
تحديث حالة order فوراً محتاج قناة مناسبة لاتجاه وحجم الرسائل.
| الاختيار | الاتجاه | إمتى والتكلفة |
|---|---|---|
| server للـ client | text stream و reconnect في EventSource؛ مناسب progress | |
| WebSocket | اتجاهين | رسائل text أو binary؛ reconnect والـ protocol مسؤوليتك |
| SignalR | اتجاهين بـ hubs | framework فوق transports مع groups و fallback؛ scale-out محتاج تخطيط |
| Long polling | طلب مستني event وبعدين طلب جديد | توافق واسع؛ headers و requests أكتر |
using System.Runtime.CompilerServices;
app.MapGet("/demo/progress", (CancellationToken ct) =>
TypedResults.ServerSentEvents(Progress(ct), eventType: "progress")); // (1)
static async IAsyncEnumerable<int> Progress(
[EnumeratorCancellation] CancellationToken ct)
{
for (int percent = 0; percent <= 100; percent += 10)
{
await Task.Delay(500, ct); // (2)
yield return percent;
}
}
- (1) TypedResults.ServerSentEvents في 10 يكتب text/event-stream بدل كتابة format يدوي.
- (2) stream تجريبي قابل للإلغاء؛ الحقيقي يقرأ updates من مصدر محدود الـ buffering.
ظبط proxy buffering والـ timeouts والـ heartbeats. لو محتاج replay ابعت event IDs واحفظ الأحداث وفسّر Last-Event-ID؛ reconnect مش معناه ضمان عدم فقد الرسائل. EventSource في المتصفح مابيدعمش arbitrary authorization headers. SignalR مش durable message ؛ خطط للـ reconnect والـ backplane أو managed service.
#gRPC والاختيار بين API stylesgRPC and choosing an API style
بين services متحكم فيها، عقد typed و رسائل binary ممكن يناسبوا أكتر من public . بيولّد client و server types من proto، وعادةً بينقل Protobuf على /2.
syntax = "proto3";
option csharp_namespace = "Store.Contracts";
service Inventory {
rpc GetStock (StockRequest) returns (StockReply); // (1)
rpc WatchStock (StockRequest) returns (stream StockReply); // (2)
}
message StockRequest { string product_id = 1; }
message StockReply { int32 available = 1; }
- (1) unary: request واحد ورد واحد. الـ field numbers جزء من wire contract؛ ماتعيدش استخدام أرقام fields اتحذفت.
- (2) server streaming. فيه كمان client streaming و bidirectional streaming؛ حدد deadlines والـ cancellation والـ flow control.
| الأسلوب | مناسب لـ | المقايضة |
|---|---|---|
| public resources والـ HTTP caching | ممكن over-fetching أو requests كتير | |
| GraphQL | client يختار fields و graph queries | query cost limits و resolver و authorization أعقد |
| gRPC | typed internal والـ streaming | browser native support محدود؛ gRPC-Web أو gateway و tooling متوافق |
| SignalR | live و groups | connections وحالة مؤقتة وتوسيع خاص؛ مش contract بديل كامل |
ماتختارش gRPC لمجرد كلمة أسرع؛ قيس payload والـ ، وتأكد إن proxies والعملاء بيدعموا الـ protocol. gRPC-Web مش بيوفر كل streaming modes المتاحة في native gRPC.
#API security checklist
توثيق مش كفاية لحماية orders. راجع كل boundary قبل نشر الـ .
- ، و token validation للـ issuer والـ audience والـ expiry والـ signature؛ secrets خارج الكود.
- Object-level و tenant-level authorization على كل resource، مش مجرد role على endpoint.
- Request منفصلة و allowlist للحقول لمنع over-posting؛ validation وحدود body والـ uploads والـ page size.
- Parameterized queries؛ whitelist لأسماء sort columns؛ منع في URLs الخارجية.
- Rate limits و quotas و timeouts و cancellation وحدود query complexity؛ 429 سياسة معلنة.
- يسمح origins محددة، لكنه مش authorization. Cookie authentication محتاج protection للعمليات المغيّرة.
- ماتعملش cache لرد حساس في shared cache بالغلط؛ راجع Cache-Control و cache keys.
- Logs منظمة من غير tokens أو بيانات دفع؛ IDs، و audit للتغييرات، و dependencies محدثة.
مراجع للمراجعة: HTTP semantics: RFC 9110 لتعريف methods والـ status codes، و ProblemDetails: RFC 9457، و ASP.NET Core 10 للـ SSE والـ OpenAPI. الأمثلة مختصرة للتعلّم؛ business models والـ authorization والـ persistence بتتكمل حسب العقد.
أسئلة الانترفيو
مفيش نتايج
راجع فهمك
ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟
8.1What makes an API RESTful?Junior
ابدأ بالـ constraints، مش بس: stateless و cacheable و uniform interface فيها hypermedia، مع فصل client و server و layers.
I describe as an architectural style rather than a synonym for over . Its constraints are client-server separation, stateless requests, cacheability, a uniform interface, layering, and optional code on demand. I identify resources with stable URIs and use representations and standard HTTP semantics. Strict REST also includes hypermedia-driven transitions. In practice, I would be clear whether an follows all those constraints or is a pragmatic resource-oriented HTTP API.
8.2How do you design resource URLs?Junior
nouns بالجمع، و query parameters للـ filtering والـ paging. ماتربطش العقد بأسماء database tables أو تعمل nesting طويل.
I use stable plural nouns such as orders and products, with identifiers for individual resources. Query parameters handle filtering, sorting and pagination. I nest paths only when ownership or context is useful, such as a customer's orders, and avoid deeply nested URLs. For an operation such as cancellation, I may create a cancellation resource or use a documented action. I design around the business contract rather than exposing database table structure directly.
8.3What is the difference between safe and idempotent methods?Junior
Safe مابيطلبش mutation؛ التكرار مايغيّرش الأثر المقصود. DELETE مثال idempotent لكنه مش safe.
I use safe to mean that the requested operation does not change resource state, as with GET and HEAD. Logging can still happen as an incidental side effect. means repeating an identical request has the same intended server effect as sending it once. PUT and DELETE are idempotent but unsafe. Responses may differ between attempts, so a DELETE returning 204 and then 404 can still be idempotent.
8.4When do you choose POST, PUT or PATCH?Junior
POST للـ processing أو creation؛ PUT استبدال عند معروف؛ PATCH تعديل جزئي و حسب نوع التعديل.
I normally use POST to create a resource under a collection or initiate processing, especially when the server chooses its . PUT replaces the writable representation at a known URI and is . PATCH applies a partial modification according to a defined patch format. I do not assume PATCH is idempotent: setting a status repeatedly can be, but incrementing a quantity is not. For concurrent updates I also consider conditional requests using ETags.
8.5Which status codes matter when creating or processing an order?Junior
201 للإنشاء، و202 لقبول processing، و204 من غير body. 409 تعارض state و412 فشل شرط version.
I return 201 when a new order is created and normally include its in Location. If processing is accepted but unfinished, I use 202 and provide a way to check progress. A successful operation without response content can return 204. Invalid input uses the documented 400 or 422 policy, a state conflict can use 409, and a failed If-Match precondition uses 412. I avoid hiding all outcomes behind 200.
8.6How do 401 and 403 differ?Junior
401 محتاج credentials صالحة و challenge مناسب؛ 403 رفض للصلاحيات. ممكن 404 لإخفاء resource حسب سياسة معلنة.
I return 401 when the request needs valid authentication credentials, for example because a bearer is missing or invalid, with the appropriate authentication challenge. I use 403 when authorization refuses the operation, usually after the caller has authenticated. For sensitive resources I may deliberately return 404 to avoid disclosing existence, following a consistent policy. In all cases I check access to the specific order, not just whether the caller has a valid token.
8.7How would you make a payment endpoint retry-safe?Mid
key ثابت للعملية، و unique claim مع request hash، والـ response يتخزّن. الدفع الخارجي محتاج provider و reconciliation.
I require a stable key per logical payment and scope it to the caller and operation. A unique database constraint arbitrates concurrent requests, and I store a payload hash to reject key reuse with different input. Completed attempts replay the recorded response, while in-progress behavior and expiry are documented. Local changes commit atomically. For an external payment provider I also reuse its idempotency mechanism and reconcile uncertain outcomes rather than assuming my database covers the remote charge.
8.8When would you use cursor pagination instead of offset?Mid
للـ feeds الكبيرة؛ ترتيب ثابت فيه tie-breaker و index مناسب. القفز لصفحة برقم أسهل بالـ offset.
I use offset for small administrative lists and direct access to numbered pages. For large feeds, cursor pagination avoids traversing a growing number of skipped rows. I use a deterministic sort, such as creation time plus a unique numeric identifier, and a matching index. The cursor captures both values and is tied to the filters. It reduces insert-related paging problems but does not create snapshot consistency when records themselves change, and arbitrary page jumps become harder.
8.9How do you expose filtering and sorting safely?Mid
allowlist للحقول والـ operators وحدود للـ limit والتكلفة. ماتركّبش من sort string جاي من المستخدم.
I define a small, explicit vocabulary of filter fields, operators and sort directions instead of accepting arbitrary query expressions. I validate types, cap page sizes and apply authorization scope before filtering and paging. Values become parameters, while dynamic identifiers come from an allowlist. I also consider supporting indexes and query cost, because a syntactically valid filter can still be expensive. Stable ordering and documented cursor behavior make the endpoint predictable for clients.
8.10How do you version an API and identify breaking changes?Mid
path و query و headers و media types اختيارات. معنى field أو requirement جديد ممكن يكسر حتى لو صالح.
I choose a consistent strategy, often a path segment for visibility, and document deprecation and retirement dates. Removing fields, changing types, adding required input or changing established behavior can break clients. Optional additions are usually compatible only if clients tolerate unknown fields and values. I use diffs and consumer contract tests to check that assumption. Before retiring a version I measure real usage and coordinate migration instead of relying only on a published date.
8.11What is contract-first development with OpenAPI?Mid
بنراجع العقد الأول، وبعده mocks والـ clients والتنفيذ والـ tests. توليد docs بعد الكود لوحده مش contract-first.
I write and review the contract before implementation, including schemas, status codes, authentication and error responses. Teams can then use mocks and generated clients while the service is being built. checks for incompatible changes, and behavioral tests verify guarantees the schema cannot express. In 10 I can use built-in OpenAPI generation to compare the implementation with the agreed document, but generating documentation alone does not make the process contract-first.
8.12How does spec-driven development differ from writing an API schema?Mid
الـ spec تشمل business rules والـ acceptance criteria والفشل؛ OpenAPI جزء من contract مش كل المطلوب.
I treat an as one part of a broader specification. The specification explains the user outcome, business rules, authorization, failure scenarios and acceptance criteria. I resolve ambiguity before planning implementation, then turn the criteria into reviewable tasks and tests. OpenAPI captures the boundary, while operational guarantees such as retry safety need additional detail. The value comes from shared, testable expectations, whether implementation is written by a person or assisted by an agent.
8.13Why use ProblemDetails rather than custom error strings?Mid
شكل معروف للأخطاء و code ثابت؛ النص للبشر مش شرط client logic. ماتسرّبش stack .
I use ProblemDetails to give clients a recognizable error structure rather than unrelated strings from each endpoint. The standard fields describe the problem type, title, status and occurrence, and I can add a stable application code and identifier. Clients should branch on stable semantics, not parse human-readable messages. I keep sensitive diagnostics in logs. provides registration and exception handling support, but I still define mappings for expected domain failures.
8.14What does AsyncAPI describe?Mid
contracts للـ event channels والـ messages و operations؛ مش بديل ولا بيدي exactly-once.
I use AsyncAPI to document an event interface: channels, message schemas, send and receive operations, and relevant protocol bindings. It gives producers and consumers a shared contract, much as OpenAPI does for . I still document delivery behavior separately, including ordering scope, retries, duplicate handling and evolution. The document can support tooling and validation, but it does not implement a or guarantee exactly-once processing for business side effects.
8.15How do you secure and reliably process webhooks?Senior
raw bytes و timestamp في signature، constant-time comparison، replay protection، و inbox قبل 2xx. duplicate و out-of-order طبيعيين.
I verify the provider's signature over the exact raw payload, validate its timestamp and compare signatures in constant time. I rotate secrets and deduplicate event identifiers because signatures do not stop replay. After durable inbox storage I acknowledge promptly and process asynchronously. The sender uses bounded retries with backoff and jitter, and we support dead-letter inspection and replay. I design state transitions for duplicate and out-of-order events and protect configurable callback destinations against .
8.16When would you choose SSE, WebSockets or SignalR?Mid
لتحديثات server للـ client؛ WebSocket لاتجاهين؛ SignalR abstraction فيها hubs و groups و transports.
I choose for one-way server updates such as progress or order status, especially when ordinary streaming fits the infrastructure. WebSockets suit bidirectional, frequent messages but leave more protocol work to the application. SignalR gives me hubs, groups and transport negotiation on top of those concerns. I also plan authentication, proxy timeouts, reconnection, and scale-out. None of these choices alone provides durable message delivery or automatic recovery of every missed event.
8.17What are gRPC streaming modes and their limitations?Mid
unary، و server streaming، و client streaming، و bidirectional؛ اتأكد من browser والـ proxies والـ deadlines.
I distinguish unary calls from server streaming, client streaming and bidirectional streaming. The proto contract generates typed , and streaming lets messages flow without creating a new request for each item. I set deadlines, propagate cancellation and account for slow consumers. Native is a good fit between controlled services, but browser clients usually need gRPC-Web or a gateway, and supported streaming modes differ. I verify the complete network path rather than assuming /2 support everywhere.
8.18How would you choose between REST, GraphQL, gRPC and SignalR?Senior
ابدأ من client والـ interaction؛ مش benchmark عام. GraphQL محتاج query cost و resolver authorization، و SignalR للـ live interaction.
I start with client needs and interaction patterns. works well for public resources and caching. GraphQL helps clients request different graph-shaped views, but requires cost controls and careful resolver authorization. suits typed internal calls and streaming when clients and infrastructure support it. SignalR fits interactive push to connected users. I compare operational cost, compatibility and measured workloads, and may combine styles rather than force one mechanism to serve every boundary.
8.19What does HATEOAS add, and when is it worth using?Senior
links حسب الحالة بتقلل حفظ transitions في client؛ بس semantics والـ tooling أعقد ومش بديل authorization.
I see as a way for representations to advertise available transitions through meaningful links. A client can discover that an order can currently be cancelled instead of hard-coding every transition . It still needs to understand relation and payload semantics, and the server must recheck authorization and state when the action runs. I use it when discoverability and long-lived client evolution justify the added representation design and tooling, rather than adding decorative links without client benefit.
8.20What would you check before publishing an order API?Senior
اختبر object authorization، و over-posting، وحدود الموارد، والـ retries، والـ cache، وتسريب البيانات. مش حماية authorization.
I check authentication, object and tenant authorization, boundaries, validation, parameterization and resource limits. I test that one customer cannot access another customer's order, even with its identifier. I review retry safety for payments, webhook verification, cache policies and error redaction. I also check rate limits, timeouts, telemetry and dependency maintenance. is only a browser access policy, and cookie-authenticated mutations need protection. Finally, I run compatibility tests against the published contract.
اختبر نفسك
إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.
DELETE returns 204 first and 404 on retry. Is it necessarily non-idempotent?
What resolves two simultaneous requests using the same idempotency key?
Which response fits accepted but unfinished processing?
Which ordering is suitable for a creation-time cursor?
Adding an enum value is backward compatible when...
What does AddOpenApi plus MapOpenApi provide?
A signed webhook is received twice. What is still needed?
Which .NET 10 API writes an SSE result?
A stale If-Match value should normally produce...
A valid token lets customer A read customer B's order. What is missing?