Skip to content

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 chromium

2. Environment Setup ​

bash
# Copy example environment file
cp .env.example .env

3. Run Development Server ​

bash
npm run dev

The service starts at http://localhost:3000 with hot-reload enabled.

Available Scripts ​

CommandDescription
npm run devStart development server with hot-reload
npm run buildCompile TypeScript to JavaScript
npm startRun production build
npm run lintRun ESLint
npm run lint:fixFix ESLint issues automatically
npm run formatFormat code with Prettier
npm run format:checkCheck code formatting
npm run typecheckRun TypeScript type checking
npm testRun tests once
npm run test:watchRun tests in watch mode
npm run test:uiOpen Vitest UI
npm run test:coverageRun tests with coverage report
npm run docs:devStart documentation dev server
npm run docs:buildBuild 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 files

Testing ​

Run All Tests ​

bash
npm test

Run Tests in Watch Mode ​

bash
npm run test:watch

Run Tests with UI ​

bash
npm run test:ui

Run Tests with Coverage ​

bash
npm run test:coverage

Test Structure ​

txt
tests/
├── routes/           # Route/endpoint tests
├── services/         # Service layer tests
├── middleware/       # Middleware tests
├── schemas/          # Schema validation tests
└── utils/            # Utility function tests

Code Quality ​

Linting ​

bash
# Check for issues
npm run lint

# Auto-fix issues
npm run lint:fix

Formatting ​

bash
# Format all files
npm run format

# Check formatting
npm run format:check

Type Checking ​

bash
npm run typecheck

API Documentation ​

Swagger UI is available in development mode:

txt
http://localhost:3000/docs

Debugging ​

Enable Debug Logging ​

bash
LOG_LEVEL=debug npm run dev

View 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:

  1. Layout Issues: Use headless: false to visually inspect page rendering before PDF generation
  2. Font Problems: Add --disable-font-subpixel-positioning to args for consistent font rendering
  3. Security Errors: Use --no-sandbox and --disable-setuid-sandbox in containerized environments
  4. Network Issues: Add --disable-web-security (development only!) to bypass CORS restrictions
  5. Performance Testing: Use --single-process to 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 launchOptions create 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-sandbox reduce security and should only be used in development

Making Changes ​

Adding a New Route ​

  1. Create route file in src/routes/
  2. Define Zod schemas in src/schemas/
  3. Register route in src/app.ts
  4. Add tests in tests/routes/

Adding a New Service ​

  1. Create service file in src/services/
  2. Export singleton instance
  3. Add tests in tests/services/

Adding Environment Variables ​

  1. Add to src/config/env.ts
  2. Update .env.example
  3. Update docs/config/env-variables.md

Released under the MIT License.