stabilność: Beta
Renderowanie faktury od początku do końca przez REST
W skrócie
Dział zatytułowany „W skrócie”Przeprowadź jedną fakturę z formatu JSON do zweryfikowanego lokalnie pliku
PDF przez powierzchnię Representational State Transfer (REST) NextPDF
Connect, jedna wymiana po drugiej. Ten przepis przesyła zadanie
renderowania do POST /api/v1/jobs, powtarza przesłanie pod tym samym
kluczem Idempotency-Key, aby pokazać ścieżkę bezpieczną wobec
duplikatów, odpytuje GET /api/v1/jobs/{id}, pobiera plik PDF z
GET /api/v1/jobs/{id}/result, sprawdza bajty za pomocą qpdf --check
i usuwa ukończone zadanie.
Każda odpowiedź poniżej to dosłowny zapis z rzeczywistego wdrożenia
Connect w edycji core (nextpdf/server pod RoadRunner, powiązany z
http://localhost:8080). Jedynym podstawieniem jest klucz API,
przedstawiony jako zmienna środowiskowa $NEXTPDF_CONNECT_TOKEN;
identyfikatory zadań, identyfikatory żądań, znaczniki czasu, nagłówki i
bajty ciała są dokładnie tym, co zwrócił serwer. Wartości nagłówków,
takie jak Date i X-Request-Id, będą oczywiście inne w Twoim
wdrożeniu.
Ten przepis obsługuje pojedynczy dokument, aby można było przeczytać
każdą wymianę w całości. W przypadku wielu dokumentów, ograniczonej
współbieżności oraz pętli odpytywania sterowanych nagłówkiem
Retry-After zobacz
Wsadowe generowanie plików PDF ze śledzeniem postępu,
które wykorzystuje tę samą powierzchnię zadań.
Instalacja
Dział zatytułowany „Instalacja”Strona serwera to standardowa dystrybucja Connect:
composer require nextpdf/serverStroną kliencką tego przepisu jest curl oraz qpdf, więc można go
przenieść do dowolnego klienta HTTP. Najpierw wyeksportuj wartości
swojego wdrożenia:
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:Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”Asynchroniczna powierzchnia zadań oddziela przesłanie od pobrania:
przesyłasz żądanie renderowania, otrzymujesz rekord zadania i pobierasz
wynik, gdy zadanie osiągnie stan completed. Samo żądanie renderowania
to uporządkowana tablica operations — te same typy operacji
(set_font, add_text, add_table, add_image, add_page), które
stoją za wywołaniami narzędzi Connect w każdym transporcie — wraz z
polami na poziomie dokumentu (page_size, orientation, title,
author).
Dwa szczegóły kontraktu kształtują zapis, który za chwilę przeczytasz:
- Idempotentne przesłanie. Przesłanie oznaczone kluczem
Idempotency-Keyzwraca201 Createdza pierwszym razem oraz200 OKz tym samym rekordem zadania przy ponowieniu, więc ponowienie sieciowe nigdy nie renderuje dwukrotnie. - Przesłanie może być już w stanie końcowym. Bieżące wydanie
przetwarza zadanie w trybie inline przed odpowiedzią na
POST, więc odpowiedź na przesłanie może już zawieraćstatus: "completed"— jak poniżej. Kontrakt odpytywania aż do stanu końcowego to stabilny kształt API: napisz pętlę odpytywania i zaakceptuj stan końcowy przy każdej próbie, w tym pierwszej.
Możesz potwierdzić, co udostępnia Twoje wdrożenie, zanim cokolwiek
prześlesz: GET /api/v1/capabilities zwraca katalog operacji, do których
ma dostęp poziom Twojego klucza API. W przechwyconym tutaj wdrożeniu
w edycji core wymieniono wyłącznie operacje core; katalogiem
wiążącym jest zawsze własna odpowiedź działającego serwera, a nie ta
strona.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”| Wymiana | Metoda i ścieżka | Przechwycony status |
|---|---|---|
| Przesłanie zadania renderowania | POST /api/v1/jobs | 201 Created |
| Ponowienie tego samego przesłania | POST /api/v1/jobs (ten sam Idempotency-Key) | 200 OK |
| Odpytanie rekordu zadania | GET /api/v1/jobs/{id} | 200 OK |
| Pobranie pliku PDF | GET /api/v1/jobs/{id}/result | 200 OK, application/pdf |
| Usunięcie ukończonego zadania | DELETE /api/v1/jobs/{id} | 204 No Content |
Uwierzytelnianie to token bearer w każdym żądaniu /api/v1/*:
Authorization: Bearer npk_live_{kid}_{secret}. Pomyślne odpowiedzi JSON
współdzielą kopertę { "data": ..., "meta": ... }; pola, na których
działasz, znajdują się pod data.
Żądanie faktury
Dział zatytułowany „Żądanie faktury”Zapisz żądanie renderowania do pliku invoice.json. To zwykła,
deterministyczna lista operacji — pogrubiony wiersz nagłówka, wiersz
wystawienia oraz tabela pozycji:
{ "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>" } ]}Pola faktury są tutaj przykładowymi danymi. Wiążącym kształtem argumentów
dla każdej operacji jest ten zgłaszany przez Twoje wdrożenie — przez MCP
tools/list zwraca pełny schemat wejściowy dla każdego typu operacji
użytego w tym żądaniu.
Zapis od początku do końca
Dział zatytułowany „Zapis od początku do końca”1. Przesłanie zadania renderowania
Dział zatytułowany „1. Przesłanie zadania renderowania”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.jsonSerwer odpowiada 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" }}Zadanie jest już w stanie końcowym w tym zapisie — status to
"completed", a result_url jest obecny — ponieważ bieżące wydanie
renderuje w trybie inline przed odpowiedzią. Nie polegaj na tym:
potraktuj odpowiedź na przesłanie jako pierwszy wynik odpytywania i
rozgałęziaj logikę według data.status jak przy każdym innym
odpytywaniu.
2. Ponowienie przesłania (ścieżka idempotentna)
Dział zatytułowany „2. Ponowienie przesłania (ścieżka idempotentna)”Powtórz dokładnie to samo polecenie — ten sam Idempotency-Key, to samo
ciało:
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.jsonSerwer zwraca 200 OK — nie 201 — z tym samym job_id, a drugie
renderowanie się nie odbywa (porównaj meta.duration_ms z pierwszą
odpowiedzią):
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. Odpytanie rekordu zadania
Dział zatytułowany „3. Odpytanie rekordu zadania”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" }}To odpytanie pokazuje rekord w stanie końcowym, więc nie ma nagłówka
Retry-After ani pola poll_url. Dopóki zadanie jest nadal w stanie
pending lub running, serwer ustawia Retry-After (odstęp
2-sekundowy) przy każdym odpytaniu — respektuj go zamiast odpytywać w
ciasnej pętli.
4. Pobranie pliku PDF
Dział zatytułowany „4. Pobranie pliku 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 GMTCiało to plik binarny PDF — 3663 bajty w tym zapisie, zgodnie z
nagłówkiem Content-Length — i zostało tutaj pominięte. Jest zapisywane
do invoice-inv-2026-0042.pdf.
5. Lokalna weryfikacja pobranych bajtów
Dział zatytułowany „5. Lokalna weryfikacja pobranych bajtów”200 z Content-Type: application/pdf samo w sobie nie jest dowodem, że
ciało jest poprawnie sformułowanym plikiem PDF. Uruchom kontrolę
strukturalną za pomocą qpdf:
qpdf --check invoice-inv-2026-0042.pdfPrzechwycone dane wyjściowe dla pliku pobranego powyżej:
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 detectWłasne sformułowanie qpdf to uczciwa granica: to kontrola składni i strumieni, a nie ustalenie zgodności z jakimkolwiek standardem.
6. Usunięcie ukończonego zadania
Dział zatytułowany „6. Usunięcie ukończonego zadania”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 GMTPo usunięciu rekord zadania i jego zapisany wynik znikają; kolejne GET
dotyczące zadania zwraca 404.
Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”- Rozgałęziaj logikę według
data.status, a nie wyłącznie według statusu HTTP. Przesłanie, ponowienie i odpytanie zwracają2xxz rekordem zadania; stan cyklu życia znajduje się wdata.status(pending,running,completed,failed,cancelled). - Ponowiony klucz z innym ciałem to
409 Conflict. Idempotentne ponowienie200następuje tylko wtedy, gdy ciało odpowiada oryginalnemu przesłaniu. Nigdy nie używaj ponownie klucza dla innej zawartości. /resultprzed ukończeniem to409. Pobieraj dopiero po tym, jak odpytanie pokażecompleted.409to normalna odpowiedź do zbadania, a nie awaria transportu — to samo oddzielenie transportu od statusu, którym kieruje się każdy przepis Connect (zobacz Konwencje przepisów).- Zadania są ograniczone do właściciela. Zadanie przesłane pod jednym
kluczem API jest niewidoczne dla innego klucza:
GETmiędzy właścicielami zwraca404, a nie403. Odpytuj z użyciem poświadczenia, którym przesłałeś. progressmoże być nieobecne. Przechwycony rekord nie zawiera polaprogress, ponieważ zadanie było już w stanie końcowym. Gdy serwer śledzi postęp zadania, które nie jest w stanie końcowym,data.progressto liczba całkowita od 0 do 100; potraktuj brakujące pole jako nieznane, a nie zerowe.- Zadanie w stanie
failedzawieradata.error. Zarejestruj to; nie przesyłaj ponownie na oślep.
Wydajność
Dział zatytułowany „Wydajność”Jedno zadanie renderowania to jedno przesłanie, co najwyżej garść odpytań
i jedno pobranie. Przechwycone wartości meta.duration_ms opowiadają tę
historię: 63,31 ms na wyrenderowanie faktury przy przesłaniu, 1,03 ms na
idempotentne ponowienie, które nie wykonało żadnej pracy, oraz odczyty
statusu poniżej milisekundy. Odpytuj w rytmie Retry-After serwera, a
nie w ciasnej pętli; odczyt statusu jest tani, ale nie darmowy, a
ogranicznik szybkości wlicza go do budżetu (obserwuj, jak
X-Ratelimit-Remaining maleje w przechwyconych nagłówkach). W przypadku partii ograniczaj liczbę
zadań w toku, zamiast przesyłać wszystko naraz —
przepis wsadowy
implementuje tę pętlę.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”- Przechowuj token bearer wyłącznie w nagłówku
Authorization. Nigdy w ciągu zapytania, wierszu dziennika ani w zatwierdzonym pliku. Zapis powyżej podstawia zmienną środowiskową właśnie z tego powodu. - Weryfikuj pobrane bajty, zanim im zaufasz. Krok 5 jest częścią
przepływu, a nie opcjonalnym dodatkiem: sprawdź, czy odpowiedź jest
plikiem PDF (co najmniej nagłówek
%PDF,qpdf --checkdla struktury), zanim ją zarchiwizujesz lub przekażesz dalej. - Usuwaj ukończone zadania, których już nie potrzebujesz. Krok 6 usuwa zapisany wynik z serwera; w przeciwnym razie ukończone zadanie pozostaje możliwe do pobrania, dopóki nie usunie go mechanizm odśmiecania zadań serwera.
- Używaj klucza o najmniejszych uprawnieniach. Ten przepływ potrzebuje klucza renderującego w edycji core i niczego więcej.
Zgodność
Dział zatytułowany „Zgodność”Ten przepis nie formułuje żadnego normatywnego twierdzenia dotyczącego
standardów. Wykorzystuje asynchroniczne punkty końcowe REST zadań Connect
i odczytuje pola rekordu zadania definiowane przez serwer. Krok
qpdf --check potwierdza wyłącznie integralność strukturalną — „the file
may still contain errors that qpdf cannot detect” to własne zastrzeżenie
qpdf, przytoczone dosłownie powyżej. Ustalenie zgodności ze standardem
(PDF/A-4, PDF/UA) należy do niezależnego walidatora i innej powierzchni —
zobacz
Uruchom kontrolę nazwanego standardu
w kwestii tej granicy.
Zobacz też
Dział zatytułowany „Zobacz też”- Wsadowe generowanie plików PDF ze śledzeniem postępu — ta sama powierzchnia zadań obsługiwana jako partia o ograniczonej współbieżności.
- Wygeneruj swój pierwszy plik PDF — najmniejsze renderowanie Connect.
- Prowadź agentową sesję dokumentu przez MCP — ten sam silnik, narzędzie po narzędziu, przez transport stdio MCP.
- Konwencje przepisów Connect — kontrakt transportu, poziomu i zgodności, którego przestrzega każdy przepis Connect.
- Obsługa błędów świadoma wyjątków przez Connect — jak oddzielić awarie transportu od statusów niepowodzenia.