Skip to main content
A cheat sheet of the most common Komga SDK patterns. Copy, paste, and customize.

Client Setup

import { createKomgaClient } from 'komga-sdk';

const client = createKomgaClient({
  baseUrl: 'http://localhost:25600',
  auth: {
    type: 'basic',
    username: 'user@example.com',
    password: 'password',
  },
});

Domain Services

Books

import { BookService } from 'komga-sdk';
const bookService = new BookService(client);

// List books
const books = await bookService.list({ page: 0, size: 20 });

// Search books
const results = await bookService.list({
  search: { fullTextSearch: 'manga' },
  page: 0,
  size: 20,
  sort: ['metadata.title,asc'],
});

// Get by ID
const book = await bookService.getById('book-123');

// Update metadata
await bookService.updateMetadata('book-123', { title: 'New Title' });

// Get pages
const pages = await bookService.getPages('book-123');

// Get thumbnail URL
const url = bookService.getThumbnailUrl('book-123');

Series

import { SeriesService } from 'komga-sdk';
const seriesService = new SeriesService(client);

// List series
const series = await seriesService.list({ page: 0, size: 20 });

// Get by ID
const s = await seriesService.getById('series-123');

// Get books in series
const books = await seriesService.getBooks('series-123', { page: 0, size: 50 });

// Update metadata
await seriesService.updateMetadata('series-123', { status: 'ONGOING' });

// Get collections containing series
const collections = await seriesService.getCollections('series-123');

Libraries

import { LibraryService } from 'komga-sdk';
const libraryService = new LibraryService(client);

// List libraries
const libraries = await libraryService.getAll();

// Get by ID
const library = await libraryService.getById('library-123');

// Update
await libraryService.update('library-123', { name: 'New Name' });

// Scan
await libraryService.scan('library-123');

// Delete
await libraryService.delete('library-123');

Direct API Functions

import { getBookById, getBooks, getSeries } from 'komga-sdk';

// Get single item
const result = await getBookById({
  client,
  path: { bookId: 'book-123' },
});
if (result.data) console.log(result.data.metadata.title);

// List with search
const booksResult = await getBooks({
  client,
  body: { fullTextSearch: 'manga' },
  query: { page: 0, size: 20, sort: ['metadata.title,asc'] },
});

Pagination Patterns

// Basic pagination
const page1 = await bookService.list({ page: 0, size: 20 });
const page2 = await bookService.list({ page: 1, size: 20 });

// With sorting
const sorted = await bookService.list({
  page: 0,
  size: 20,
  sort: ['metadata.title,asc'],
});

// Get all (unpaged)
const all = await bookService.list({ unpaged: true });

// Iterate all pages
let page = 0;
let hasMore = true;
while (hasMore) {
  const result = await bookService.list({ page, size: 100 });
  processBooks(result.content);
  hasMore = page < result.totalPages - 1;
  page++;
}

Error Handling

import {
  isApiError,
  isValidationError,
  isNetworkError,
  isTimeoutError,
} from 'komga-sdk';

try {
  await bookService.getById('invalid-id');
} catch (error) {
  if (isApiError(error)) {
    console.log(`HTTP ${error.status}: ${error.statusText}`);
    if (error.status === 404) console.log('Not found');
    if (error.status === 401) console.log('Unauthorized');
  } else if (isValidationError(error)) {
    console.log('Validation failed:', error.getFieldErrors());
  } else if (isTimeoutError(error)) {
    console.log('Request timed out');
  } else if (isNetworkError(error)) {
    console.log('Network error:', error.message);
  }
}

Validation

import { 
  BookDtoSchema, 
  validateResponse, 
  safeValidateResponse,
  createPageSchema 
} from 'komga-sdk';

// Throw on invalid
const book = validateResponse(BookDtoSchema, data);

// Safe (no throw)
const result = safeValidateResponse(BookDtoSchema, data);
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error);
}

// Paginated response
const PageBookSchema = createPageSchema(BookDtoSchema);
const page = PageBookSchema.parse(rawData);

Interceptors

import {
  createLoggingInterceptor,
  createErrorTransformInterceptor,
  createValidationInterceptor,
  BookDtoSchema,
} from 'komga-sdk';

// Logging
const { request, response } = createLoggingInterceptor({ logHeaders: true });
client.interceptors.request.use(request);
client.interceptors.response.use(response);

// Error transform
const errorTransform = createErrorTransformInterceptor();
client.interceptors.error.use(errorTransform);

// Validation
const validation = createValidationInterceptor({
  schemas: { '/api/books': BookDtoSchema },
  throwOnError: true,
});
client.interceptors.response.use(validation);

Common Workflows

Batch update metadata

const books = await bookService.list({ 
  search: { fullTextSearch: 'tag:untagged' },
  unpaged: true 
});

for (const book of books.content) {
  await bookService.updateMetadata(book.id, {
    tags: ['processed'],
  });
}
const books = await bookService.list({ page: 0, size: 20 });

const gallery = books.content.map(book => ({
  id: book.id,
  title: book.metadata.title,
  thumbnail: bookService.getThumbnailUrl(book.id),
}));

Scan and wait

await libraryService.scan('library-123');
// Note: scan is async on server side
// Poll or use websockets to check completion

TypeScript Types

import type {
  BookDto,
  SeriesDto,
  LibraryDto,
  BookMetadataUpdateDto,
  SeriesMetadataUpdateDto,
  PageBookDto,
} from 'komga-sdk';

function processBook(book: BookDto) {
  console.log(book.metadata.title);
}

function updateBook(id: string, updates: BookMetadataUpdateDto) {
  return bookService.updateMetadata(id, updates);
}

Environment Variables

const client = createKomgaClient({
  baseUrl: process.env.KOMGA_URL || 'http://localhost:25600',
  auth: {
    type: 'basic',
    username: process.env.KOMGA_USER || 'admin',
    password: process.env.KOMGA_PASS || 'password',
  },
});

Next steps

Domain Services

Full documentation for all service methods.

Direct API

Low-level API function reference.