الاستقرار: بيتا
عرض فاتورة من البداية إلى النهاية عبر REST
لمحة سريعة
قسم بعنوان «لمحة سريعة»انقُل فاتورة واحدة من JSON إلى ملف PDF مُتحقَّق منه محليًا عبر سطح نقل الحالة التمثيلية Representational State Transfer (REST) في NextPDF Connect، تبادلًا واحدًا عبر الشبكة في كل مرة. تُرسِل هذه الوصفة مهمة عرض إلى POST /api/v1/jobs، وتُعيد الإرسال بترويسة Idempotency-Key نفسها لتُظهِر المسار الآمن تجاه التكرار، وتستطلِع GET /api/v1/jobs/{id}، وتنزِّل ملف PDF من GET /api/v1/jobs/{id}/result، وتفحص البايتات بأمر qpdf --check، وتحذف المهمة المكتملة.
كل استجابة أدناه هي نسخة حرفية مُلتقَطة من نشر Connect فعلي من المستوى الأساسي (nextpdf/server تحت RoadRunner، مربوطًا بـ http://localhost:8080). الاستبدال الوحيد هو مفتاح الواجهة، مُظهَرًا بوصفه متغير البيئة $NEXTPDF_CONNECT_TOKEN؛ أما مُعرِّفات المهام ومُعرِّفات الطلبات والطوابع الزمنية والترويسات وبايتات الجسم فهي بالضبط ما أعاده الخادم. وستختلف بالطبع قيم الترويسات مثل Date وX-Request-Id في نشرك.
تُشغِّل هذه الوصفة مستندًا واحدًا حتى تتمكن من قراءة كل تبادل كاملًا. أما للمستندات المتعددة، والتزامن المحدود، وحلقات الاستطلاع الموجَّهة بترويسة Retry-After، فراجِع توليد ملفات PDF مُجمَّعة مع تتبُّع التقدُّم، التي تستخدم سطح المهام نفسه.
التثبيت
قسم بعنوان «التثبيت»يستخدم جانب الخادم توزيع Connect القياسي:
composer require nextpdf/serverجانب العميل في هذه الوصفة هو curl مع qpdf، لذا يمكنك نقله إلى أي عميل HTTP. صدِّر قيم نشرك أولًا:
export NEXTPDF_CONNECT_URL="http://localhost:8080"export NEXTPDF_CONNECT_TOKEN="npk_live_{kid}_{secret}" # your real key# Key provisioning and server startup live in the quickstart:نظرة مفاهيمية عامة
قسم بعنوان «نظرة مفاهيمية عامة»يفصل سطح المهام غير المتزامنة الإرسال عن الاسترجاع: تُرسِل طلب عرض، وتتلقى سجل مهمة، وتجلب النتيجة حين تبلغ المهمة حالة completed. طلب العرض نفسه هو مصفوفة operations مرتَّبة — أنواع العمليات نفسها (set_font، add_text، add_table، add_image، add_page) التي تدعم استدعاءات أدوات Connect عبر كل وسيلة نقل — إضافةً إلى حقول على مستوى المستند (page_size، orientation، title، author).
يُشكِّل تفصيلان في العقد النسخةَ المُلتقَطة التي أنت على وشك قراءتها:
- إرسال آمن تجاه التكرار. يُعيد الإرسال المصحوب بترويسة
Idempotency-Keyرمز201 Createdفي المرة الأولى، ويُعيد200 OKمع سجل المهمة نفسه عند إعادة الإرسال، فلا تُنفِّذ إعادة المحاولة على الشبكة عملية عرض مرتين أبدًا. - قد يكون الإرسال نهائيًا بالفعل. يعالج الإصدار الحالي المهمة ضمن مسار الطلب قبل أن يردّ على
POST، لذا يمكن أن تحمل استجابة الإرسال بالفعلstatus: "completed"— كما تفعل أدناه. عقد الاستطلاع حتى الحالة النهائية هو شكل الواجهة المستقر: اكتب حلقة الاستطلاع، واقبل حالة نهائية عند أي محاولة، بما في ذلك الأولى.
يمكنك تأكيد ما يكشفه نشرك قبل إرسال أي شيء: تُعيد GET /api/v1/capabilities كتالوج العمليات الذي يمكن لمستوى مفتاح واجهتك الوصول إليه. في النشر من المستوى الأساسي المُلتقَط هنا، أدرجت العمليات الأساسية فقط؛ والكتالوج المرجعي هو دائمًا استجابة الخادم قيد التشغيل نفسها، لا هذه الصفحة.
سطح واجهة برمجة التطبيقات
قسم بعنوان «سطح واجهة برمجة التطبيقات»| التبادل | الطريقة والمسار | الحالة المُلتقَطة |
|---|---|---|
| إرسال مهمة العرض | POST /api/v1/jobs | 201 Created |
| إعادة الإرسال نفسه | POST /api/v1/jobs (بترويسة Idempotency-Key نفسها) | 200 OK |
| استطلاع سجل المهمة | GET /api/v1/jobs/{id} | 200 OK |
| تنزيل ملف PDF | GET /api/v1/jobs/{id}/result | 200 OK، application/pdf |
| حذف المهمة المكتملة | DELETE /api/v1/jobs/{id} | 204 No Content |
المصادقة رمز حامل على كل طلب /api/v1/*: Authorization: Bearer npk_live_{kid}_{secret}. تتشارك استجابات JSON الناجحة الغلاف { "data": ..., "meta": ... }؛ والحقول التي تعتمد عليها في تصرّفك تقع تحت data.
طلب الفاتورة
قسم بعنوان «طلب الفاتورة»اكتب طلب العرض في invoice.json. إنه قائمة عمليات بسيطة وحتمية — سطر ترويسة عريض، وسطر إصدار، وجدول بنود:
{ "page_size": "A4", "orientation": "portrait", "title": "Invoice INV-2026-0042", "author": "Aurora Fixtures Ltd.", "operations": [ { "type": "set_font", "family": "helvetica", "style": "B", "size": 16 }, { "type": "add_text", "text": "Invoice INV-2026-0042" }, { "type": "set_font", "family": "helvetica", "style": "", "size": 10 }, { "type": "add_text", "text": "Issued 2026-07-08 by Aurora Fixtures Ltd. Payment is due within 30 days.", "width": 0, "line_height": 5 }, { "type": "add_table", "html": "<table><tr><th>Item</th><th>Qty</th><th>Unit price</th><th>Amount</th></tr><tr><td>Cable tray, 300 mm</td><td>12</td><td>18.40</td><td>220.80</td></tr><tr><td>Mounting kit</td><td>4</td><td>9.75</td><td>39.00</td></tr><tr><td>Site delivery</td><td>1</td><td>25.00</td><td>25.00</td></tr><tr><td>Total (EUR)</td><td></td><td></td><td>284.80</td></tr></table>" } ]}حقول الفاتورة هنا بيانات عيّنة. وشكل الوسائط المُعتمَد لكل عملية هو الذي يُبلِغ عنه نشرك — فعبر MCP، تُعيد tools/list مخطط الإدخال الكامل لكل نوع عملية يستخدمه هذا الطلب.
سِجل التبادل من البداية إلى النهاية
قسم بعنوان «سِجل التبادل من البداية إلى النهاية»1. إرسال مهمة العرض
قسم بعنوان «1. إرسال مهمة العرض»curl -sS -i -X POST "$NEXTPDF_CONNECT_URL/api/v1/jobs" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: inv-2026-0042" \ --data-binary @invoice.jsonيردّ الخادم بـ 201 Created:
HTTP/1.1 201 CreatedCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 99X-Request-Id: 019f3fd6-c6ed-727c-958e-2fa4370ba97eDate: Wed, 08 Jul 2026 03:47:48 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd6-c6ed-727c-958e-2fa4370ba97e", "timestamp": "2026-07-08T03:47:48+00:00", "duration_ms": 63.31, "api_version": "v1" }}المهمة نهائية بالفعل في هذه النسخة المُلتقَطة — فحقل status هو "completed" وحقل result_url موجود — لأن الإصدار الحالي يعرض ضمن مسار الطلب قبل أن يردّ. لا تعتمد على ذلك: عامِل استجابة الإرسال بوصفها نتيجة الاستطلاع الأولى، وتفرّع على data.status مثل أي استطلاع آخر.
2. إعادة الإرسال (المسار الآمن تجاه التكرار)
قسم بعنوان «2. إعادة الإرسال (المسار الآمن تجاه التكرار)»أعِد المحاولة بالأمر نفسه تمامًا — ترويسة Idempotency-Key نفسها، والجسم نفسه:
curl -sS -i -X POST "$NEXTPDF_CONNECT_URL/api/v1/jobs" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: inv-2026-0042" \ --data-binary @invoice.jsonيُعيد الخادم 200 OK — لا 201 — مع job_id نفسه، ولا تحدث عملية عرض ثانية (قارِن meta.duration_ms بالاستجابة الأولى):
HTTP/1.1 200 OKCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 99X-Request-Id: 019f3fd6-c756-7226-bc85-b255d75bd449Date: Wed, 08 Jul 2026 03:47:48 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd6-c756-7226-bc85-b255d75bd449", "timestamp": "2026-07-08T03:47:48+00:00", "duration_ms": 1.03, "api_version": "v1" }}3. استطلاع سجل المهمة
قسم بعنوان «3. استطلاع سجل المهمة»curl -sS -i "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN"HTTP/1.1 200 OKCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 98X-Request-Id: 019f3fd7-0038-7131-b71e-4c69e637233bDate: Wed, 08 Jul 2026 03:48:02 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd7-0038-7131-b71e-4c69e637233b", "timestamp": "2026-07-08T03:48:02+00:00", "duration_ms": 0.24, "api_version": "v1" }}يُظهِر هذا الاستطلاع سجلًا نهائيًا، لذا لا توجد ترويسة Retry-After ولا حقل poll_url. وما دامت المهمة pending أو running، يضبط الخادم Retry-After (فاصل زمني قدره ثانيتان) في كل استطلاع — احترِمه بدلًا من الاستطلاع في حلقة محكمة.
4. تنزيل ملف PDF
قسم بعنوان «4. تنزيل ملف PDF»curl -sS -D result-headers.txt \ "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc/result" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -o invoice-inv-2026-0042.pdfHTTP/1.1 200 OKCache-Control: no-storeContent-Disposition: attachment; filename="job-job_9bd0808960f10eb568484acc.pdf"Content-Length: 3663Content-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/pdfReferrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 98X-Request-Id: 019f3fd7-0056-711e-acad-9f7ac5f21ba7Date: Wed, 08 Jul 2026 03:48:02 GMTالجسم هو بايتات PDF الثنائية — 3,663 بايتًا في هذه النسخة المُلتقَطة، مطابقةً لترويسة Content-Length — وقد حُذف هنا اختصارًا. وقد كُتب إلى invoice-inv-2026-0042.pdf.
5. التحقّق من البايتات المُنزَّلة محليًا
قسم بعنوان «5. التحقّق من البايتات المُنزَّلة محليًا»إن 200 مع Content-Type: application/pdf ليست، بمفردها، دليلًا على أن الجسم ملف PDF سليم البِنية. شغِّل فحصًا بنيويًا باستخدام qpdf:
qpdf --check invoice-inv-2026-0042.pdfالمخرجات المُلتقَطة للملف المُنزَّل أعلاه:
checking invoice-inv-2026-0042.pdfPDF Version: 2.0File is not encryptedFile is not linearizedNo syntax or stream encoding errors found; the file may still containerrors that qpdf cannot detectصياغة qpdf نفسها هي الحدّ الصادق: هذا فحص للصياغة ولترميز التدفق، لا حكم بالمطابقة لأي معيار.
6. حذف المهمة المكتملة
قسم بعنوان «6. حذف المهمة المكتملة»curl -sS -i -X DELETE "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN"HTTP/1.1 204 No ContentCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 97X-Request-Id: 019f3fd7-0084-722f-bf54-3dabead8aeeaDate: Wed, 08 Jul 2026 03:48:02 GMTبعد الحذف، يزول سجل المهمة ونتيجتها المخزَّنة؛ ويُعيد أي GET لاحق على المهمة رمز 404.
الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- تفرّع على
data.status، لا على حالة HTTP وحدها. يُعيد كل من الإرسال وإعادة الإرسال والاستطلاع رمز2xxمع سجل مهمة؛ وتقع حالة دورة الحياة فيdata.status(pending،running،completed،failed،cancelled). - مفتاح مُعاد إرساله بجسم مختلف هو
409 Conflict. لا تحدث إعادة الإرسال الآمنة تجاه التكرار بالرمز200إلا حين يطابق الجسمُ الإرسالَ الأصلي. لا تُعِد أبدًا استخدام مفتاح لمحتوى مختلف. /resultقبل الاكتمال هو409. نزِّل فقط بعد أن يُظهِر الاستطلاعcompleted. الرمز409استجابة عادية تُفحَص، لا فشل نقل — وهو الفصل نفسه بين النقل والحالة الذي تتبعه كل وصفة من وصفات Connect (راجِع اصطلاحات الوصفات).- المهام محصورة بالمالك. مهمة مُرسَلة بمفتاح واجهة واحد تكون غير مرئية لمفتاح آخر:
GETعبر مالك مختلف يُعيد404، لا403. استطلِع ببيانات الاعتماد نفسها التي أرسلت بها. - قد يكون
progressغائبًا. لا يحمل السجل المُلتقَط أي حقلprogressلأن المهمة كانت نهائية بالفعل. وحين يتتبَّع الخادم التقدُّم لمهمة غير نهائية، يكونdata.progressعددًا صحيحًا من 0 إلى 100؛ عامِل الحقل المفقود على أنه مجهول، لا صفر. - مهمة
failedتحملdata.error. سجِّلها؛ ولا تُعِد الإرسال بشكل أعمى.
الأداء
قسم بعنوان «الأداء»تُكلِّف مهمة عرض واحدة إرسالًا واحدًا، وبضعة استطلاعات على الأكثر، وتنزيلًا واحدًا. تروي قيم meta.duration_ms المُلتقَطة القصة: 63.31 مِلي ثانية لعرض الفاتورة عند الإرسال، و1.03 مِلي ثانية لإعادة الإرسال الآمنة تجاه التكرار التي لم تُنجِز أي عمل، وقراءات حالة دون المِلي ثانية. استطلِع بوتيرة Retry-After لدى الخادم بدلًا من حلقة محكمة؛ فقراءة الحالة زهيدة التكلفة لكنها ليست مجانية، ويرصد لها مقيِّد المعدل ميزانية (راقِب X-Ratelimit-Remaining وهو يتناقص في الترويسات المُلتقَطة). أما للدفعات، فقيِّد المهام قيد التنفيذ بدلًا من إرسال كل شيء دفعةً واحدة — وصفة المعالجة المُجمَّعة تُنفِّذ تلك الحلقة.
ملاحظات الأمان
قسم بعنوان «ملاحظات الأمان»- أبقِ الرمز الحامل في ترويسة
Authorizationفقط. لا تضعه أبدًا في سلسلة استعلام، أو سطر سجل، أو ملف مُودَع في المستودع. تستبدل النسخة المُلتقَطة أعلاه متغير بيئة لهذا السبب بالضبط. - تحقَّق من صحة البايتات المُنزَّلة قبل الوثوق بها. الخطوة 5 جزء من التدفق، لا إضافة اختيارية: تأكَّد أن الاستجابة ملف PDF (ترويسة
%PDFكحدّ أدنى، وqpdf --checkللبِنية) قبل أرشفتها أو إعادة توجيهها. - احذف المهام المكتملة التي لم تعد بحاجة إليها. تُزيل الخطوة 6 النتيجة المخزَّنة من الخادم؛ وإلا تبقى المهمة المكتملة قابلة للتنزيل حتى يُزيلها جامع نفايات المهام لدى الخادم.
- استخدِم مفتاحًا بأقل امتياز. يحتاج هذا التدفق مفتاح عرض من المستوى الأساسي ولا شيء أكثر.
المطابقة
قسم بعنوان «المطابقة»لا تطرح هذه الوصفة أي ادعاء معياري مُلزِم. إنها تستخدم نقاط نهاية REST للمهام غير المتزامنة في Connect وتقرأ حقول سجل المهمة التي يُعرِّفها الخادم. تؤكد خطوة qpdf --check السلامة البنيوية فقط — وعبارة “the file may still contain errors that qpdf cannot detect” هي تحذير qpdf نفسه، مُقتبَسًا حرفيًا أعلاه. أما تحديد المطابقة لمعيار (PDF/A-4، PDF/UA) فهو مهمة مُدقِّق مستقل، وسطح مختلف — راجِع إجراء فحص لمعيار مُسمّى لأجل ذلك الحدّ.
اطّلِع أيضًا
قسم بعنوان «اطّلِع أيضًا»- توليد ملفات PDF مُجمَّعة مع تتبُّع التقدُّم — سطح المهام نفسه مُشغَّلًا كدفعة بتزامن محدود.
- ولِّد أول ملف PDF لك — أصغر عملية عرض عبر Connect.
- أدِر جلسة مستند وكيل عبر MCP — المحرك نفسه، أداةً بأداة، عبر وسيلة نقل
stdioفي MCP. - اصطلاحات وصفات Connect — عقد النقل والمستوى والمطابقة الذي تتبعه كل وصفة من وصفات Connect.
- معالجة الأخطاء المُدركة للاستثناءات عبر Connect — كيف تفصل فشل النقل عن الحالات غير الناجحة.