Code Style¶
This page documents the coding conventions and style guidelines for contributing to Pipelit.
The Most Important Rule¶
This project uses FastAPI + SQLAlchemy, NOT Django
Pipelit's backend is built on FastAPI, SQLAlchemy 2.0, Alembic, and RQ (Redis Queue). Never reference Django models, Django ORM, Django settings, Django views, or any Django concepts in code, comments, commit messages, or documentation.
- Models are SQLAlchemy
declarative_baseclasses, notdjango.db.models.Model - Migrations use Alembic, not
manage.py migrate - Settings use Pydantic
BaseSettings, notdjango.conf.settings - Background tasks use RQ, not Celery (though Celery is also not Django-specific)
- Authentication uses FastAPI dependencies with Bearer tokens, not Django middleware
Python¶
General¶
- Follow PEP 8 for formatting and naming
- Maximum line length: 100 characters (relaxed from PEP 8's 79)
- Use absolute imports from
platform/as the root (e.g.,from models.node import WorkflowNode) - Prefer f-strings over
.format()or%formatting
Type Hints¶
Use type hints on all function signatures:
# Good
def resolve_llm_for_node(node: WorkflowNode, db: Session) -> BaseChatModel:
...
# Bad
def resolve_llm_for_node(node, db):
...
Use from __future__ import annotations at the top of every module for modern annotation syntax:
from __future__ import annotations
from typing import Any
def process(data: dict[str, Any]) -> list[str]:
...
Naming Conventions¶
| Element | Convention | Example |
|---|---|---|
| Modules | snake_case | node_type_defs.py |
| Classes | PascalCase | WorkflowNode, NodeTypeSpec |
| Functions | snake_case | get_component_factory() |
| Constants | UPPER_SNAKE_CASE | COMPONENT_REGISTRY, NODE_TYPE_REGISTRY |
| Variables | snake_case | edge_type, node_id |
| Private | Leading underscore | _safe_eval(), _platform_dir |
Imports¶
Organize imports in three groups, separated by blank lines:
# 1. Standard library
from __future__ import annotations
import os
from pathlib import Path
# 2. Third-party
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
# 3. Local
from auth import get_current_user
from database import get_db
from models.workflow import Workflow
SQLAlchemy Models¶
- Use SQLAlchemy 2.0 style with
Mappedtype annotations - Use
mapped_column()instead ofColumn() - Define relationships with explicit
Mappedtypes
# Good (SQLAlchemy 2.0)
class WorkflowNode(Base):
__tablename__ = "workflow_nodes"
id: Mapped[int] = mapped_column(primary_key=True)
node_id: Mapped[str] = mapped_column(String(100))
workflow_id: Mapped[int] = mapped_column(ForeignKey("workflows.id"))
# Bad (SQLAlchemy 1.x style)
class WorkflowNode(Base):
__tablename__ = "workflow_nodes"
id = Column(Integer, primary_key=True)
node_id = Column(String(100))
FastAPI Endpoints¶
- Use
APIRouterfor route grouping - Use dependency injection for database sessions and authentication
- Return Pydantic models or dicts, never raw ORM objects
- Use appropriate HTTP status codes
@router.post("/workflows/", status_code=201)
def create_workflow(
data: WorkflowCreate,
db: Session = Depends(get_db),
user: UserProfile = Depends(get_current_user),
):
...
Pydantic Schemas¶
- Use
Literaltypes for enum-like fields (component_type,edge_type) - Use
model_config = ConfigDict(from_attributes=True)for ORM compatibility - Separate create, update, and response schemas
Authentication¶
- API uses Bearer token authentication exclusively
- Always use the
get_current_userdependency for authenticated endpoints - Agent users are created without passwords via
create_agent_user - Never use the user's personal API key for agent operations
TypeScript¶
General¶
- Use TypeScript strict mode (enabled in
tsconfig.json) - Prefer
constoverlet; never usevar - Use arrow functions for callbacks and inline functions
- Use template literals over string concatenation
Types¶
- Define interfaces for API response types in
types/models.ts - Use
interfacefor object shapes,typefor unions and intersections - Avoid
any-- useunknownand narrow with type guards when the type is uncertain
// Good
interface WorkflowNode {
id: number;
node_id: string;
component_type: ComponentType;
}
// Bad
const node: any = response.data;
React Components¶
- Use functional components with hooks
- Use TanStack Query for all API data fetching
- Use Shadcn/ui components for UI elements
- Colocate component-specific types with the component file
// Good
export function WorkflowCanvas({ slug }: { slug: string }) {
const { data: nodes } = useNodes(slug);
// ...
}
// Bad - class component
class WorkflowCanvas extends React.Component { ... }
API Hooks¶
All API interactions go through TanStack Query hooks defined in frontend/src/api/:
// Queries (GET)
export function useWorkflows() {
return useQuery({ queryKey: ["workflows"], queryFn: fetchWorkflows });
}
// Mutations (POST/PATCH/DELETE)
export function useCreateWorkflow() {
return useMutation({ mutationFn: createWorkflow });
}
File Organization¶
- One concern per file. Do not mix unrelated functionality.
- Keep files focused. If a file grows beyond 300-400 lines, consider splitting it.
- Mirror backend structure in frontend. API hooks in
api/, page components infeatures/, shared components incomponents/.
Commit Messages¶
- Use present tense, imperative mood: "add feature" not "added feature"
- Start with a category when appropriate:
fix:,feat:,test:,docs:,refactor: - Keep the first line under 72 characters
- Reference issue numbers when applicable:
fix: resolve edge validation crash (#42)
Documentation¶
- Document public functions and classes with docstrings
- Use
"""triple double quotes"""for Python docstrings - Use
/** JSDoc */comments for exported TypeScript functions - Keep comments focused on "why" rather than "what"