Skip to content
Setting up a new project + Linters/Formatters

Setting up a new project + Linters/Formatters

SvelteKit

Default prject structure:

<app-name>/
├─ eslint.config.mjs
├─ prettier.config.mjs
├─ .prettierignore
├─ package.json
└─ src/

Setting up linter and formatter (ESLint and Prettier):

Install dev packages

npm install --save-dev eslint prettier eslint-config-prettier \
@eslint/js @eslint/eslintrc eslint-plugin-svelte svelte-eslint-parser \
@typescript-eslint/parser @typescript-eslint/eslint-plugin

Set up config files (copy from below)

eslint.config.mjs
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import pluginSvelte from "eslint-plugin-svelte";
import prettierConfig from "eslint-config-prettier";

const __filename = fileURLToPath(import.meta.url);
const dirname = dirname(filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  ...compat.extends(
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
  ),

  prettierConfig,

  {
    files: ["**/*.svelte"],
    plugins: { svelte: pluginSvelte },
    languageOptions: {
      parser: pluginSvelte.parsers["svelte-eslint-parser"],
      parserOptions: {
        parser: "@typescript-eslint/parser",
        extraFileExtensions: [".svelte"],
      },
    },
    rules: {
      "svelte/no-at-html-tags": "error",
      "svelte/no-ignored-svelte-ignore": "warn",
    },
  },

  {
    ignores: [
      "node_modules/**",
      ".svelte-kit/**",
      "build/**",
      "dist/**",
      "static/**",
      "coverage/**",
      "vite.config.*",
      "svelte.config.*",
    ],
    rules: {
      "no-console": "warn",
      "no-debugger": "error",
      eqeqeq: ["error", "always"],
      "no-var": "error",
      "prefer-const": "warn",
      "prefer-arrow-callback": "warn",
    },
  },
];

export default eslintConfig;
prettier.config.mjs

Defaults are commented out.

/** @type {import("prettier").Config} */
const config = {
  //   semi: true,
  //   trailingComma: "all",
  //   singleQuote: false,
  //   printWidth: 80,
  //   tabWidth: 2,
  //   useTabs: false,
  //   endOfLine: "lf",
};

export default config;
.prettierignore
# Dependencies
node_modules/
.next/
out/
build/
dist/
public/

# Production
*.min.js
*.min.css

# Environment variables
.env
.env*.local
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

# Coverage
coverage/
*.lcov

# Misc
.nextflow/
*.swp
*.swo

Update package.json:

"scripts": {
  "lint": "eslint .",
  "lint:fix": "eslint . --fix",
  "format": "prettier --check .",
  "format:fix": "prettier --write ."
}

Usage: (TODO: add to mise)

//Check for ESLint errors
npm run lint

//Fix fixable ESLint issues
npm run lint:fix

//Check code formatting
npm run format

//Format code automatically
npm run format:fix

FastAPI (Python)

Default prject structure:

<app-name>/
├─ pyproject.toml
├─ mise.toml
├─ docker-compose.yml
└─ src/

Setting up the project

  1. Install mise

  2. Install uv

  3. Recommended language server: ty

  4. mise init

  5. Add to mise.toml:

    [env]
    UV_PROJECT_ENVIRONMENT = ".venv"
  6. mise use <tool>@<version>

  7. uv init

  8. Update pypropject.toml with Ruff configuration like this:

    ...
    
    [tool.ruff]
    exclude = [".venv"]
    
    [tool.ruff.lint]
    select = ["ALL"]
    ignore = [
      # Rules to ignore, if any
    ]
    
    [tool.ruff.lint.pydocstyle]
    convention = "google"
  9. uv add <package> --group dev for development/test-only or uv add <package> for production.

  10. Follow the documentation to configure mise tasks. See example:

    [tasks.db]
    description = "Starts the DB through docker compose"
    run = "docker compose up -d mongodb"
    
    [tasks.dev]
    description = "Run FastAPI in development mode."
    depends = ["db"]
    run = "uv run unicorn src.main:app"
    run_windows = "uv run unicorn src.main:app"
    
    [tasks.compose]
    description = "Run FastAPI via self-sufficient docker compose config"
    run = "docker compose up -d"
  11. Recommended: define docker-compose.yml with the necessary services for your project. See example:

    name: april-robots-online-general-api
    
    services:
      app:
        build:
          context: .
          dockerfile: .docker/dev/Dockerfile
          args:
            WORKDIR: /app
        environment:
          # Override MONGOHOST and KEYCLOAK_SERVER_URL
          # because containers are within one network
          MONGOHOST: mongodb
          KEYCLOAK_SERVER_URL: "http://keycloak:8080/"
          # Override default wait_and_start.sh args
          KC_HOST: keycloak
          HOST: 0.0.0.0
        volumes:
          # Why not just `.:/app`? Because it messes up ./.venv
          - ./app:/app/app
          - ./database:/app/database
          - ./scripts:/app/scripts
          - ./tests:/app/tests
          - ./static:/app/static
          - ./version_maps:/app/version_maps
          - ./wait_and_start.sh:/app/wait_and_start.sh
        ports:
          - "8000:8000"
        networks:
          - april-hub
        depends_on:
          - mongodb
        healthcheck:
          test: ["CMD", "curl", "-f", "http://0.0.0.0:8000"]
          interval: 5s
          timeout: 5s
          retries: 3
          start_period: 3s
        # This script ensures Keycloak has time to boot
        # before starting uvicorn
        command: ["./wait_and_start.sh"]
    
      mongodb:
        image: mongo:8.0.12-noble
        environment:
          MONGO_INITDB_ROOT_USERNAME: ${MONGOUSER:-root}
          MONGO_INITDB_ROOT_PASSWORD: ${MONGOPASSWORD:-password}
        ports:
          - "27017:27017"
        volumes:
          - mongodb_data:/data/db
        networks:
          - april-hub
    
      mongo-express:
        image: mongo-express:1.0.2
        ports:
          - "8081:8081"
        environment:
          ME_CONFIG_BASICAUTH_USERNAME: admin
          ME_CONFIG_BASICAUTH_PASSWORD: admin
          ME_CONFIG_MONGODB_URL: mongodb://${MONGOUSER:-root}:${MONGOPASSWORD:-password}@mongodb:27017/
        depends_on:
          - mongodb
        networks:
          - april-hub
    
    volumes:
      mongodb_data:
    
    networks:
      april-hub:
        name: april-hub-network
  12. Write Dockerfiles for development and production in .docker/dev/Dockerfile or .docker/production/Dockerfile, correspondingly. See example:

    FROM python:3.12-slim
    
    ARG WORKDIR="/app"
    
    WORKDIR ${WORKDIR}
    
    RUN pip install --no-cache-dir uv
    
    COPY pyproject.toml uv.lock ./
    
    RUN uv sync --no-dev
    
    COPY . .
    
    # Use uv to run inside the managed environment
    CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Setting up the IDE

See Quickstart