Skip to content

Commit dc94926

Browse files
committed
working prototype
1 parent b9d347e commit dc94926

File tree

12 files changed

+1005
-183
lines changed

12 files changed

+1005
-183
lines changed

runtimes/fastapi/ARCHITECTURE.md

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# FastAPI Runtime Architecture
2+
3+
## Overview
4+
5+
The FastAPI runtime enables native support for FastAPI applications in Azure Functions Python Worker. It acts as an adapter layer that discovers FastAPI routes, converts them to Azure Functions, and handles request routing.
6+
7+
## Architecture Diagram
8+
9+
```
10+
┌─────────────────────────────────────────────────────────────┐
11+
│ Azure Functions Host │
12+
└──────────────────────┬──────────────────────────────────────┘
13+
14+
│ gRPC Communication
15+
16+
┌──────────────────────▼──────────────────────────────────────┐
17+
│ Proxy Worker │
18+
│ (workers/proxy_worker/) │
19+
└──────────────────────┬──────────────────────────────────────┘
20+
21+
│ Python Import/Call
22+
23+
┌──────────────────────▼──────────────────────────────────────┐
24+
│ FastAPI Runtime Package │
25+
│ (runtimes/fastapi/) │
26+
│ │
27+
│ ┌────────────────────────────────────────────────────────┐ │
28+
│ │ handle_event.py - Main Event Handler │ │
29+
│ │ - worker_init_request() │ │
30+
│ │ - functions_metadata_request() │ │
31+
│ │ - function_load_request() │ │
32+
│ │ - invocation_request() │ │
33+
│ └────────────┬──────────────────┬────────────────────────┘ │
34+
│ │ │ │
35+
│ ┌────────────▼──────────┐ ┌───▼──────────────────────┐ │
36+
│ │ indexer.py │ │ converter.py │ │
37+
│ │ - Find FastAPI app │ │ - Convert routes to │ │
38+
│ │ - Scan routes │ │ Azure Functions │ │
39+
│ │ - Extract metadata │ │ - Generate bindings │ │
40+
│ └───────────────────────┘ └───────────────────────────┘ │
41+
│ │
42+
│ ┌──────────────────────────────────────────────────────┐ │
43+
│ │ handler.py - Request/Response Handler │ │
44+
│ │ - Convert Azure Functions request to ASGI │ │
45+
│ │ - Execute FastAPI route handler │ │
46+
│ │ - Convert response back to Azure Functions format │ │
47+
│ └──────────────────────────────────────────────────────┘ │
48+
└──────────────────────┬──────────────────────────────────────┘
49+
50+
│ Direct Call
51+
52+
┌──────────────────────▼──────────────────────────────────────┐
53+
│ User's FastAPI App │
54+
│ (function_app.py) │
55+
│ │
56+
│ app = FastAPI() │
57+
│ │
58+
│ @app.get("/hello") │
59+
│ async def hello(): │
60+
│ return {"message": "Hello"} │
61+
└──────────────────────────────────────────────────────────────┘
62+
```
63+
64+
## Request Flow
65+
66+
### 1. Initialization (Worker Init)
67+
68+
```
69+
Host → Proxy Worker → FastAPI Runtime
70+
71+
indexer.py
72+
73+
Discover FastAPI app
74+
75+
Scan all routes
76+
77+
converter.py
78+
79+
Convert to Azure Functions metadata
80+
```
81+
82+
### 2. Metadata Discovery (Function Metadata Request)
83+
84+
```
85+
Host → Proxy Worker → FastAPI Runtime → Return list of functions
86+
(one per FastAPI route)
87+
```
88+
89+
### 3. Function Invocation (HTTP Request)
90+
91+
```
92+
HTTP Request → Host → Proxy Worker → FastAPI Runtime
93+
94+
handler.py
95+
96+
Extract Azure Functions request
97+
98+
Call FastAPI route handler
99+
100+
Format response
101+
102+
Host ← Proxy Worker ← FastAPI Runtime
103+
```
104+
105+
## Key Components
106+
107+
### indexer.py - FastAPI Route Discovery
108+
109+
**Purpose**: Discovers and catalogs all routes in a FastAPI application
110+
111+
**Key Classes**:
112+
- `FastAPIIndexer`: Main indexer class
113+
- `FastAPIFunctionMetadata`: Metadata for each discovered route
114+
115+
**Process**:
116+
1. Import the user's Python module
117+
2. Find the FastAPI app instance
118+
3. Iterate through `app.routes`
119+
4. Extract metadata for each route:
120+
- Route path (e.g., `/api/users/{id}`)
121+
- HTTP methods (GET, POST, etc.)
122+
- Handler function reference
123+
- Async vs sync
124+
125+
### converter.py - Azure Functions Adapter
126+
127+
**Purpose**: Converts FastAPI route metadata to Azure Functions format
128+
129+
**Key Classes**:
130+
- `FastAPIConverter`: Converts routes to functions
131+
- `AzureFunctionInfo`: Azure Functions metadata structure
132+
133+
**Process**:
134+
1. Take FastAPI route metadata
135+
2. Create HTTP trigger binding for each route
136+
3. Create HTTP output binding
137+
4. Generate function metadata compatible with Python worker
138+
139+
**Binding Structure**:
140+
```python
141+
{
142+
"name": "req",
143+
"type": "httpTrigger",
144+
"direction": "in",
145+
"methods": ["get"],
146+
"route": "api/users"
147+
}
148+
```
149+
150+
### handler.py - Request/Response Processing
151+
152+
**Purpose**: Executes FastAPI routes and handles request/response conversion
153+
154+
**Key Functions**:
155+
- `execute_fastapi_route()`: Main execution entry point
156+
- `FastAPIHandler.handle_request()`: Request processing
157+
- `_format_response()`: Response formatting
158+
159+
**Request Flow**:
160+
1. Receive Azure Functions HTTP request
161+
2. Extract relevant data (method, URL, headers, body)
162+
3. Call the FastAPI route handler directly
163+
4. Convert result to Azure Functions response format
164+
165+
### handle_event.py - Event Processing
166+
167+
**Purpose**: Main event handler that responds to Azure Functions worker protocol
168+
169+
**Key Functions**:
170+
- `worker_init_request()`: Initialize runtime, index app
171+
- `functions_metadata_request()`: Return discovered functions
172+
- `function_load_request()`: Load specific function
173+
- `invocation_request()`: Execute function
174+
- `function_environment_reload_request()`: Reload/re-index app
175+
176+
## Data Flow
177+
178+
### Route to Function Conversion
179+
180+
```
181+
FastAPI Route:
182+
Path: /api/users/{user_id}
183+
Method: GET
184+
Handler: async def get_user(user_id: int)
185+
186+
↓ (indexer.py)
187+
188+
FastAPIFunctionMetadata:
189+
name: "get_api_users_user_id"
190+
route_path: "/api/users/{user_id}"
191+
http_methods: ["GET"]
192+
route_handler: <function get_user>
193+
is_async: True
194+
195+
↓ (converter.py)
196+
197+
AzureFunctionInfo:
198+
name: "get_api_users_user_id"
199+
bindings: [
200+
{
201+
"type": "httpTrigger",
202+
"direction": "in",
203+
"methods": ["get"],
204+
"route": "api/users/{user_id}"
205+
},
206+
{
207+
"type": "http",
208+
"direction": "out"
209+
}
210+
]
211+
212+
↓ (handle_event.py)
213+
214+
RpcFunctionMetadata:
215+
(protobuf message sent to host)
216+
```
217+
218+
### Request Execution Flow
219+
220+
```
221+
HTTP GET /api/users/123
222+
223+
224+
225+
Azure Functions Host
226+
- Routes to function "get_api_users_user_id"
227+
228+
229+
230+
Proxy Worker
231+
- Forwards invocation request
232+
233+
234+
235+
FastAPI Runtime (invocation_request)
236+
- Looks up function metadata
237+
- Extracts HTTP request data
238+
239+
240+
241+
handler.py (execute_fastapi_route)
242+
- Calls get_user(user_id=123)
243+
- Returns result
244+
245+
246+
247+
Format Response
248+
{
249+
"status_code": 200,
250+
"headers": {...},
251+
"body": '{"user": {...}}'
252+
}
253+
254+
255+
256+
Return to Host → HTTP Response
257+
```
258+
259+
## Key Design Decisions
260+
261+
### 1. Direct Handler Invocation
262+
- Routes are executed by calling the FastAPI handler directly
263+
- Bypasses ASGI server for performance
264+
- Simplifies integration with Azure Functions
265+
266+
### 2. Route-to-Function Mapping
267+
- Each FastAPI route becomes a separate Azure Function
268+
- Maintains granular control and monitoring
269+
- Enables per-route configuration
270+
271+
### 3. Metadata-Driven Indexing
272+
- App is indexed once during initialization
273+
- Metadata cached for fast lookups
274+
- Re-indexing supported for hot reload
275+
276+
### 4. Protobuf Communication
277+
- Uses same protocol as standard Python worker
278+
- Seamless integration with host
279+
- No changes needed to Azure Functions infrastructure
280+
281+
## Integration Points
282+
283+
### With Proxy Worker
284+
- Proxy worker imports and calls FastAPI runtime functions
285+
- Uses standard Python function calls (not gRPC internally)
286+
- Passes protobuf objects for requests/responses
287+
288+
### With User's FastAPI App
289+
- Runtime imports user's module dynamically
290+
- Discovers FastAPI app instance via reflection
291+
- Maintains reference to route handlers for invocation
292+
293+
### With Azure Functions Host
294+
- Communicates via gRPC (through proxy worker)
295+
- Uses standard worker protocol
296+
- Reports functions via metadata requests
297+
298+
## Future Enhancements
299+
300+
### 1. Full ASGI Protocol Support
301+
- Implement complete ASGI lifecycle
302+
- Support ASGI middleware
303+
- Enable streaming responses
304+
305+
### 2. Advanced FastAPI Features
306+
- Dependency injection support
307+
- Background tasks
308+
- WebSocket support
309+
- Lifespan events
310+
311+
### 3. Performance Optimizations
312+
- Connection pooling
313+
- Response caching
314+
- Lazy loading of routes
315+
316+
### 4. Development Experience
317+
- Hot reload support
318+
- Better error messages
319+
- FastAPI-specific debugging tools
320+
321+
## Testing Strategy
322+
323+
### Unit Tests
324+
- `test_indexer.py`: Route discovery
325+
- `test_converter.py`: Metadata conversion
326+
- `test_handler.py`: Request/response handling (TODO)
327+
328+
### Integration Tests
329+
- `test_example_app.py`: End-to-end with sample app
330+
- Real FastAPI app indexing and conversion
331+
332+
### Manual Testing
333+
- Deploy to Azure Functions
334+
- Test with actual HTTP requests
335+
- Verify monitoring and logging

0 commit comments

Comments
 (0)