PDF API के लिए TypeScript SDK: पहली कॉल तक पाँच मिनट, और जो काम वह आप पर छोड़ता है
TypeScript से @pdf123/sdk इस्तेमाल करके फ़ाइलें मिलाएँ, वॉटरमार्क जोड़ें, फ़ाइल की जानकारी पढ़ें और कई उपकरणों को एक ही अनुरोध में जोड़ें। उपकरण या पैरामीटर के नाम में ग़लती हो तो वह कुछ भी अपलोड होने से पहले पकड़ लेता है; सर्वर के अस्वीकार करने पर कारण और संकेत मिलते हैं, जबकि पुनः प्रयास, रद्द करना और बैच में आंशिक विफलताएँ आपके ज़िम्मे हैं।

Node से PDF API को कॉल करते समय @pdf123/sdk सॉफ़्टवेयर डेवलपमेंट किट (SDK) उपकरण या पैरामीटर के नाम की ग़लती किसी भी फ़ाइल के अपलोड होने से पहले पकड़ लेता है। वह न पुनः प्रयास करता है, न आपके लिए रद्द करता है, न नतीजों को स्ट्रीम करता है। new Pdf123Client() डिफ़ॉल्ट रूप से बिना पहचान के https://pdf123.xyz की ओर इशारा करता है; आपकी फ़ाइलें वहीं अपलोड होती हैं और वहीं प्रोसेस होती हैं, क्योंकि कोई स्थानीय इंजन नहीं है।
फ़ोल्डर में दो PDF हों तो मिलाने के लिए काफ़ी हैं
आपको Node 20.3 या उससे नया संस्करण, या Bun चाहिए। पैकेज एक ES मॉड्यूल है: कोड को .mjs फ़ाइल में रखें, या पहले npm pkg set type=module चलाएँ। a.pdf और b.pdf को मौजूदा डायरेक्टरी में रखें।
npm install @pdf123/sdk
// merge.mjs
import { Pdf123Client } from "@pdf123/sdk";
import { readFileInput, saveResult } from "@pdf123/sdk/node";
const client = new Pdf123Client();
const merged = await client.run("merge", {
input: [await readFileInput("a.pdf"), await readFileInput("b.pdf")],
});
console.log(await saveResult(merged, { output: "merged.pdf" }));
node merge.mjs चलाने के बाद टर्मिनल वही आउटपुट पथ छापता है जो आपने दिया था, merged.pdf, और फ़ाइल मौजूदा डायरेक्टरी में होती है। readFileInput और saveResult @pdf123/sdk/node में हैं। रूट एंट्री केवल fetch पर निर्भर है और इनपुट सादे { name, data } के रूप में लेती है, इसलिए बिना फ़ाइल सिस्टम वाला वातावरण भी वही क्लाइंट इस्तेमाल कर सकता है। नतीजा { data, contentType, filename, json } होता है; data एक पूरा Uint8Array है, और Buffer.from(result.data) आपको Buffer देता है।
PDF मिलाएँ बस एक उपकरण है।
फ़ाइलें data में आती हैं, रिपोर्टें json में
हर उपकरण client.run(toolName, { input, params }) से चलता है। नीचे का कोड पिछले खंड के merge.mjs को आगे बढ़ाता है, जिसमें client और saveResult पहले से मौजूद हैं। नतीजा कहाँ से पढ़ना है, यह getTool के returns फ़ील्ड पर निर्भर है, जो "file" या "json" होता है: पहले के लिए data, दूसरे के लिए json इस्तेमाल करें।
import { getTool } from "@pdf123/sdk";
import { readFileInput, saveResult } from "@pdf123/sdk/node";
// client पिछले खंड के merge.mjs से आता है
const marked = await client.run("watermark", {
input: await readFileInput("a.pdf"),
params: { watermarkText: "DRAFT", fontSize: 40 },
});
console.log(await saveResult(marked, { output: "marked.pdf" }));
const info = await client.run("get-info", { input: await readFileInput("a.pdf") });
console.log(info.json.FileSize, info.json.Encrypted);
console.log(getTool("get-info")?.returns); // "json"
पहला ब्लॉक वॉटरमार्क लगी फ़ाइल को marked.pdf में लिखता है; दूसरे में info.json रिपोर्ट है। फ़ील्ड के नाम, डिफ़ॉल्ट या मान्य मान याद रखने की ज़रूरत नहीं: getTool("watermark")?.fields वही सूची है, और कमांड लाइन पर pdfx describe watermark भी उसी को पढ़ता है। TOOLS में सभी 95 उपकरण हैं। पूरा संदर्भ डेवलपर पेज पर SDK गाइड में है।
मिलाकर, फिर वॉटरमार्क लगाकर, फिर संपीड़ित करने के लिए आप pipeline या लगातार तीन run कॉल इस्तेमाल कर सकते हैं; कौन-सा, यह इस पर निर्भर है कि आपको बीच की फ़ाइलें चाहिए या नहीं। यहाँ भी यह ऊपर के client से आगे चलता है। pipeline तीनों चरणों को एक अनुरोध में समेट देता है, बीच के नतीजे सर्वर पर ही रहते हैं, और कॉल करने वाले को केवल आख़िरी चरण मिलता है, जिसे नीचे का कोड out.pdf में लिखता है। अगर हर चरण की फ़ाइल चाहिए, तो उन्हें अलग-अलग कॉल करें।
const result = await client.pipeline(
[{ tool: "merge" }, { tool: "watermark", params: { watermarkText: "DRAFT" } }, { tool: "compress" }],
[await readFileInput("a.pdf"), await readFileInput("b.pdf")],
);
console.log(await saveResult(result, { output: "out.pdf" }));
एक पाइपलाइन में अधिकतम 8 चरण होते हैं। 9वें चरण पर HTTP 400: at most 8 pipeline steps allowed मिलता है।
बिना पहचान के, कुंजी के साथ, या अपने सर्वर की ओर
अगर आपने PDFX_API_BASE सेट किया और फिर भी कॉल https://pdf123.xyz पर जा रही है, तो इसकी वजह यह है कि new Pdf123Client() पर्यावरण चर नहीं पढ़ता; वह केवल अपने कंस्ट्रक्टर के विकल्प देखता है।
बिना पहचान की कॉल के लिए इस कंस्ट्रक्टर को जैसा है वैसा इस्तेमाल करें। कुंजी के साथ new Pdf123Client({ apiKey: process.env.PDFX_API_KEY }) लिखें; अनुरोध का हेडर X-API-KEY है।
अपने सर्वर की ओर इशारा करने के लिए @pdf123/sdk/node का clientFromEnv() इस्तेमाल करें। वह PDFX_API_BASE और PDFX_API_KEY पढ़ता है। आप सीधे new Pdf123Client({ baseUrl: "http://localhost:8080" }) भी लिख सकते हैं। पता पहुँच से बाहर हो तो कॉल विफल होती है; ऑफ़लाइन मोड नहीं है। फ़ाइलें अपने नेटवर्क में रखने पर क्या मिलता है और उसकी क्या क़ीमत है, यह स्वयं-होस्टिंग असल में क्या देती है (और इसकी क़ीमत क्या है) में है।
ग़लत पैरामीटर अपलोड से पहले पकड़ा जाता है
इसके बिना फ़ाइल पहले अपलोड होती और फ़ील्ड के नाम की वर्तनी की ग़लती सर्वर के 400 के रूप में ही सामने आती। हर उपकरण के पैरामीटर के प्रकार सूची से बनाए जाते हैं, इसलिए तीन आम चूकें tsc के चरण पर ही विफल हो जाती हैं:
| आप क्या लिखते हैं | कंपाइलर की त्रुटि (अंश) |
|---|---|
client.run("rotat", { input }) |
Argument of type '"rotat"' is not assignable to parameter of type 'ToolId' |
params: { watermarkTxt: "DRAFT" } |
Object literal may only specify known properties, but 'watermarkTxt' does not exist, अंत में Did you mean to write 'watermarkText'? |
params: { angle: 45 } (rotate केवल 90, 180, 270 लेता है) |
Type '45' is not assignable to type …, उसके बाद मान्य मान |
{ angle: 90 } या { watermarkText: "DRAFT", fontSize: 30 } जैसी सही कॉल कंपाइल हो जाती हैं। संख्यात्मक फ़ील्ड संख्या और स्ट्रिंग दोनों लेते हैं, इसलिए fontSize: 30 और fontSize: "30" बराबर हैं। जो प्रोजेक्ट TypeScript इस्तेमाल नहीं करता, उसे वही त्रुटियाँ रनटाइम पर मिलती हैं, और अनुरोध कभी भेजा नहीं जाता: तीसरे मामले में Field "angle" of "rotate" must be one of: 90, 180, 270 आता है, और उपकरण के नाम में ग़लती पर unknown_tool आता है, जिसके संदेश में मिलते-जुलते नाम सूचीबद्ध होते हैं।
सर्वर अस्वीकार करे तो reason पर शाखा बनाएँ
जब सर्वर किसी अनुरोध को अस्वीकार करता है, SDK status, code, reason और problem.hint के साथ Pdf123Error फेंकता है। एक code के कई reason मान हो सकते हैं, इसलिए पहले reason जाँचें और फिर code पर लौटें। तीन आम विफलताएँ, उसी इनपुट के साथ स्थानीय रूप से चलाने पर, ऐसी दिखती हैं:
| इनपुट | status | code | reason | hint (मूल अंग्रेज़ी में है) |
|---|---|---|---|---|
| एन्क्रिप्टेड PDF, पासवर्ड नहीं दिया | 400 | bad_request |
password_required |
दस्तावेज़ का पासवर्ड दें, या पहले PDF को अनलॉक करें |
| ग़लत पासवर्ड | 400 | bad_request |
wrong_password |
पासवर्ड जाँचें और फिर कोशिश करें |
| PDF नहीं है, या फ़ाइल क्षतिग्रस्त है | 400 | invalid_document |
invalid_pdf |
एक मान्य, सही-सलामत PDF अपलोड करें; आप पहले उसे सुधारने की कोशिश कर सकते हैं |
import { Pdf123Client, Pdf123Error } from "@pdf123/sdk";
import { readFileInput } from "@pdf123/sdk/node";
const client = new Pdf123Client();
const input = await readFileInput("locked.pdf");
try {
await client.run("compress", { input });
} catch (error) {
if (!(error instanceof Pdf123Error)) throw error;
console.error(error.status, error.code, error.reason, error.problem?.hint);
if (error.reason === "password_required") {
await client.run("compress", { input, password: "secret" });
}
}
पासवर्ड पता हो तो एक फ़ाइल के लिए password दें; अनलॉक करना और लक्ष्य उपकरण एक ही अनुरोध में होते हैं। क्षतिग्रस्त फ़ाइल के लिए पहले PDF सुधारें आज़माएँ।
"कोई नतीजा नहीं" के मामलों को अलग पहचानना ज़रूरी है। जब PDF से CSV को PDF में कोई तालिका नहीं मिलती, सर्वर 204 लौटाता है और SDK code: "no_content" फेंकता है; इसका कारण खाली तालिका निर्यात (204): आपकी PDF में शायद कोई स्तंभ ही नहीं में है। शर्त वाले फ़िल्टर उपकरण झूठी शर्त को सामान्य नतीजा मानते हैं: वे कुछ फेंकते नहीं, और खाली data के साथ matched: false लौटाते हैं।
पुनः प्रयास, मेमोरी और रद्द करना कॉल करने वाले का काम है
नेटवर्क त्रुटियाँ और 5xx जवाब जैसे हैं वैसे ही फेंके जाते हैं; SDK अपने आप कभी पुनः प्रयास नहीं करता। जो अनुरोध स्थिति बदलता है, उसके लिए अपनी idempotencyKey दें: उसी कुंजी से दोबारा भेजने पर दोबारा प्रोसेस करने के बजाय पहला नतीजा लौटता है, और तंत्र तथा सीमाएँ Idempotency-Key: PDF कार्यों के लिए सुरक्षित पुनः प्रयास में हैं। नतीजे पूरे मेमोरी में पढ़े जाते हैं, स्ट्रीमिंग नहीं है। जब इनपुट और आउटपुट दोनों बड़े हों, तो गिनें कि वे एक साथ कितनी मेमोरी घेरते हैं।
रद्द करना और टाइमआउट दो अलग code मान हैं, और एक कॉल में जो पहले होता है, केवल वही रिपोर्ट होता है। नीचे का उदाहरण इन्हें दो अलग कॉल में परखता है, ताकि दोनों शाखाएँ सच में चल सकें:
import { Pdf123Client, Pdf123Error } from "@pdf123/sdk";
import { readFileInput } from "@pdf123/sdk/node";
const client = new Pdf123Client();
const input = await readFileInput("a.pdf");
const controller = new AbortController();
setTimeout(() => controller.abort(), 10_000);
try {
await client.run("compress", { input, signal: controller.signal });
} catch (error) {
if (error instanceof Pdf123Error && error.code === "cancelled") { /* आपने इसे रद्द किया */ }
}
try {
await client.run("compress", { input, timeoutMs: 60_000 });
} catch (error) {
if (error instanceof Pdf123Error && error.code === "timeout") { /* timeoutMs पार हुआ; इस बार कोई signal नहीं */ }
}
जैसे ही रद्द करने का संकेत (AbortSignal) चालू होता है, अनुरोध code: "cancelled" के साथ अस्वीकार हो जाता है; टाइमआउट पार होने पर code: "timeout" मिलता है। हर अनुरोध का डिफ़ॉल्ट टाइमआउट 5 मिनट है, जिसे आप क्लाइंट बनाते समय बदल सकते हैं या हर कॉल में timeoutMs से ओवरराइड कर सकते हैं। टुकड़ों में अपलोड में हर अनुरोध यह टाइमआउट अलग से लेकर चलता है। रद्द करना केवल इस कनेक्शन को बंद करता है; इसकी कोई गारंटी नहीं कि सर्वर प्रोसेसिंग रोक दे।
एक फ़ाइल विफल हो, बाक़ी चलती रहें, और कॉलबैक पूरा होने के क्रम में आएँ
एकल-फ़ाइल उपकरण को इनपुटों का बैच देने के लिए runBatch इस्तेमाल करें। एक फ़ाइल विफल हो तो बाक़ी चलती रहती हैं। onResult हर फ़ाइल के पूरा होते ही बुलाया जाता है, इसलिए वह पूरा होने के क्रम में चलता है, और index इनपुट सरणी में फ़ाइल की स्थिति है; लौटाई गई सरणी हमेशा इनपुट के क्रम में होती है। बाइट इसी कॉलबैक के भीतर डिस्क पर लिखें: कॉलबैक लौटते ही retainData: false data को ख़ाली कर देता है, इसलिए जिस नतीजे को आप यहाँ saveResult नहीं करते वह चला जाता है। अगर चलना बीच में रुक जाए, तो पहले लिखी जा चुकी फ़ाइलें बनी रहती हैं।
मान लें फ़ोल्डर में तीन सही PDF हैं, a.pdf, b.pdf और एन्क्रिप्टेड locked.pdf (पासवर्ड secret), साथ ही एक टूटी हुई फ़ाइल broken.pdf है जिसमें बस कुछ टाइप किए अक्षर हैं:
import { Pdf123Client } from "@pdf123/sdk";
import { fileSource, saveResult } from "@pdf123/sdk/node";
const client = new Pdf123Client();
const results = await client.runBatch("compress", [
fileSource("a.pdf"), fileSource("broken.pdf"), fileSource("b.pdf"), fileSource("locked.pdf"),
], {
concurrency: 2,
passwordFor: (file) => (file.name === "locked.pdf" ? "secret" : undefined),
onResult: async (entry, index) => {
console.log(index, entry.input.name, entry.ok ? "ok" : entry.error.reason);
if (entry.ok) await saveResult(entry.result, { output: "out/" });
},
retainData: false,
});
डिफ़ॉल्ट रूप से एक समय में दो फ़ाइलें प्रोसेस होती हैं; इसे concurrency से बदलें। fileSource(path) फ़ाइल को डिस्क से तभी पढ़ता है जब उसकी बारी आती है, इसलिए बड़ी फ़ाइलों का बड़ा बैच कभी एक साथ मेमोरी में नहीं होता। passwordFor से लॉक और बिना लॉक वाली फ़ाइलों को मिलाने वाला बैच एक ही बार में चल जाता है। ऊपर की चारों फ़ाइलों को स्थानीय रूप से चलाने पर कॉलबैक को यह मिला:
1 broken.pdf invalid_pdf
0 a.pdf ok
2 b.pdf ok
3 locked.pdf ok
पूरा होने का क्रम हर बार अलग हो सकता है; यह बस एक बार का नतीजा है। retainData: false लौटाई गई सरणी में data को ख़ाली कर देता है; सफल फ़ाइलें ऊपर के saveResult से पहले ही out/ में लिखी जा चुकी थीं। जब आप runBatch को idempotencyKey देते हैं, तो हर फ़ाइल के लिए असल में भेजी जाने वाली कुंजी <key>:<index> होती है।
टुकड़ों में अपलोड केवल कुल 95 MiB से ऊपर शुरू होता है
सीधे अनुरोध की बॉडी की सीमा 100 MiB है, और उससे बड़ी को अस्वीकार कर दिया जाता है; विवरण के लिए बड़ी PDF अपलोड के अस्वीकार होने पर: 100 MiB की अनुरोध-निकाय सीमा और वह त्रुटि जो ग़लत दिशा दिखाती है देखें। जब एक अनुरोध की सभी फ़ाइलें मिलाकर 95 MiB से ज़्यादा हों, तो SDK टुकड़ों में अपलोड पर चला जाता है, और आपकी client.run कॉल नहीं बदलती। एक अपलोड के लिए सर्वर की डिफ़ॉल्ट अधिकतम सीमा 500 MiB है; स्वयं होस्ट करने पर इसे PDFX_UPLOAD_MAX_BYTES से बदलें।
यही ऑपरेशन curl, MCP और कमांड लाइन में कैसे लिखा जाता है, इसके लिए वही प्रचालन, चार क्लाइंट: ब्राउज़र, curl, MCP, pdfx देखें: वह लेख चारों कॉल साथ-साथ रखता है, और यह लेख केवल SDK को विस्तार से बताता है। npm पर पैकेज का पेज @pdf123/sdk है।