Project Structure Guide¶
This guide explains the recommended project structure for FraiseQL applications, created automatically by fraiseql init.
Visual Structure¶
my-project/
โโโ src/ # ๐ Application source code
โ โโโ main.py # ๐ GraphQL schema & FastAPI app
โ โโโ types/ # ๐ท๏ธ GraphQL type definitions
โ โ โโโ user.py # โโ User, Post, Comment types
โ โ โโโ post.py
โ โ โโโ __init__.py
โ โโโ queries/ # ๐ Custom query resolvers
โ โ โโโ user_queries.py # โโ Complex business logic
โ โ โโโ __init__.py
โ โโโ mutations/ # โ๏ธ Mutation handlers
โ โ โโโ user_mutations.py # โโ Data modification ops
โ โ โโโ __init__.py
โ โโโ __init__.py
โโโ tests/ # ๐งช Test suite
โ โโโ test_user.py # โโ Unit & integration tests
โ โโโ conftest.py
โโโ migrations/ # ๐๏ธ Database evolution
โ โโโ 001_initial_schema.sql # โโ Versioned schema changes
โ โโโ 002_add_indexes.sql
โโโ .env # ๐ Environment config
โโโ .gitignore # ๐ซ Git ignore rules
โโโ pyproject.toml # ๐ฆ Dependencies & config
โโโ README.md # ๐ Project documentation
Overview¶
FraiseQL projects follow a database-first architecture with clear separation of concerns. The structure emphasizes: - Database-first design: Schema and views come first - Modular organization: Separate directories for different concerns - Scalable patterns: Easy to grow from minimal to enterprise
Template Selection Guide¶
Choose the right starting template based on your project needs:
๐ Quickstart (No Template)¶
Best for: Learning FraiseQL, prototypes, experimentation What you get: Single-file app with basic CRUD operations When to use: First time with FraiseQL, proof-of-concepts Evolution path: Migrate to minimal template when growing
๐ฆ Minimal Template¶
Best for: Simple applications, MVPs, small projects Features: - Single-file GraphQL schema - Basic CRUD operations - PostgreSQL integration - Development server setup Example: Todo app, simple blog, basic API
๐๏ธ Standard Template¶
Best for: Production applications, medium complexity Features: - Multi-file organization (types, queries, mutations) - User authentication & authorization - Query result caching - Comprehensive testing setup - Migration system Example: SaaS app, e-commerce platform, content management
๐ข Enterprise Template¶
Best for: Large-scale applications, high traffic Features: - Multi-tenant architecture - Advanced caching (APQ, result caching) - Monitoring & observability - Microservices-ready structure - Performance optimizations Example: Enterprise platforms, high-traffic APIs
Evolution Path¶
Quickstart โ Minimal โ Standard โ Enterprise
โ โ โ โ
Learning Simple Production Scale
Prototypes Apps Apps Apps
Migration Tips:
- Quickstart โ Minimal: Use fraiseql init and move code to organized structure
- Minimal โ Standard: Split into multiple files, add authentication
- Standard โ Enterprise: Add multi-tenancy, advanced caching, monitoring
Best Practices by Template¶
Quickstart Best Practices¶
- โ Keep it simple - single file for learning
- โ Focus on GraphQL concepts over architecture
- โ Use for experimentation and prototyping
- โ Don't use for production applications
- โ Don't add complex business logic
Example Projects: Todo App Quickstart
Minimal Template Best Practices¶
- โ Single-file schema for simple domains
- โ Clear type definitions with descriptions
- โ Basic error handling and validation
- โ Database-first design principles
- โ Don't mix concerns in main.py
- โ Don't skip input validation
Example Projects: Simple Blog, Basic API
Standard Template Best Practices¶
- โ Separate types, queries, and mutations
- โ Comprehensive test coverage
- โ Authentication and authorization
- โ Query result caching
- โ Proper error handling
- โ Don't put business logic in resolvers
- โ Don't skip database migrations
Example Projects: Blog with Auth, E-commerce
Enterprise Template Best Practices¶
- โ Multi-tenant data isolation
- โ Advanced performance optimizations
- โ Comprehensive monitoring
- โ Microservices communication patterns
- โ Automated testing and deployment
- โ Don't compromise on security
- โ Don't skip performance monitoring
Example Projects: Enterprise Blog, Multi-tenant App
Directory Structure¶
my-project/
โโโ src/ # Application source code
โ โโโ main.py # GraphQL schema and FastAPI app
โ โโโ types/ # GraphQL type definitions
โ โ โโโ user.py # User type
โ โ โโโ post.py # Post type
โ โ โโโ __init__.py
โ โโโ queries/ # Custom query resolvers
โ โ โโโ user_queries.py
โ โ โโโ __init__.py
โ โโโ mutations/ # Mutation handlers
โ โ โโโ user_mutations.py
โ โ โโโ __init__.py
โ โโโ __init__.py
โโโ tests/ # Test files
โ โโโ test_user.py
โ โโโ conftest.py
โโโ migrations/ # Database schema changes
โ โโโ 001_initial_schema.sql
โ โโโ 002_add_indexes.sql
โโโ .env # Environment configuration
โโโ .gitignore # Git ignore rules
โโโ pyproject.toml # Python dependencies and config
โโโ README.md # Project documentation
Directory Purposes¶
src/ - Application Code¶
Purpose: Contains all Python application code organized by responsibility.
main.py: Entry point with GraphQL schema definition and FastAPI apptypes/: GraphQL type definitions using@fraiseql.typedecoratorsqueries/: Custom query resolvers for complex business logicmutations/: Mutation handlers for data modification operations
tests/ - Test Suite¶
Purpose: Comprehensive test coverage for reliability.
- Unit tests for individual functions
- Integration tests for database operations
- API tests for GraphQL endpoints
- Performance tests for critical paths
migrations/ - Database Evolution¶
Purpose: Version-controlled database schema changes.
- SQL files for schema modifications
- Named with timestamps or sequential numbers
- Applied with
fraiseql migratecommand
Configuration Files¶
.env: Environment variables (database URLs, secrets)pyproject.toml: Python dependencies and tool configuration.gitignore: Excludes sensitive files from version control
File Organization Patterns¶
Type Definitions (src/types/)¶
# src/types/user.py
import fraiseql
from fraiseql import fraise_field
from fraiseql.types import ID
@fraiseql.type
class User:
"""A user in the system."""
id: ID = fraise_field(description="User ID")
username: str = fraise_field(description="Unique username")
email: str = fraise_field(description="Email address")
created_at: str = fraise_field(description="Account creation date")
Query Resolvers (src/queries/)¶
# src/queries/user_queries.py
import fraiseql
from fraiseql import fraise_field
from fraiseql import fraise_field
from ..types.user import User
@fraiseql.type
class UserQueries:
"""User-related query operations."""
users: list[User] = fraise_field(description="List all users")
user_by_username: User | None = fraise_field(description="Find user by username")
async def resolve_users(self, info):
db = info.context["db"]
return await db.find("v_user", "users", info)
async def resolve_user_by_username(self, info, username: str):
db = info.context["db"]
return await db.find_one("v_user", username=username)
Mutation Handlers (src/mutations/)¶
# src/mutations/user_mutations.py
import fraiseql
from fraiseql import fraise_field
from fraiseql import fraise_field
from fraiseql.types import ID
from ..types.user import User
@input
class CreateUserInput:
"""Input for creating a new user."""
username: str = fraise_field(description="Desired username")
email: str = fraise_field(description="Email address")
@fraiseql.type
class UserMutations:
"""User-related mutation operations."""
create_user: User = fraise_field(description="Create a new user account")
async def resolve_create_user(self, info, input: CreateUserInput):
db = info.context["db"]
result = await db.execute_function("fn_create_user", {
"username": input.username,
"email": input.email
})
return await db.find_one("v_user", id=result["id"])
Main Application (src/main.py)¶
# src/main.py
import os
import fraiseql
from fraiseql import fraise_field
from fraiseql import fraise_field
from .types.user import User
from .queries.user_queries import UserQueries
from .mutations.user_mutations import UserMutations
@fraiseql.type
class QueryRoot(UserQueries):
"""Root query type combining all query operations."""
pass
@fraiseql.type
class MutationRoot(UserMutations):
"""Root mutation type combining all mutation operations."""
pass
# Create the FastAPI app
app = fraiseql.create_fraiseql_app(
queries=[QueryRoot],
mutations=[MutationRoot],
database_url=os.getenv("FRAISEQL_DATABASE_URL"),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
Database Organization¶
Schema Files (migrations/)¶
migrations/
โโโ 001_initial_schema.sql # Core tables and views
โโโ 002_add_user_auth.sql # Authentication tables
โโโ 003_add_indexes.sql # Performance indexes
โโโ 004_add_audit_triggers.sql # Audit logging
Naming Conventions¶
Tables:
- tb_entity - Base tables (e.g., tb_user, tb_post)
- tb_entity_history - Audit/history tables
Views:
- v_entity - Regular views for queries
- tv_entity - Materialized views for performance
Functions:
- fn_operation_entity - Mutation functions (e.g., fn_create_user)
Scaling Patterns¶
From Minimal to Standard¶
- Split main.py: Move types to
src/types/ - Add authentication: Create user management
- Add caching: Enable query result caching
- Add tests: Comprehensive test coverage
From Standard to Enterprise¶
- Multi-tenancy: Add tenant isolation
- Advanced caching: APQ and result caching
- Monitoring: Add observability
- Microservices: Split into services
Best Practices¶
Code Organization¶
- One type per file in
src/types/ - Group related operations in query/mutation files
- Use clear, descriptive names
- Add docstrings to all public functions
Database Design¶
- Design views for query patterns, not storage
- Use functions for complex business logic
- Index columns used in WHERE clauses
- Plan for growth and partitioning
Testing Strategy¶
- Unit tests for pure functions
- Integration tests for database operations
- API tests for GraphQL endpoints
- Performance tests for critical queries
Configuration Management¶
- Use
.envfor environment-specific settings - Never commit secrets to version control
- Document all configuration options
- Use sensible defaults
Tooling Integration¶
Development Tools¶
# Start development server
fraiseql dev
# Run tests
pytest
# Format code
ruff format
# Type checking
mypy
Production Deployment¶
- Use environment variables for configuration
- Set up proper logging and monitoring
- Configure database connection pooling
- Enable caching and performance optimizations
Migration from Quickstart¶
When your quickstart project grows:
- Run
fraiseql init: Create proper structure - Move code: Migrate from single file to organized modules
- Add tests: Create comprehensive test suite
- Add migrations: Version control database changes
- Configure CI/CD: Set up automated testing and deployment
This structure provides a solid foundation that scales from simple prototypes to complex, production-ready applications.