Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CodeWithCJ/SparkyFitness/llms.txt

Use this file to discover all available pages before exploring further.

SparkyFitness is an open-source, privacy-first fitness platform and warmly welcomes contributions from the community. Whether you want to squash a bug, build a new feature, improve documentation, or translate the UI into a new language, every contribution makes SparkyFitness better for families everywhere. This guide explains how to get involved and what the project asks of contributors.

Ways to Contribute

Bug reports

Open a GitHub issue with steps to reproduce, expected vs. actual behaviour, environment details, and screenshots for visual bugs.

Feature requests

Open a GitHub issue to discuss the idea before writing any code. Maintainers will confirm alignment with the project roadmap.

Code pull requests

Fork the repo, create a feature branch, follow the coding standards below, and open a PR with the required checklist completed.

Documentation

Fix inaccuracies, add examples, or document undocumented behaviour. Docs live in the docs/ Nuxt / Docus site.

Translations

Only update the English (en) translation file in the main repo. Non-English translations are maintained in the separate SparkyFitnessTranslations repository via Weblate.

Code quality

Refactoring, performance improvements, security fixes, and dependency updates are all welcome.
The AGENTS.md file at the repository root contains special instructions for AI coding assistants (such as GitHub Copilot, Claude, and similar tools). If you are using an AI assistant to help write code for SparkyFitness, make sure your assistant has read AGENTS.md — it defines cross-package rules, migration requirements, shared utilities to prefer, and common command entrypoints. Package-level AGENTS.md / CLAUDE.md files in SparkyFitnessFrontend/, SparkyFitnessServer/, and SparkyFitnessMobile/ take precedence over the root file for work inside those packages.

Development Setup

Follow the Dev Environment guide to get the full stack running locally. The short version:
git clone https://github.com/CodeWithCJ/SparkyFitness.git
cd SparkyFitness
cp docker/.env.example .env
# Generate encryption key
sed "s/SPARKY_FITNESS_API_ENCRYPTION_KEY=changeme_replace_with_a_64_character_hex_string/SPARKY_FITNESS_API_ENCRYPTION_KEY=$(openssl rand -hex 32)/" \
  .env > .env.tmp && mv .env.tmp .env
./docker/docker-helper.sh dev up

Development Workflow

1

Fork and clone

Fork the repository on GitHub, then clone your fork:
git clone https://github.com/YOUR_USERNAME/SparkyFitness.git
cd SparkyFitness
git remote add upstream https://github.com/CodeWithCJ/SparkyFitness.git
2

Create a feature branch

Always branch from main:
git checkout main
git pull upstream main
git checkout -b feature/your-feature-name
# or: fix/login-validation, docs/update-setup-guide
3

Make changes and test

Follow the coding standards below. Test your changes thoroughly — happy path, error cases, and edge cases.
4

Run quality checks

# Frontend
cd SparkyFitnessFrontend && pnpm run validate

# Backend
cd SparkyFitnessServer && pnpm run typecheck && pnpm run lint && pnpm run test

# Mobile (if applicable)
cd SparkyFitnessMobile && pnpm run lint && pnpm run test:run -- --watchman=false --runInBand
5

Commit your changes

git add .
git commit -m "feat: add exercise tracking functionality"
Use a short, descriptive commit message with a conventional prefix (feat:, fix:, docs:, refactor:, test:, chore:).
6

Open a pull request

Push your branch and open a PR on GitHub. Fill out the PR template completely — the automated validation workflow checks that all required boxes are ticked.

Coding Standards

General principles

  • Clean, readable code — prefer self-documenting names; add comments for non-obvious logic.
  • DRY — avoid duplication; extract shared logic into utilities or helpers.
  • Separation of concerns — keep routing, business logic, and data access in their respective layers.
  • No any — use TypeScript strictly; define interfaces and types explicitly.

Frontend (React + TypeScript)

// Use functional components with hooks
const ExampleComponent: React.FC<ExampleProps> = ({ prop1, prop2 }) => {
  // 1. Hooks at the top
  const [state, setState] = useState<StateType>('');
  const { data, isLoading } = useExampleData();

  // 2. Event handlers
  const handleSubmit = useCallback((e: React.FormEvent) => {
    e.preventDefault();
    // handle submission
  }, []);

  // 3. Early returns for loading/error states
  if (isLoading) return <LoadingSpinner />;

  return (
    <div className="component-container">
      {/* JSX */}
    </div>
  );
};
  • Use Tailwind CSS utility classes for all styling.
  • Prefer shadcn/ui components over custom implementations.
  • Always add responsive classes (sm:, md:, lg:).
  • Support both light and dark themes.
Follow the established pattern — API functions and query keys live in src/api/, custom hooks wrapping useQuery / useMutation live in src/hooks/:
// src/api/exampleApi.ts
export const exampleKeys = {
  all: ['examples'] as const,
  detail: (id: string) => [...exampleKeys.all, id] as const,
};

export const getExamples = () => api.get('/examples');

// src/hooks/useExamples.ts
export const useExamples = () =>
  useQuery({ queryKey: exampleKeys.all, queryFn: getExamples });

Backend (Node.js + Express)

  • All new backend files must be written in TypeScript.
  • Existing JavaScript files may remain as-is, but conversion is encouraged when adding significant functionality.
  • Enable strict mode; avoid any.
All new API endpoints must define Zod schemas for request validation:
import { z } from 'zod';

const createFoodEntrySchema = z.object({
  foodItemId: z.string().uuid(),
  quantity:   z.number().positive(),
  unit:       z.string().min(1),
  mealType:   z.enum(['breakfast', 'lunch', 'dinner', 'snack']),
  consumedAt: z.string().datetime(),
});

router.post('/food-entries', async (req, res) => {
  const validated = createFoodEntrySchema.parse(req.body);
  // ...
});
All database operations must use the repository pattern with explicit transaction management:
const foodEntryRepository = {
  async create(data: CreateFoodEntryInput) {
    const client = await pool.connect();
    try {
      await client.query('BEGIN');
      // parameterised queries only — never string interpolation
      const result = await client.query(
        'INSERT INTO food_entries(user_id, food_item_id, quantity) VALUES($1,$2,$3) RETURNING *',
        [data.userId, data.foodItemId, data.quantity]
      );
      await client.query('COMMIT');
      return result.rows[0];
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  },
};
Every new API endpoint needs tests covering at least:
  • Happy path (valid input, expected response)
  • Validation errors (malformed input)
  • Authentication / authorisation failures
  • Edge cases

Database standards

  • All schema changes must go through a migration file in SparkyFitnessServer/db/migrations/.
  • Every new user-specific table must have RLS policies added to SparkyFitnessServer/db/rls_policies.sql.
  • Classify new tables into Tier 1, Tier 2, or Tier 3 in the database security documentation.
  • Wrap all migrations in a BEGIN / COMMIT transaction.
  • Use DECIMAL(10,2) for nutritional values; UUID for all primary keys.

Project Structure

Understanding where to add code:
What you’re addingWhere it goes
New API routeSparkyFitnessServer/routes/<feature>.ts
Database queriesSparkyFitnessServer/models/<feature>Repository.ts
Business logicSparkyFitnessServer/services/<feature>Service.ts
Request validation schemaSparkyFitnessServer/schemas/<feature>.ts
New device/service integrationSparkyFitnessServer/integrations/<provider>/
Frontend pageSparkyFitnessFrontend/src/pages/<FeaturePage>.tsx
Reusable UI componentSparkyFitnessFrontend/src/components/<Feature>/
API function + query keySparkyFitnessFrontend/src/api/<feature>Api.ts
Custom hookSparkyFitnessFrontend/src/hooks/use<Feature>.ts
Shared types / schemasshared/src/schemas/ or shared/src/constants/

Adding a New Integration

SparkyFitness already integrates with services such as FatSecret, Nutritionix, OpenFoodFacts, Strava, Withings, Garmin Connect, Hevy, Polar, and more. Each integration lives in its own directory under SparkyFitnessServer/integrations/<provider>/. To add a new device or data-provider integration:
1

Create the integration directory

mkdir SparkyFitnessServer/integrations/myprovider
2

Implement the integration module

Follow the structure of an existing integration. At minimum, export:
  • An authentication / OAuth flow handler
  • A data-fetch function that maps provider data to SparkyFitness types
  • A Zod schema for the provider’s API response
3

Add a route

Register the integration’s endpoints in a new route file and mount it in the main Express app.
4

Store credentials securely

Save OAuth tokens or API keys in external_data_providers (a Tier 2 table — owner writes, delegates with share_external_providers can read). Encrypt all secrets using the SPARKY_FITNESS_API_ENCRYPTION_KEY.
5

Write tests

Mock the external HTTP calls and test the happy path plus error handling.

Translations

SparkyFitness uses i18next and react-i18next for internationalisation. Translation files are stored under SparkyFitnessFrontend/public/locales/{{languageCode}}/translation.json.
Only update the English (en) translation file in the main repository. Non-English translation files are maintained in the separate SparkyFitnessTranslations repository via Weblate. Do not modify non-English files in this repo.
When adding new UI strings:
  1. Add the key and English string to public/locales/en/translation.json.
  2. Use the useTranslation hook in your component:
    import { useTranslation } from 'react-i18next';
    
    const MyComponent = () => {
      const { t } = useTranslation();
      return <h1>{t('myFeature.title', { defaultValue: 'My Feature' })}</h1>;
    };
    
  3. Add a hardcoded fallback value directly in the code so the UI remains usable even if the translation file hasn’t loaded.
To add a new language (e.g. Spanish — es):
  1. Create public/locales/es/translation.json.
  2. Add 'es' to supportedLngs in SparkyFitnessFrontend/src/i18n.ts.
  3. Add a <SelectItem> for Spanish in the language switcher in Settings.tsx.

Pull Request Checklist

Before submitting your PR, verify:
  • Branch is up to date with the latest main
  • Changes have been tested thoroughly
  • Code follows project conventions and standards
  • No new console errors or warnings
  • Mobile responsiveness verified for any UI changes
  • Database migrations included if schema changed
  • New tables added to RLS policies and classified in security docs
  • English translation keys updated if new UI strings added
  • Before/after screenshots attached for UI changes
  • Frontend: pnpm run validate passes in SparkyFitnessFrontend/
  • Backend: pnpm run typecheck && pnpm run lint && pnpm run test passes in SparkyFitnessServer/
  • Backend new files: Written in TypeScript with Zod schemas and tests

Automated PR validation

A GitHub Actions workflow runs automatically on every PR and:
  1. Detects which packages changed (frontend, backend, mobile, UI).
  2. Validates that the appropriate checkboxes in the PR template are checked.
  3. Posts a comment with validation results and guidance.
  4. Fails the PR check if required checkboxes are missing or unchecked.
Do not remove checkboxes from the PR template. The validation workflow checks for their presence and the PR will fail if they are missing.

Getting Help

GitHub Issues

Use GitHub Issues to report bugs or ask technical questions. Search existing issues before opening a new one.

GitHub Discussions

Use GitHub Discussions for general questions, ideas, and community conversation.
For complex architectural questions or significant feature proposals, tag a maintainer in the issue so they can weigh in before you invest time in implementation.

Build docs developers (and LLMs) love