11# PolicyEngine API v2
22
3- A FastAPI service for running PolicyEngine microsimulations with Supabase backend, object storage, and background task processing.
3+ FastAPI service for PolicyEngine microsimulations with Supabase backend, object storage, and background task processing.
44
55## Features
66
7- - RESTful API for creating and managing simulations
8- - Supabase for database and storage
9- - Redis caching and Celery for background task processing
7+ - RESTful API for tax-benefit microsimulations
8+ - Supabase for PostgreSQL database and object storage
9+ - Celery workers for background simulation tasks
1010- SQLModel for type-safe database models
11+ - Logfire observability and monitoring
1112- Terraform deployment to AWS
1213
1314## Quick start
@@ -16,257 +17,192 @@ A FastAPI service for running PolicyEngine microsimulations with Supabase backen
1617
1718- [ Supabase CLI] ( https://supabase.com/docs/guides/cli )
1819- Docker and Docker Compose
19- - Python 3.11+
20+ - Python 3.13+ with uv
2021
21- ### Local development with Supabase
22+ ### Local development
2223
23241 . ** Set up environment**
2425
25- Copy the example environment file:
26-
2726``` bash
2827cp .env.example .env
2928```
3029
31- The ` .env ` file contains default values for local Supabase development . Key variables :
30+ The defaults in ` .env.example ` work with local Supabase. Key settings :
3231
3332``` bash
34- # Supabase local instance (defaults work with `supabase start`)
3533SUPABASE_URL=http://127.0.0.1:54321
36- SUPABASE_KEY=< anon-key> # Public anon key
37- SUPABASE_SERVICE_KEY=< service-role-key> # Admin key for seeding
3834SUPABASE_DB_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
39-
40- # Storage
41- STORAGE_BUCKET=datasets
42-
43- # Redis (via Docker)
4435REDIS_URL=redis://localhost:6379/0
36+ STORAGE_BUCKET=datasets
4537```
4638
47- ** Important:** The service key is required for database seeding operations (uploading datasets to storage). The default keys in ` .env.example ` work with local Supabase.
48-
49392 . ** Start Supabase**
5040
5141``` bash
5242supabase start
5343```
5444
55- This creates a local instance at ` http://localhost:54321 ` with:
56- - PostgreSQL on port 54322
57- - Storage API with S3-compatible interface
58- - Auth API
59- - Studio dashboard at ` http://localhost:54323 `
45+ Creates a local instance with PostgreSQL (port 54322), Storage API, and Studio dashboard at ` http://localhost:54323 ` .
6046
61473 . ** Run integration tests**
6248
63- This will set up the database, seed it with UK/US models, and run tests:
49+ Sets up database, seeds models, and runs tests:
6450
6551``` bash
6652make integration-test
6753```
6854
6955This command:
70- - Starts Supabase (if not running)
7156- Creates all database tables from SQLModel definitions
72- - Applies RLS policies and storage bucket configuration
73- - Seeds UK and US tax-benefit models with all variables, parameters, and datasets
74- - Runs integration tests to verify everything works
57+ - Applies RLS policies and storage bucket migrations
58+ - Seeds UK and US tax-benefit models with variables, parameters, and datasets
59+ - Runs integration tests
7560
76614 . ** Start the API**
7762
7863``` bash
7964docker compose up
8065```
8166
82- The API will be available at ` http://localhost:8000 ` . Visit ` http://localhost:8000/docs ` for interactive API documentation.
83-
84- ## Observability
85-
86- The API uses [ Logfire] ( https://logfire.pydantic.dev/ ) for observability and monitoring. Logfire automatically instruments:
87- - All HTTP requests and responses
88- - Database queries
89- - Background tasks
90- - Performance metrics
91-
92- To view traces and logs, visit the [ Logfire dashboard] ( https://logfire.pydantic.dev ) .
93-
94- ## Architecture
95-
96- The system consists of three main components:
97-
98- 1 . ** API server** - FastAPI application serving REST endpoints
99- 2 . ** Database** - Supabase PostgreSQL storing models, parameters, simulations
100- 3 . ** Storage** - Supabase storage for dataset files (.h5)
101- 4 . ** Worker** - Celery workers executing simulations in background
102-
103- ### Models
104-
105- The API provides eleven core resources:
106-
107- - ** Datasets** - Microdata for simulations (stored in Supabase storage)
108- - ** Policies** - Parametric reforms to tax-benefit systems
109- - ** Simulations** - Execution of tax-benefit models on datasets
110- - ** Variables** - Calculated outputs (income_tax, universal_credit, etc.)
111- - ** Parameters** - System settings (personal_allowance, benefit_rates, etc.)
112- - ** ParameterValues** - Time-bound parameter values
113- - ** Dynamics** - Dynamic modeling configurations
114- - ** TaxBenefitModels** - Country models (UK, US)
115- - ** TaxBenefitModelVersions** - Model versions
116- - ** AggregateOutputs** - Aggregate statistics from simulations
117- - ** ChangeAggregates** - Reform impact analysis
118-
119- ### Workflow
120-
121- 1 . Upload dataset: ` POST /datasets ` with file upload to Supabase storage
122- 2 . Create policy reform: ` POST /policies `
123- 3 . Create simulation: ` POST /simulations `
124- 4 . Poll simulation status: ` GET /simulations/{id} `
125- 5 . Create aggregates: ` POST /outputs/aggregate `
67+ API available at ` http://localhost:8000 ` . Visit ` http://localhost:8000/docs ` for interactive documentation.
12668
12769## API endpoints
12870
129- All endpoints at root level:
71+ Base URL: ` http://localhost:8000 `
13072
131- ``` bash
132- GET /health → {" status" : " healthy" }
133- GET /datasets → List all datasets
73+ ### Core resources
74+
75+ ```
76+ GET /health → Health check
77+ GET /datasets → List datasets
13478POST /datasets → Create dataset
135- GET /policies → List all policies
79+ GET /policies → List policies
13680POST /policies → Create policy
137- GET /simulations → List all simulations
138- POST /simulations → Create and queue simulation
139- GET /variables → List all variables
140- GET /parameters → List all parameters
141- GET /parameter-values → List all parameter values
142- GET /dynamics → List all dynamics
143- GET /tax-benefit-models → List all models
144- GET /tax-benefit-model-versions → List all model versions
145- GET /outputs/aggregate → List aggregates
146- POST /outputs/aggregate → Compute aggregate
147- GET /change-aggregates → List change aggregates
148- POST /change-aggregates → Compute reform impact
149-
150- POST /initialize/uk → Initialize UK model
151- POST /initialize/us → Initialize US model
81+ GET /simulations → List simulations
82+ POST /simulations → Create simulation
83+ GET /simulations/{id} → Get simulation status
15284```
15385
154- ## Configuration
155-
156- Set environment variables or create a ` .env ` file:
86+ ### Model metadata
15787
158- ``` bash
159- SUPABASE_URL=http://localhost:54321
160- SUPABASE_KEY=your-anon-key
161- SUPABASE_DB_URL=postgresql://postgres:postgres@localhost:54322/postgres
162- REDIS_URL=redis://localhost:6379/0
163- CELERY_BROKER_URL=redis://localhost:6379/0
164- CELERY_RESULT_BACKEND=redis://localhost:6379/1
165- STORAGE_BUCKET=datasets
166- DEBUG=false
88+ ```
89+ GET /variables → List variables
90+ GET /parameters → List parameters
91+ GET /parameter-values → List parameter values
92+ GET /tax-benefit-models → List models
93+ GET /tax-benefit-model-versions → List model versions
16794```
16895
169- ## Storage
96+ ### Aggregates and analysis
17097
171- Datasets are stored in Supabase storage:
98+ ```
99+ GET /aggregates → List aggregates
100+ POST /aggregates → Create aggregate
101+ GET /aggregates/{id} → Get aggregate
102+ GET /change-aggregates → List change aggregates
103+ POST /change-aggregates → Create change aggregate
104+ GET /change-aggregates/{id} → Get change aggregate
105+ DELETE /change-aggregates/{id} → Delete change aggregate
106+ ```
172107
173- ``` python
174- from policyengine_api.services import storage
108+ ## Typical workflow
175109
176- # Upload dataset
177- url = storage.upload_dataset(" /path/to/dataset.h5" , " frs_2023_24.h5" )
110+ 1 . Create simulation: ` POST /simulations ` with dataset and policy
111+ 2 . Poll status: ` GET /simulations/{id} ` until status is "completed"
112+ 3 . Request aggregates: ` POST /aggregates ` with simulation_id and variable
113+ 4 . Compare reforms: ` POST /change-aggregates ` with baseline and reform simulation IDs
178114
179- # Download dataset
180- storage.download_dataset(" frs_2023_24.h5" , " /local/path.h5" )
115+ ## Database management
181116
182- # Get public URL
183- url = storage.get_dataset_url(" frs_2023_24.h5" )
117+ ### Local development
184118
185- # List all datasets
186- files = storage.list_datasets()
119+ ``` bash
120+ make integration-test # Full setup: tables, migrations, seeding, tests
121+ make seed # Seed UK/US models only
122+ make reset # Reset Supabase database
187123```
188124
189- ## Development
190-
191- ### Running tests
125+ ### Production
192126
193127``` bash
194- pytest
128+ make db-reset-prod # Reset production database (requires confirmation)
195129```
196130
197- ### Code formatting
131+ ** Warning:** ` db-reset-prod ` drops all tables, recreates schema, and reseeds data. It requires typing "yes" to confirm.
132+
133+ ## Development
134+
135+ ### Code quality
198136
199137``` bash
200- ruff format .
201- ruff check --fix .
138+ make format # Format code with ruff
139+ make lint # Lint and fix with ruff
140+ make test # Run unit tests
202141```
203142
204143### Database schema
205144
206- The database schema uses a hybrid approach :
145+ Schema is defined in two parts :
207146
208- ** SQLModel for tables:** All table schemas are defined in Python using SQLModel (see ` src/policyengine_api/models/ ` ). This provides:
209- - Single source of truth in Python code
210- - Type safety and IDE autocomplete
211- - Automatic relationship handling
212- - Easy testing
147+ ** SQLModel tables** (` src/policyengine_api/models/ ` ): All table definitions use SQLModel for type safety and single source of truth.
213148
214- ** SQL migrations for Postgres features:** SQL files in ` supabase/migrations/ ` handle:
215- - Row-level security (RLS) policies
216- - Storage bucket configuration
217- - Postgres-specific features SQLModel can't express
149+ ** SQL migrations** (` supabase/migrations/ ` ): Row-level security policies, storage buckets, and Postgres-specific features.
218150
219- To regenerate the schema :
151+ To recreate tables :
220152
221153``` bash
222- # Creates all tables from SQLModel and applies SQL migrations
223154uv run python scripts/create_tables.py
224155```
225156
226- ** When to create SQL migrations: **
157+ Only create SQL migrations for RLS policies or storage configuration, not table schemas.
227158
228- Only create new SQL migrations for RLS policies, storage buckets, or other Postgres-specific features. Table schemas should be defined in SQLModel classes, not SQL.
159+ ## Observability
229160
230- ``` bash
231- # Create a new migration (only for RLS/storage/triggers)
232- supabase migration new add_new_rls_policy
233- ```
161+ [ Logfire] ( https://logfire.pydantic.dev/ ) instruments:
162+ - HTTP requests and responses
163+ - Database queries
164+ - Background tasks
165+ - Performance metrics
234166
235- ### Supabase management
167+ View traces at the [ Logfire dashboard ] ( https://logfire.pydantic.dev ) .
236168
237- ``` bash
238- # Start local Supabase
239- supabase start
169+ ## Architecture
240170
241- # Stop Supabase
242- supabase stop
171+ ### Components
243172
244- # Reset database
245- supabase db reset
173+ - ** API server** : FastAPI application (port 8000 local, 80 production)
174+ - ** Database** : Supabase PostgreSQL
175+ - ** Storage** : Supabase object storage for .h5 dataset files
176+ - ** Worker** : Celery workers for background simulations
177+ - ** Cache** : Redis for Celery broker and API caching
246178
247- # View logs
248- supabase logs
179+ ### Data models
249180
250- # Open Studio dashboard
251- supabase studio
252- ```
181+ - ** Datasets** : Microdata files in Supabase storage
182+ - ** Policies** : Parameter reforms
183+ - ** Simulations** : Tax-benefit calculations
184+ - ** Variables** : Model outputs (income_tax, universal_credit)
185+ - ** Parameters** : System settings (personal_allowance, benefit_rates)
186+ - ** ParameterValues** : Time-bound parameter values
187+ - ** Aggregates** : Statistics from simulations
188+ - ** ChangeAggregates** : Reform impact analysis
253189
254- ## Project structure
190+ ### Project structure
255191
256192```
257193policyengine-api-v2/
258- ├── src/
259- │ └ ── policyengine_api/
260- │ ├── api / # FastAPI routers
261- │ ├── config / # Settings
262- │ ├── models / # SQLModel database models
263- │ ├── services / # Database, storage, initialization
264- │ ├ ── tasks/ # Celery tasks
265- │ └ ── main.py # FastAPI application
266- ├ ── supabase / # Supabase configuration and migrations
267- ├── tests / # Test suite
268- ├── docker-compose.yml # Docker services (API, worker, Redis)
269- └── pyproject.toml # Dependencies
194+ ├── src/policyengine_api/
195+ │ ├ ── api/ # FastAPI routers
196+ │ ├── config / # Settings
197+ │ ├── models / # SQLModel database models
198+ │ ├── services / # Database, storage
199+ │ ├── tasks / # Celery tasks
200+ │ └ ── main.py # FastAPI app
201+ ├ ── supabase/
202+ │ └ ── migrations / # RLS policies and storage
203+ ├── scripts / # Database setup and seeding
204+ ├── tests/ # Test suite
205+ └── docker-compose.yml # Local services
270206```
271207
272208## License
0 commit comments