Contributing¶
Thank you for your interest in contributing to Tunarr! This guide will help you get started with development.
Prerequisites¶
Before you begin, ensure you have the following installed:
- Node.js 22 or later
- pnpm 10.28.0 or later (do not use npm or yarn; easiest to use
corepackto get the right version) - Git
- FFmpeg (for testing streaming features)
Getting Started¶
1. Fork and Clone¶
- Fork the repository on GitHub: chrisbenincasa/tunarr
- Clone your fork locally:
2. Install Dependencies¶
3. Start Development Servers¶
This starts:
- Backend server on
http://localhost:8000 - Frontend dev server on
http://localhost:5173/web
Repository Structure¶
Tunarr is a monorepo with four main packages:
| Package | Path | Description |
|---|---|---|
@tunarr/server |
server/ |
Node.js backend (Fastify, SQLite) |
@tunarr/web |
web/ |
React frontend (Vite, Material-UI) |
@tunarr/types |
types/ |
Shared TypeScript types and Zod schemas |
@tunarr/shared |
shared/ |
Utility functions shared between packages |
Common Commands¶
Root-Level Commands¶
# Install all dependencies
pnpm i
# Start all dev servers
pnpm turbo dev
# Build all packages
pnpm turbo build
# Run all tests
pnpm turbo test
# Run the typechecker
pnpm turbo typecheck
# Format code with Prettier
pnpm fmt
# Lint changed files only
pnpm lint-changed
Server Commands¶
cd server
pnpm dev # Start server with hot reload
pnpm debug # Start server with debugger attached
pnpm test # Run server tests
pnpm test path/to/test.ts # Run a single test file
pnpm generate-openapi # Regenerate OpenAPI spec
pnpm kysely # Run Kysely CLI for database operations
pnpm tunarr # Run CLI commands
Web Commands¶
cd web
pnpm dev # Start Vite dev server
pnpm bundle # Build production bundle
pnpm generate-client # Regenerate API client from OpenAPI spec
pnpm regen-routes # Regenerate TanStack Router routes
Development Workflow¶
Making Changes¶
-
Create a new branch from
dev: -
Make your changes
-
Ensure code passes all checks:
-
Commit your changes using conventional commit format
- Push your branch and open a Pull Request against
dev
Adding API Endpoints¶
When adding new API endpoints, follow these steps:
- Define your route in
server/src/api/{domain}Api.ts - Use Fastify with the Zod type provider for type-safe requests/responses
- Register the route in
server/src/api/index.ts -
Regenerate the OpenAPI spec:
-
Regenerate the web client:
Database Changes¶
The codebase uses both Kysely (legacy) and Drizzle ORM:
- New code should use Drizzle ORM
- Schema definitions are in
server/src/db/schema/ - Database migrations are in
server/src/migration/ - Access the database via
DBAccess, which provides bothdb(Kysely) anddrizzle(Drizzle) instances
Working with Dependencies¶
When adding new services or components that need dependency injection:
- Define your service key in
server/src/types/inject.ts - Register your service in the appropriate module:
DBModule.tsfor database servicesServicesModule.tsfor business logic servicesStreamModule.tsfor streaming servicesFFmpegModule.tsfor FFmpeg-related services
- Use
@injectable()decorator and@inject(KEYS.ServiceName)for constructor injection
Code Style¶
General Guidelines¶
- TypeScript: All code must be written in TypeScript
- No
as any: Never cast types usingas any - Formatting: Prettier handles formatting (run
pnpm fmt) - Linting: ESLint 9.x with flat config
- Pre-commit hooks: Husky + lint-staged run automatically
Import Aliases¶
- Server: Use
@/prefix (e.g.,import { foo } from '@/services/foo') - Web: Uses configured path aliases
Pre-Commit Hooks¶
Tunarr uses Husky and lint-staged to automatically run checks before each commit. These hooks are installed automatically when you run pnpm i.
What Runs on Commit¶
When you commit, the following checks run automatically on staged files:
- Prettier - Formats code and auto-fixes formatting issues
- ESLint - Lints code and reports errors
If any check fails, the commit will be blocked. Fix the reported issues and try again.
Bypassing Hooks (Not Recommended)¶
In rare cases where you need to skip pre-commit hooks:
Warning
Only bypass hooks when absolutely necessary. All checks will still run in CI, so skipping them locally just delays catching issues.
Troubleshooting Hooks¶
If hooks aren't running after cloning:
If hooks are misconfigured or you need to reinstall them:
Commit Messages¶
Tunarr uses Conventional Commits for commit messages. This standardized format enables automatic changelog generation and makes the git history easier to read.
Format¶
Types¶
| Type | Description |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation changes only |
style |
Code style changes (formatting, semicolons, etc.) |
refactor |
Code changes that neither fix bugs nor add features |
perf |
Performance improvements |
test |
Adding or updating tests |
chore |
Maintenance tasks (deps, build config, etc.) |
ci |
CI/CD configuration changes |
Scope (Optional)¶
The scope indicates which part of the codebase is affected:
server- Backend changesweb- Frontend changestypes- Shared types packageshared- Shared utilities packagedocs- Documentationdeps- Dependency updates
Examples¶
# Feature
git commit -m "feat(web): add dark mode toggle to settings"
# Bug fix
git commit -m "fix(server): resolve race condition in stream cleanup"
# Documentation
git commit -m "docs: update contributing guide with commit conventions"
# Refactor with scope
git commit -m "refactor(server): migrate channel queries to Drizzle"
# Chore without scope
git commit -m "chore: update dependencies"
# Breaking change (add ! after type)
git commit -m "feat(api)!: change channel endpoint response format"
Commit Message Body¶
For complex changes, add a body to explain what and why:
git commit -m "fix(server): handle null media duration gracefully
Previously, media items with null duration would cause the scheduler
to crash. This change treats null duration as 0 and logs a warning.
Fixes #1234"
Testing¶
- Framework: Vitest
- Test files: Use
.test.tsextension - Test data: Use
@faker-js/fakerfor generating test data
# Run all tests
pnpm turbo test
# Run tests in watch mode
pnpm test:watch
# Run a single test file
cd server && pnpm test path/to/file.test.ts
Architecture Overview¶
Server¶
- Fastify for HTTP server with Zod type provider
- Inversify for dependency injection
- Drizzle ORM (preferred) and Kysely (legacy) for database access
- better-sqlite3 for SQLite database
- Meilisearch for search functionality
Key directories:
api/- Route handlers organized by domaindb/- Database layer and schemaservices/- Business logicstream/- Video streaming pipelineffmpeg/- FFmpeg wrapper and pipeline builderexternal/- API clients for Plex, Jellyfin, Emby
Web¶
- React 18 with TypeScript
- Vite for bundling
- Material-UI v7 for components
- TanStack Router for file-based routing
- TanStack Query for data fetching
- Zustand for state management
- React Hook Form + Zod for forms
Key directories:
routes/- File-based routingcomponents/- Reusable componentshooks/- Custom React hooksstore/- Zustand storesgenerated/- Auto-generated API client
Getting Help¶
- GitHub Issues: Report bugs or request features
- Discord: Join the community for discussion and support
Pull Request Guidelines¶
- Target the
devbranch for all PRs - Keep PRs focused - one feature or fix per PR
- Use conventional commits - follow the commit message format
- Ensure all checks pass before requesting review
- Update documentation if your change affects user-facing behavior
- Add tests for new functionality when applicable