Skip to content

Instantly share code, notes, and snippets.

@sebastianknopf
Created May 1, 2026 07:48
Show Gist options
  • Select an option

  • Save sebastianknopf/8321a27cf520c8fb6179f4311ba0a649 to your computer and use it in GitHub Desktop.

Select an option

Save sebastianknopf/8321a27cf520c8fb6179f4311ba0a649 to your computer and use it in GitHub Desktop.
Python & Vue WebApp Template w/DockerCompose
description Backend development instructions for Python/FastAPI service. Use when: modifying Python code, API endpoints, database models, migrations, adapters, backend services, or backend tests.
applyTo
backend/**/*.py
backend/pyproject.toml
backend/Dockerfile

Backend Development Instructions

Project Structure

  • The backend uses a src-layout: application source code lives under backend/src/, not directly in backend/
  • The package is declared in backend/pyproject.toml with [tool.setuptools.packages.find] where = ["src"] (or equivalent for the build backend in use)
  • Tests live in backend/tests/ (outside src/) and import from the installed or editable package
  • Alembic migration files live in backend/alembic/

Python Version Compatibility

  • Target Python version: >= 3.10
  • All code must remain compatible with Python 3.10 and newer versions
  • Do not use Python features that require versions newer than 3.10 unless explicitly approved

Dependency Management

  • Explicit approval required before adding new packages to pyproject.toml
  • Dependencies are declared in pyproject.toml under [project.dependencies]
  • When suggesting new dependencies, explicitly ask for approval and explain why the dependency is needed
  • Prefer using existing dependencies when possible

Testing Requirements

Test Framework

  • Use only unittest module from Python standard library
  • Do not import pytest, nose, or other testing frameworks without explicit approval

Test Execution

  • All tests must be discoverable and runnable with: python -m unittest discover
  • Tests are located in backend/tests/ directory
  • Test files must follow the pattern test_*.py
  • Test classes must inherit from unittest.TestCase

Test Coverage

  • New modules must include corresponding unit tests
  • New functions and methods should be covered by tests
  • Tests should verify both success and error cases
  • Use descriptive test method names

Modifying Existing Tests

  • Critical: Only modify unit tests when the expected output of the tested method changes
  • When refactoring code without changing behavior:
    • Do NOT modify the tests – they verify the contract remains intact
    • Only modify the implementation being tested
  • Modify tests only when:
    • The method's public API changes (parameters, return type)
    • The expected behavior or output changes
    • Fixing a bug that the test should have caught

Test Structure Example

import unittest
from app.module import function_to_test

class TestModuleName(unittest.TestCase):
    def setUp(self) -> None:
        pass

    def tearDown(self) -> None:
        pass

    def test_function_success_case(self) -> None:
        result = function_to_test(valid_input)
        self.assertEqual(result, expected_output)

    def test_function_error_case(self) -> None:
        with self.assertRaises(ExpectedException):
            function_to_test(invalid_input)

Code Style

Language Requirements

  • All code must be written in English
    • Variable names, function names, class names in English
    • Comments and docstrings in English
    • Error messages and log messages in English
  • Non-English languages are only allowed for user-facing text via the frontend localization system

Conventions

  • Strict type hints are mandatory on all function signatures (parameters and return types), class attributes, and variables where the type is not immediately obvious
  • Use from __future__ import annotations at the top of files where needed for forward references
  • Use async/await for all database operations (SQLAlchemy async)
  • Follow PEP 8 style guidelines
  • Use meaningful variable and function names
  • Keep functions focused and single-purpose

Imports

  • Group imports: standard library, third-party, local imports
  • Use absolute imports from the app package
  • Avoid wildcard imports (from module import *)

Error Handling

  • Use appropriate exception types
  • Log errors using the configured logger
  • Provide meaningful error messages
  • Handle database errors gracefully

Database Migrations (Alembic)

General Rules

  • All schema changes must be managed via Alembic migrations – never modify the database schema directly
  • Migration scripts live in alembic/versions/
  • The alembic.ini and alembic/env.py must be kept in sync with the SQLAlchemy models

Creating Migrations

  • Auto-generate migrations from model changes: alembic revision --autogenerate -m "<description>"
  • Always review auto-generated migrations before applying – autogenerate can miss or misinterpret changes (e.g., renamed columns, custom types, constraints)
  • Use a short, descriptive slug in the revision message (e.g., add_user_email_index, drop_legacy_tokens_table)

Migration Conventions

  • One logical change per migration file – do not bundle unrelated schema changes
  • Provide both upgrade() and downgrade() implementations unless a downgrade is genuinely not possible; in that case, raise NotImplementedError with an explanation
  • Never delete or edit a migration that has already been applied to any environment
  • Use server_default instead of default for database-level defaults in column definitions
  • Prefer nullable=False with a server_default over allowing nulls when adding columns to existing tables

Naming Conventions

  • Constraint names must be explicit and follow this pattern:
    • Primary keys: pk_<table>
    • Foreign keys: fk_<table>_<column>_<referenced_table>
    • Unique constraints: uq_<table>_<column>
    • Indexes: ix_<table>_<column>
  • Use naming_convention in the SQLAlchemy MetaData to enforce this automatically:
from sqlalchemy import MetaData

metadata = MetaData(naming_convention={
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
})

Running Migrations

  • Apply pending migrations: alembic upgrade head
  • Rollback one step: alembic downgrade -1
  • Show current revision: alembic current
  • Show migration history: alembic history --verbose

API Development

FastAPI Routers

  • Each router handles a specific domain
  • Use Pydantic models for request/response validation
  • Routers are thin: they handle HTTP concerns only (input parsing, response shaping, status codes)
  • Business logic must not live inside routers – extract it into dedicated service modules

Service Layer

  • Shared logic (e.g., database access, business rules, external calls) belongs in service modules, not in routers
  • A service is a plain Python module or class with typed functions – no FastAPI dependencies
  • Multiple routers may depend on the same service; never duplicate logic across routers
  • Services receive dependencies (e.g., DB sessions) via parameters, not by importing global state

Response Models

  • Define clear Pydantic schemas with strict type hints for all endpoints
  • Use proper HTTP status codes
  • Return meaningful error messages

Authentication

  • Endpoints are protected by default. Every endpoint requires a valid authenticated session unless explicitly declared public.
  • Public endpoints must be marked clearly, e.g. by applying a dedicated public dependency or by documenting the intent with a comment.
  • Authentication is enforced via a FastAPI dependency (e.g., Depends(get_current_user)) injected at the router or endpoint level.
  • The dependency resolves the current user from the request (e.g., via a JWT bearer token or session cookie) and raises HTTP 401 if the credential is missing or invalid.
  • Endpoints that require specific roles or permissions raise HTTP 403 when the authenticated user lacks the required access.
  • Never rely on obscurity – omitting auth from an endpoint must be a conscious, documented decision.

Token Lifecycle

  • JWTs must carry an exp claim. Tokens without an expiry are not permitted.
  • Use a sliding expiry pattern: on every authenticated request, if the token is still valid but within a configurable renewal window (e.g., the remaining lifetime is less than half the total TTL), issue a fresh token with a new exp and return it to the client (e.g., via a response header or response body field).
  • The total token TTL and the renewal threshold must be configurable via environment variables – never hard-coded.
  • The client is responsible for storing the refreshed token and using it for subsequent requests.
  • Expired tokens must be rejected with HTTP 401; the client must then redirect to the login flow.

Security

  • Never commit secrets or credentials
  • Use environment variables for configuration

Common Patterns

Logging

import logging

logger = logging.getLogger(__name__)
logger.info("Information message")
logger.error("Error message", exc_info=True)

Before Committing

  • Run tests: python -m unittest discover
  • Ensure compatibility with Python >= 3.10
  • Verify no new dependencies were added without approval
  • Verify all migration files have both upgrade() and downgrade() implemented

Copilot Instructions

General Development Principles

Code Changes

  • Minimize changes: Only modify code that is directly relevant to the task
  • Targeted edits: Make the smallest possible changes to achieve the goal
  • Refactoring: Allowed when it meaningfully improves code quality, maintainability, or performance
  • Preserve behavior: Existing functionality must not break unless explicitly requested

Code Quality

  • Write clean, readable, and idiomatic code
  • Follow existing code patterns and conventions in the project
  • Maintain consistent formatting with the existing codebase
  • Add comments only when the code's intent is not self-evident

Testing

  • New features and modules should be covered by appropriate tests
  • Bug fixes should include tests to prevent regression when applicable
  • Follow project-specific testing guidelines (see backend/frontend instructions)

Documentation

  • Update relevant documentation when making functional changes
  • Keep inline documentation concise and meaningful
  • Document complex algorithms or non-obvious design decisions

API Consistency

  • When modifying API endpoints (either in backend or frontend):
    • Ensure changes are synchronized between backend implementation and frontend consumption
    • Update both sides to maintain API contract compatibility
    • Verify request/response schemas match on both ends
    • Test the complete request/response cycle after changes
  • Backend API changes require corresponding frontend updates and vice versa
  • Breaking API changes must be communicated and coordinated

Project Layout

  • /frontend – all frontend code
  • /backend – all backend code
  • /docs – all documentation
    • /docs/manual – end-user documentation
    • /docs/dev – developer documentation (architecture, API contracts, setup guides, etc.)

Do not place source code outside these directories.

Technology Constraints

Backend

  • Python >= 3.10 (minimum version)
  • FastAPI, Pydantic
  • Alembic for PostgreSQL schema migrations
  • No new dependencies without explicit approval

Frontend

  • Vue 3 with Material Design 3 (Google's official MD3 library)
  • Vite as build tool
  • No additional frameworks without explicit approval

Docker Environment

The application runs in Docker containers. Configuration is managed via .env file based on .env.example if present.

Networking

  • Services communicate over the internal Docker Compose network
  • Ports are exposed within the Compose network only – never published to the host unless explicitly required for development access
  • External port bindings (ports:) must be kept to the minimum necessary

Database

  • If a database is required, use PostgreSQL as a dedicated Docker Compose service
  • The database container is only accessible from within the Compose network – never bind its port to the host in production configurations
  • Only the backend may connect to the database. The frontend must never communicate with the database directly, not even indirectly via a shared connection string
  • Database credentials and connection URLs are managed via environment variables, never hard-coded
description Frontend development instructions for Vue 3 application. Use when: modifying Vue components, views, router, stores, API client, styles, or frontend configuration.
applyTo
frontend/**/*.vue
frontend/**/*.js
frontend/**/*.ts
frontend/index.html
frontend/vite.config.js
frontend/package.json

Frontend Development Instructions

Technology Stack

  • Vue 3 – Composition API (<script setup>) is the required style
  • Vue Router – client-side routing
  • vue-i18n – internationalization
  • @material/web – Google's official Material Design 3 web components (MD3)
  • Open Props – CSS design token library (spacing, shadows, typography, animations) by Adam Argyle (ex-Google)
  • Vite – build tool
  • No additional UI frameworks (no Vuetify, Quasar, PrimeVue, etc.) without explicit approval
  • No CSS frameworks (no Tailwind, Bootstrap, Pico CSS, etc.) without explicit approval
  • Mapping: if map rendering is required, use MapLibre GL JS (maplibre-gl). Any additional MapLibre plugins or related packages still require explicit approval.

Dependency Management

  • Explicit approval required before adding new packages to package.json
  • When suggesting new dependencies, explain why no existing dependency covers the need
  • Prefer native browser APIs and Vue built-ins over external libraries

Component Authoring

General Rules

  • Use <script setup> syntax for all components – no Options API
  • Keep components focused: one responsibility per component
  • Extract reusable logic into composables (use*.js in src/composables/)
  • Props must have explicit types; use defineProps with type declarations
  • Emit events with defineEmits; name events in kebab-case

Structure Order in SFCs

<script setup>
// imports, props, emits, composables, state, computed, methods, lifecycle
</script>

<template>
  <!-- markup -->
</template>

<style scoped>
/* component-local overrides only – see Styling section */
</style>

Component Naming

  • Component files: PascalCase (UserCard.vue)
  • In templates: PascalCase for custom components (<UserCard />)
  • Views (routed pages): suffix View (HomeView.vue, ItemsView.vue)

Styling

Approach

Styling is token-based, class-light, and framework-free. It follows a three-layer model:

  1. MD3 design tokens (--md-sys-color-*, --md-ref-typeface-*) – theming and interactive component appearance. Always prefer MD3 tokens over custom values for anything an MD3 component consumes.
  2. Open Props tokens (--shadow-*, --size-*, --radius-*, --font-size-*, --ease-*, etc.) – structural design primitives for layout, spacing, typography scale, shadows, and animation. Use these instead of hard-coding raw values.
  3. Custom layout CSS (src/assets/layout.css) – structural layout rules and project-specific custom properties (prefixed --mk-* to avoid collisions). Only add custom properties here when neither MD3 nor Open Props provides what's needed.

Open Props ships as CSS custom properties only – no HTML classes, no JavaScript, no conflict with MD3 web components (which use Shadow DOM).

Rules

  • Never add inline styles for layout or theming – use CSS classes or tokens
  • Inline styles are acceptable only for truly one-off values (e.g., max-width on a single element) where creating a class would be overkill
  • Component-scoped <style scoped> blocks are for component-local structural tweaks only, not for overriding global theme or layout
  • Do not introduce utility-class frameworks (Tailwind, Bootstrap, etc.) without explicit approval
  • Responsive design is achieved with CSS custom properties, clamp(), Open Props fluid sizes, flexbox, and grid – no breakpoint framework required
  • When a design value is needed (shadow, spacing, radius, easing), check Open Props first before writing a custom value

MD3 Tokens

  • Theme colors are set via --md-sys-color-* tokens on :root
  • Typography typefaces are set via --md-ref-typeface-brand and --md-ref-typeface-plain
  • Use MD3 component variants as intended (e.g., md-filled-button for primary actions, md-text-button for low-emphasis actions)

Open Props Tokens

Use Open Props variables for structural primitives instead of hard-coded values:

/* correct */
box-shadow: var(--shadow-2);
border-radius: var(--radius-2);
gap: var(--size-4);
font-size: var(--font-size-fluid-1);
animation-timing-function: var(--ease-3);

/* wrong */
box-shadow: 0 4px 6px -1px rgba(0,0,0,.1);
border-radius: 1rem;

Custom Layout Tokens

Project-specific tokens (prefixed --mk-*) in src/assets/layout.css are used only for values not covered by MD3 or Open Props. Consume them via variables, never duplicate them inline.

Using MD3 Components

  • Import @material/web components at the entry point (main.js) or in the relevant component – never duplicate imports
  • Use the appropriate component for the semantic intent (buttons, text fields, dialogs, icons, etc.)
  • Icon names come from the @material-symbols/font-400 icon font; use <md-icon> to render them
  • Do not wrap MD3 components in unnecessary container components just to rename them

Frontend vs. Backend Responsibility

  • The frontend is UI only. Its sole responsibility is rendering data and capturing user input.
  • All business logic, calculations, data transformations, and aggregations must run on the backend. If a computation is not purely presentational (e.g., formatting a date for display), it belongs in the backend.
  • Do not replicate backend logic in the frontend. If you need a derived value that isn't returned by the API, extend the API – do not compute it client-side.
  • Validation in the frontend is for immediate user feedback only (e.g., required field hints). Authoritative validation always runs on the backend.

Routing

  • All routes are defined in src/router/index.js
  • Protected routes require authentication; use a navigation guard to enforce this
  • Public routes (e.g., login) must be explicitly marked (e.g., meta: { public: true })
  • Lazy-load route components with dynamic import() to keep the initial bundle small

State Management

  • Use Vue's built-in reactivity (reactive, ref) for shared state – no external state management library is required
  • Shared state is implemented as plain reactive singletons exported from src/stores/ modules
  • Do not put UI state (loading spinners, open dialogs) in shared stores unless it needs to be shared across multiple components; keep it local with ref/reactive
  • Do not introduce Pinia or Vuex without explicit approval

API Communication

  • All HTTP calls go through a central API client (src/api/client.js)
  • Never call axios or fetch directly from a component or store – always use the client module
  • The client handles base URL, auth headers, and error normalization centrally
  • API functions are grouped by domain (e.g., src/api/items.js, src/api/auth.js)

Internationalization (i18n)

  • All user-facing strings must be externalized into locale files (src/locales/)
  • Never hard-code display text in components – always use $t('key') or t('key')
  • Locale files are the only place where non-English natural language is permitted
  • Add keys to all locale files simultaneously to avoid missing translations

Code Style

Language

  • All code (variable names, function names, comments) must be written in English
  • Non-English natural language is only allowed in locale files

Conventions

  • Use const by default; let only when reassignment is necessary
  • Prefer async/await over .then() chains
  • Use destructuring for props and object access where it improves readability
  • Keep template expressions simple – move complex logic to computed properties or methods

Before Committing

  • Ensure all new user-facing strings have entries in every locale file
  • Check that no new dependencies were added without approval
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment