Development Guide ​
This guide covers setting up and developing the Chromium PDF Service locally.
Prerequisites ​
- Node.js >= 24.0.0
- npm or yarn
- Docker (optional, for containerized development)
Local Setup ​
1. Clone and Install ​
bash
git clone https://github.com/chromium-pdf/chromium-pdf-service.git
cd chromium-pdf-service
# Install dependencies
npm install
# Install Playwright browsers
npx playwright install chromium2. Environment Setup ​
bash
# Copy example environment file
cp .env.example .env3. Run Development Server ​
bash
npm run devThe service starts at http://localhost:3000 with hot-reload enabled.
Available Scripts ​
| Command | Description |
|---|---|
npm run dev | Start development server with hot-reload |
npm run build | Compile TypeScript to JavaScript |
npm start | Run production build |
npm run lint | Run ESLint |
npm run lint:fix | Fix ESLint issues automatically |
npm run format | Format code with Prettier |
npm run format:check | Check code formatting |
npm run typecheck | Run TypeScript type checking |
npm test | Run tests once |
npm run test:watch | Run tests in watch mode |
npm run test:ui | Open Vitest UI |
npm run test:coverage | Run tests with coverage report |
npm run docs:dev | Start documentation dev server |
npm run docs:build | Build documentation |
Project Structure ​
chromium-pdf-service/
├── src/
│ ├── index.ts # Entry point
│ ├── app.ts # Fastify app setup
│ ├── config/
│ │ ├── env.ts # Environment variables
│ │ └── default-settings.ts
│ ├── routes/
│ │ ├── pdf.routes.ts # PDF generation endpoints
│ │ ├── screenshot.routes.ts # Screenshot endpoints
│ │ ├── status.routes.ts # Job status endpoints
│ │ ├── health.routes.ts # Health check endpoints
│ │ └── settings.routes.ts
│ ├── services/
│ │ ├── pdf-generator.ts # Core PDF generation
│ │ ├── screenshot-generator.ts # Screenshot generation
│ │ ├── queue-manager.ts # Job queue management
│ │ └── settings-manager.ts
│ ├── middleware/
│ │ ├── error-handler.ts
│ │ └── auth.ts # API key authentication
│ ├── schemas/ # Zod validation schemas
│ ├── types/ # TypeScript types
│ └── utils/
│ ├── logger.ts
│ ├── filename.ts
│ ├── url-validator.ts
│ └── html-sanitizer.ts
├── tests/ # Test files
├── docs/ # VitePress documentation
├── data/ # Runtime data (settings, queue)
├── pdf-files/ # Generated PDFs/screenshots
└── logs/ # Log filesTesting ​
Run All Tests ​
bash
npm testRun Tests in Watch Mode ​
bash
npm run test:watchRun Tests with UI ​
bash
npm run test:uiRun Tests with Coverage ​
bash
npm run test:coverageTest Structure ​
txt
tests/
├── routes/ # Route/endpoint tests
├── services/ # Service layer tests
├── middleware/ # Middleware tests
├── schemas/ # Schema validation tests
└── utils/ # Utility function testsCode Quality ​
Linting ​
bash
# Check for issues
npm run lint
# Auto-fix issues
npm run lint:fixFormatting ​
bash
# Format all files
npm run format
# Check formatting
npm run format:checkType Checking ​
bash
npm run typecheckAPI Documentation ​
Swagger UI is available in development mode:
txt
http://localhost:3000/docsDebugging ​
Enable Debug Logging ​
bash
LOG_LEVEL=debug npm run devView Logs ​
Logs are written to:
- Console (pretty-printed in development)
logs/directory (JSON format)
Using launchOptions for Browser Debugging ​
When debugging PDF or screenshot generation issues, you can pass custom browser launchOptions per request to see what's happening in the browser. This is especially useful for:
Visual Debugging with Non-Headless Mode:
Running the browser in non-headless mode lets you see exactly what the page looks like before PDF/screenshot generation:
bash
curl -X POST http://localhost:3000/api/pdf/from-url \
-H "Content-Type: application/json" \
-d '{
"requestedKey": "debug-test",
"url": "https://example.com",
"options": {
"browser": {
"launchOptions": {
"headless": false
}
}
}
}'Common Debugging Scenarios:
- Layout Issues: Use
headless: falseto visually inspect page rendering before PDF generation - Font Problems: Add
--disable-font-subpixel-positioningto args for consistent font rendering - Security Errors: Use
--no-sandboxand--disable-setuid-sandboxin containerized environments - Network Issues: Add
--disable-web-security(development only!) to bypass CORS restrictions - Performance Testing: Use
--single-processto simplify debugging
Example with Multiple Debug Args:
json
{
"requestedKey": "debug-complex",
"html": "<h1>Test Page</h1>",
"options": {
"browser": {
"launchOptions": {
"headless": false,
"args": [
"--window-size=1920,1080",
"--disable-gpu",
"--no-sandbox",
"--disable-setuid-sandbox"
]
},
"waitAfter": 2000
}
}
}Important Notes:
- Custom
launchOptionscreate a dedicated browser instance for that job - The browser is automatically closed after job completion
- Without custom
launchOptions, jobs use the shared browser instance - Non-headless mode is not recommended for production use
- Some args like
--no-sandboxreduce security and should only be used in development
Making Changes ​
Adding a New Route ​
- Create route file in
src/routes/ - Define Zod schemas in
src/schemas/ - Register route in
src/app.ts - Add tests in
tests/routes/
Adding a New Service ​
- Create service file in
src/services/ - Export singleton instance
- Add tests in
tests/services/
Adding Environment Variables ​
- Add to
src/config/env.ts - Update
.env.example - Update
docs/config/env-variables.md