feat: add official Node.js and Python SDKs
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled

- Node.js SDK (sdk/nodejs/): TypeScript, zero deps, native fetch, Node 18+
- Python SDK (sdk/python/): sync + async clients via httpx, Python 3.8+
- Both wrap all conversion endpoints (html, markdown, url, templates)
- Proper error handling with DocFastError
- Full README documentation for each
This commit is contained in:
DocFast Bot 2026-02-20 20:25:43 +00:00
parent 45b5be248c
commit 2e29d564ab
9 changed files with 547 additions and 0 deletions

103
sdk/python/README.md Normal file
View file

@ -0,0 +1,103 @@
# DocFast Python SDK
Official Python client for the [DocFast](https://docfast.dev) HTML-to-PDF API.
## Install
```bash
pip install docfast
```
Requires Python 3.8+.
## Quick Start
```python
from docfast import DocFast
client = DocFast("df_pro_your_api_key")
# HTML to PDF
pdf = client.html("<h1>Hello World</h1>")
with open("output.pdf", "wb") as f:
f.write(pdf)
# Markdown to PDF
pdf = client.markdown("# Hello\n\nThis is **bold**.")
# URL to PDF
pdf = client.url("https://example.com")
```
## Async Usage
```python
from docfast import AsyncDocFast
async with AsyncDocFast("df_pro_your_api_key") as client:
pdf = await client.html("<h1>Hello</h1>")
```
## API
### `DocFast(api_key, *, base_url=None)`
Create a synchronous client. Use as a context manager or call `client.close()`.
### `AsyncDocFast(api_key, *, base_url=None)`
Create an async client. Use as an async context manager.
### Conversion Methods
All methods return PDF bytes and accept optional keyword arguments:
| Method | Input | Description |
|--------|-------|-------------|
| `client.html(html, **opts)` | HTML string | Convert HTML to PDF |
| `client.markdown(markdown, **opts)` | Markdown string | Convert Markdown to PDF |
| `client.url(url, **opts)` | URL string | Convert webpage to PDF |
| `client.templates()` | — | List available templates |
| `client.render_template(id, data, **opts)` | Template ID + data dict | Render template to PDF |
### PDF Options
Pass as keyword arguments to any conversion method:
```python
pdf = client.html(
"<h1>Report</h1>",
format="A4",
landscape=True,
margin={"top": "20mm", "bottom": "20mm"},
print_background=True,
)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `format` | str | `"A4"` | Page size: A4, Letter, Legal, A3, A5, Tabloid |
| `landscape` | bool | `False` | Landscape orientation |
| `margin` | dict | — | `{top, bottom, left, right}` in CSS units |
| `header` | dict | — | `{content, height}` for page header |
| `footer` | dict | — | `{content, height}` for page footer |
| `scale` | float | `1.0` | Render scale |
| `print_background` | bool | `False` | Include background colors/images |
## Error Handling
```python
from docfast import DocFast, DocFastError
client = DocFast("df_pro_your_api_key")
try:
pdf = client.html("<h1>Test</h1>")
except DocFastError as e:
print(e) # "Invalid API key"
print(e.status) # 403
```
## License
MIT

View file

@ -0,0 +1,6 @@
"""DocFast — Official Python SDK for the HTML-to-PDF API."""
from .client import DocFast, AsyncDocFast, DocFastError
__all__ = ["DocFast", "AsyncDocFast", "DocFastError"]
__version__ = "0.1.0"

View file

@ -0,0 +1,148 @@
"""DocFast API clients (sync and async)."""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from urllib.parse import quote
import httpx
class DocFastError(Exception):
"""Error returned by the DocFast API."""
def __init__(self, message: str, status: int, code: Optional[str] = None):
super().__init__(message)
self.status = status
self.code = code
_KEY_MAP = {"print_background": "printBackground"}
def _build_body(key: str, value: str, options: Optional[Dict[str, Any]]) -> Dict[str, Any]:
body: Dict[str, Any] = {key: value}
if options:
body["options"] = {_KEY_MAP.get(k, k): v for k, v in options.items()}
return body
def _handle_error(response: httpx.Response) -> None:
if response.is_success:
return
message = f"HTTP {response.status_code}"
code = None
try:
data = response.json()
if "error" in data:
message = data["error"]
code = data.get("code")
except Exception:
pass
raise DocFastError(message, response.status_code, code)
class DocFast:
"""Synchronous DocFast client."""
def __init__(self, api_key: str, *, base_url: Optional[str] = None):
if not api_key:
raise ValueError("API key is required")
self._base_url = (base_url or "https://docfast.dev").rstrip("/")
self._client = httpx.Client(
base_url=self._base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=120.0,
)
def __enter__(self) -> "DocFast":
return self
def __exit__(self, *args: Any) -> None:
self.close()
def close(self) -> None:
self._client.close()
def _convert(self, path: str, body: Dict[str, Any]) -> bytes:
r = self._client.post(path, json=body)
_handle_error(r)
return r.content
def html(self, html: str, **options: Any) -> bytes:
"""Convert HTML to PDF."""
return self._convert("/v1/convert/html", _build_body("html", html, options or None))
def markdown(self, markdown: str, **options: Any) -> bytes:
"""Convert Markdown to PDF."""
return self._convert("/v1/convert/markdown", _build_body("markdown", markdown, options or None))
def url(self, url: str, **options: Any) -> bytes:
"""Convert a URL to PDF."""
return self._convert("/v1/convert/url", _build_body("url", url, options or None))
def templates(self) -> List[Dict[str, Any]]:
"""List available templates."""
r = self._client.get("/v1/templates")
_handle_error(r)
return r.json()
def render_template(self, template_id: str, data: Dict[str, Any], **options: Any) -> bytes:
"""Render a template to PDF."""
body: Dict[str, Any] = {"data": data}
if options:
body["options"] = options
return self._convert(f"/v1/templates/{quote(template_id, safe='')}/render", body)
class AsyncDocFast:
"""Asynchronous DocFast client."""
def __init__(self, api_key: str, *, base_url: Optional[str] = None):
if not api_key:
raise ValueError("API key is required")
self._base_url = (base_url or "https://docfast.dev").rstrip("/")
self._client = httpx.AsyncClient(
base_url=self._base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=120.0,
)
async def __aenter__(self) -> "AsyncDocFast":
return self
async def __aexit__(self, *args: Any) -> None:
await self.close()
async def close(self) -> None:
await self._client.aclose()
async def _convert(self, path: str, body: Dict[str, Any]) -> bytes:
r = await self._client.post(path, json=body)
_handle_error(r)
return r.content
async def html(self, html: str, **options: Any) -> bytes:
"""Convert HTML to PDF."""
return await self._convert("/v1/convert/html", _build_body("html", html, options or None))
async def markdown(self, markdown: str, **options: Any) -> bytes:
"""Convert Markdown to PDF."""
return await self._convert("/v1/convert/markdown", _build_body("markdown", markdown, options or None))
async def url(self, url: str, **options: Any) -> bytes:
"""Convert a URL to PDF."""
return await self._convert("/v1/convert/url", _build_body("url", url, options or None))
async def templates(self) -> List[Dict[str, Any]]:
"""List available templates."""
r = await self._client.get("/v1/templates")
_handle_error(r)
return r.json()
async def render_template(self, template_id: str, data: Dict[str, Any], **options: Any) -> bytes:
"""Render a template to PDF."""
body: Dict[str, Any] = {"data": data}
if options:
body["options"] = options
return await self._convert(f"/v1/templates/{quote(template_id, safe='')}/render", body)

View file

26
sdk/python/pyproject.toml Normal file
View file

@ -0,0 +1,26 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "docfast"
version = "0.1.0"
description = "Official Python client for the DocFast HTML-to-PDF API"
readme = "README.md"
license = "MIT"
requires-python = ">=3.8"
authors = [{ name = "DocFast", email = "support@docfast.dev" }]
keywords = ["pdf", "html-to-pdf", "markdown-to-pdf", "docfast", "api"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Libraries",
]
dependencies = ["httpx>=0.24.0"]
[project.urls]
Homepage = "https://docfast.dev"
Documentation = "https://docfast.dev/docs"
Repository = "https://git.cloonar.com/openclawd/docfast"