aws-python-helper 0.35.0__tar.gz → 0.37.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/PKG-INFO +317 -19
  2. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/README.md +314 -18
  3. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/__init__.py +22 -0
  4. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/base.py +17 -1
  5. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/dispatcher.py +11 -3
  6. aws_python_helper-0.37.0/aws_python_helper/invoker/__init__.py +19 -0
  7. aws_python_helper-0.37.0/aws_python_helper/invoker/api_client.py +153 -0
  8. aws_python_helper-0.37.0/aws_python_helper/invoker/exceptions.py +23 -0
  9. aws_python_helper-0.37.0/aws_python_helper/invoker/lambda_invoker.py +96 -0
  10. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/lambda_standalone/base.py +36 -6
  11. aws_python_helper-0.37.0/aws_python_helper/model_query/__init__.py +3 -0
  12. aws_python_helper-0.37.0/aws_python_helper/model_query/base.py +120 -0
  13. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper.egg-info/PKG-INFO +317 -19
  14. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper.egg-info/SOURCES.txt +6 -0
  15. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper.egg-info/requires.txt +2 -0
  16. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/pyproject.toml +3 -1
  17. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/__init__.py +0 -0
  18. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/auth_middleware.py +0 -0
  19. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/auth_validators.py +0 -0
  20. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/exceptions.py +0 -0
  21. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/fetcher.py +0 -0
  22. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/api/handler.py +0 -0
  23. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/context/__init__.py +0 -0
  24. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/context/session.py +0 -0
  25. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/context/state_validator.py +0 -0
  26. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/database/__init__.py +0 -0
  27. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/database/database_proxy.py +0 -0
  28. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/database/external_database_proxy.py +0 -0
  29. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/database/external_mongo_manager.py +0 -0
  30. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/database/mongo_manager.py +0 -0
  31. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/fargate/__init__.py +0 -0
  32. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/fargate/executor.py +0 -0
  33. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/fargate/fetcher.py +0 -0
  34. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/fargate/handler.py +0 -0
  35. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/fargate/task_base.py +0 -0
  36. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/lambda_standalone/__init__.py +0 -0
  37. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
  38. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/lambda_standalone/handler.py +0 -0
  39. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/repository/__init__.py +0 -0
  40. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/repository/base.py +0 -0
  41. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sns/__init__.py +0 -0
  42. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sns/publisher.py +0 -0
  43. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sqs/__init__.py +0 -0
  44. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sqs/consumer_base.py +0 -0
  45. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sqs/fetcher.py +0 -0
  46. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/sqs/handler.py +0 -0
  47. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/utils/__init__.py +0 -0
  48. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/utils/json_encoder.py +0 -0
  49. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/utils/response.py +0 -0
  50. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper/utils/serializer.py +0 -0
  51. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper.egg-info/dependency_links.txt +0 -0
  52. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/aws_python_helper.egg-info/top_level.txt +0 -0
  53. {aws_python_helper-0.35.0 → aws_python_helper-0.37.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 0.35.0
3
+ Version: 0.37.0
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -15,6 +15,8 @@ Requires-Python: >=3.9
15
15
  Description-Content-Type: text/markdown
16
16
  Requires-Dist: motor==3.3.2
17
17
  Requires-Dist: pymongo==4.6.1
18
+ Requires-Dist: pydantic>=2.0
19
+ Requires-Dist: httpx>=0.27.0
18
20
 
19
21
  # AWS Python Framework
20
22
 
@@ -33,6 +35,10 @@ Mini-framework to create REST APIs, SQS Consumers, SNS Publishers, Fargate Tasks
33
35
  - **Fargate Tasks**: Same pattern to run tasks in Fargate containers
34
36
  - **Standalone Lambdas**: Create lambdas invocable directly with AWS SDK
35
37
  - **Authentication middleware**: Built-in token-based authentication
38
+ - **Pydantic schema validation**: Declare a `schema` property on any `API` or `Lambda` and the framework validates automatically before `validate()` — returns 400 on failure
39
+ - **LambdaInvoker**: Invoke other Lambda functions (sync or async) with built-in error handling
40
+ - **ApiClient**: HTTP client for inter-service communication — resolves service URLs and auth token from environment variables automatically
41
+ - **ModelQueryLambda**: Base class for MongoDB query-proxy Lambdas — declare an `allowed_collections` whitelist and the framework handles `find` and `aggregate` operations generically
36
42
  - **JSON utilities**: Automatic serialization of MongoDB types
37
43
  - **Type hints**: Modern Python with type annotations
38
44
  - **Async/await**: Full support for asynchronous operations
@@ -60,6 +66,14 @@ All available classes and functions:
60
66
  | `FargateExecutor` | `aws_python_helper.fargate.executor` | Launches Fargate tasks from Lambda |
61
67
  | `fargate_handler` | `aws_python_helper.fargate.handler` | Entry point handler for Fargate |
62
68
  | `Repository` | `aws_python_helper.repository.base` | Base class for MongoDB repositories |
69
+ | `ModelQueryLambda` | `aws_python_helper` | Generic MongoDB query-proxy Lambda base class |
70
+ | `LambdaInvoker` | `aws_python_helper` | Invoke Lambda functions (sync/async) with error handling |
71
+ | `ApiClient` | `aws_python_helper` | HTTP client for inter-service communication |
72
+ | `LambdaInvocationError` | `aws_python_helper` | Raised when boto3 fails to invoke a Lambda |
73
+ | `LambdaResponseError` | `aws_python_helper` | Raised when a Lambda returns a FunctionError |
74
+ | `ServiceNotConfiguredError` | `aws_python_helper` | Raised when a service name is not in MICROSERVICE_URLS |
75
+ | `ApiClientError` | `aws_python_helper` | Raised when an HTTP request fails (network/timeout) |
76
+ | `ApiResponseError` | `aws_python_helper` | Raised when a remote API returns 4xx/5xx |
63
77
  | `Session` | `aws_python_helper` | Request-scoped session object (state + user) |
64
78
  | `get_session` | `aws_python_helper` | Read the current Session from async context |
65
79
  | `set_session` | `aws_python_helper` | Set the current Session in async context |
@@ -897,19 +911,21 @@ Here's a complete example showing how an API can invoke a standalone lambda:
897
911
  **1. The API endpoint** (`src/api/shippings/post.py`):
898
912
 
899
913
  ```python
900
- from aws_python_helper.api.base import API
901
- import boto3
902
- import json
914
+ from pydantic import BaseModel
915
+ from aws_python_helper import API, LambdaInvoker
916
+
917
+ class ShippingSchema(BaseModel):
918
+ customer_id: str
919
+ address: str
920
+ items: list[str]
903
921
 
904
922
  class ShippingPostAPI(API):
905
- async def validate(self):
906
- required_fields = ['customer_id', 'address', 'items']
907
- for field in required_fields:
908
- if field not in self.data:
909
- raise ValueError(f"{field} is required")
923
+
924
+ @property
925
+ def schema(self):
926
+ return ShippingSchema # automatic validation → 400 on failure
910
927
 
911
928
  async def process(self):
912
- # Create shipping in database
913
929
  shipping = {
914
930
  'customer_id': self.data['customer_id'],
915
931
  'address': self.data['address'],
@@ -921,15 +937,11 @@ class ShippingPostAPI(API):
921
937
  result = await self.db.deliveries.shippings.insert_one(shipping)
922
938
  shipping_id = str(result.inserted_id)
923
939
 
924
- # Invoke standalone lambda asynchronously to generate route
925
- lambda_client = boto3.client('lambda')
926
- lambda_client.invoke(
927
- FunctionName='GenerateRouteLambda',
928
- InvocationType='Event', # Asynchronous
929
- Payload=json.dumps({
930
- 'data': {'shipping_id': shipping_id}
931
- })
932
- )
940
+ # Invoke standalone lambda asynchronously (fire-and-forget)
941
+ invoker = LambdaInvoker()
942
+ invoker.invoke_async('GenerateRouteLambda', payload={
943
+ 'data': {'shipping_id': shipping_id}
944
+ })
933
945
 
934
946
  self.set_code(201)
935
947
  self.set_body({
@@ -1008,6 +1020,290 @@ __all__ = ['generate_route_handler']
1008
1020
  - Can retry lambda independently if it fails
1009
1021
  - Scalable architecture
1010
1022
 
1023
+ ## ✅ Pydantic Schema Validation
1024
+
1025
+ The framework supports automatic request validation via [Pydantic](https://docs.pydantic.dev/) v2. Override the `schema` property on any `API` or `Lambda` subclass to declare the expected shape of `self.data`. The framework validates before calling `validate()` and replaces `self.data` with the coerced output — the property name stays the same.
1026
+
1027
+ ### In an API endpoint
1028
+
1029
+ For `POST`/`PUT`, `self.data` is the request body. For `GET`/`LIST`, it is the query parameters. Both are validated the same way.
1030
+
1031
+ ```python
1032
+ from pydantic import BaseModel
1033
+ from aws_python_helper import API
1034
+
1035
+ class SearchSchema(BaseModel):
1036
+ keys: list[str]
1037
+ state: str
1038
+ limit: int = 100
1039
+
1040
+ class SearchPostAPI(API):
1041
+
1042
+ @property
1043
+ def schema(self):
1044
+ return SearchSchema # ← that's all
1045
+
1046
+ async def process(self):
1047
+ keys = self.data["keys"] # self.data is validated and coerced
1048
+ limit = self.data["limit"] # default applied by Pydantic
1049
+ ...
1050
+ ```
1051
+
1052
+ **On validation failure the framework automatically returns HTTP 400** — no extra code needed.
1053
+
1054
+ ### In a Standalone Lambda
1055
+
1056
+ Same property, same behavior. On failure a `ValueError` is raised, which the handler returns as `{"success": False, "error": "..."}`.
1057
+
1058
+ ```python
1059
+ from pydantic import BaseModel
1060
+ from aws_python_helper import Lambda
1061
+
1062
+ class SyncSchema(BaseModel):
1063
+ shipping_id: str
1064
+ carrier_id: str
1065
+
1066
+ class SyncCarrierLambda(Lambda):
1067
+
1068
+ @property
1069
+ def schema(self):
1070
+ return SyncSchema
1071
+
1072
+ async def process(self):
1073
+ shipping_id = self.data["shipping_id"] # validated
1074
+ ...
1075
+ ```
1076
+
1077
+ ### Without a schema
1078
+
1079
+ If `schema` is not overridden (returns `None`, the default), the behavior is identical to before — no change.
1080
+
1081
+ ---
1082
+
1083
+ ## 🔗 Inter-Service Communication
1084
+
1085
+ The framework provides two utilities for calling other services: `LambdaInvoker` for direct Lambda invocations and `ApiClient` for HTTP API calls.
1086
+
1087
+ ### LambdaInvoker
1088
+
1089
+ Wraps `boto3` Lambda invocations with proper error handling. Never returns `None` — raises typed exceptions instead.
1090
+
1091
+ ```python
1092
+ from aws_python_helper import LambdaInvoker, LambdaInvocationError, LambdaResponseError
1093
+
1094
+ invoker = LambdaInvoker()
1095
+
1096
+ # Synchronous — waits for result
1097
+ result = invoker.invoke("my-function-name", payload={"key": "value"})
1098
+ # result is the parsed dict returned by the Lambda
1099
+
1100
+ # Asynchronous fire-and-forget
1101
+ invoker.invoke_async("my-function-name", payload={"key": "value"})
1102
+ ```
1103
+
1104
+ **Exceptions:**
1105
+
1106
+ | Exception | When |
1107
+ |-----------|------|
1108
+ | `LambdaInvocationError` | boto3 could not reach the function (network, permissions, etc.) |
1109
+ | `LambdaResponseError` | The function itself raised an unhandled exception (`FunctionError` in the response) |
1110
+
1111
+ **Note:** `boto3` is part of the AWS Lambda Python runtime — no installation needed. For Fargate containers, add `boto3` to your `requirements.txt`.
1112
+
1113
+ ### ApiClient
1114
+
1115
+ HTTP client for service-to-service calls. Resolves the target URL and auth token automatically from environment variables — no hardcoded URLs or tokens in code.
1116
+
1117
+ ```python
1118
+ from aws_python_helper import ApiClient
1119
+
1120
+ client = ApiClient("dockets") # service name only
1121
+ result = await client.post("/search", body={"keys": [...]})
1122
+ result = await client.get("/dockets/123")
1123
+ result = await client.list("/dockets", params={"state": "CT"})
1124
+ result = await client.put("/dockets/123", body={"status": "active"})
1125
+ result = await client.patch("/dockets/123", body={"reviewed": True})
1126
+ result = await client.delete("/dockets/123")
1127
+
1128
+ # Extra headers merged with the defaults (token is always injected automatically)
1129
+ client = ApiClient("dockets", headers={"constitution-state": "CT"})
1130
+ ```
1131
+
1132
+ **How URL and token are resolved:**
1133
+
1134
+ | Env var | Purpose |
1135
+ |---------|---------|
1136
+ | `MICROSERVICE_URLS` | JSON map: `{"dockets": "https://api.example.com/", "title-search": "https://..."}` |
1137
+ | `INTER_SERVICE_TOKEN` | Bearer token injected as `Authorization: Bearer <token>` |
1138
+ | `AUTH_BYPASS_TOKEN` | Fallback if `INTER_SERVICE_TOKEN` is not set |
1139
+
1140
+ **Exceptions:**
1141
+
1142
+ | Exception | When |
1143
+ |-----------|------|
1144
+ | `ServiceNotConfiguredError` | Service name not found in `MICROSERVICE_URLS` |
1145
+ | `ApiClientError` | Network error or timeout |
1146
+ | `ApiResponseError` | Remote API returned 4xx or 5xx (has `.status_code` and `.response_body` attributes) |
1147
+
1148
+ **Terraform configuration** — set once per environment in Secrets Manager with keys `inter_service_token` and `microservice_urls`. The Lambda and Fargate modules pick them up automatically from infrastructure outputs; no extra variables needed in resource files beyond passing them through:
1149
+
1150
+ ```hcl
1151
+ # In AWS Secrets Manager secret (JSON):
1152
+ # {
1153
+ # "inter_service_token": "my-secret-token",
1154
+ # "microservice_urls": "{\"dockets\": \"https://api-dockets.execute-api.us-east-2.amazonaws.com/\"}",
1155
+ # ...
1156
+ # }
1157
+
1158
+ # In Terraform module call (already handled by the modules):
1159
+ module "my_lambda" {
1160
+ source = "../modules/lambda"
1161
+ inter_service_token = data.terraform_remote_state.infrastructure.outputs.inter_service_token
1162
+ microservice_urls = data.terraform_remote_state.infrastructure.outputs.microservice_urls
1163
+ ...
1164
+ }
1165
+ ```
1166
+
1167
+ ---
1168
+
1169
+ ## 🔍 ModelQueryLambda
1170
+
1171
+ `ModelQueryLambda` is a ready-to-use base class for creating MongoDB query-proxy Lambdas. Instead of writing a custom Lambda every time another microservice needs to read data from your MongoDB, you subclass `ModelQueryLambda`, declare an `allowed_collections` whitelist, and the framework exposes both `find` and `aggregate` operations automatically.
1172
+
1173
+ **Use case:** microservice A needs to query data owned by microservice B. Microservice B deploys one `model-query` Lambda; microservice A calls it via `LambdaInvoker` passing the collection name and query parameters.
1174
+
1175
+ ### Creating a model-query Lambda
1176
+
1177
+ Create `src/lambda/model-query/main.py` in the microservice that owns the data:
1178
+
1179
+ ```python
1180
+ from aws_python_helper import ModelQueryLambda
1181
+
1182
+ class ModelQueryLambda(ModelQueryLambda):
1183
+
1184
+ @property
1185
+ def allowed_collections(self) -> list:
1186
+ return ["orders", "customers"] # whitelist — unlisted collections are rejected
1187
+ ```
1188
+
1189
+ Register the handler in `src/handlers/lambda_handler.py`:
1190
+
1191
+ ```python
1192
+ from aws_python_helper.lambda_standalone.handler import lambda_handler
1193
+
1194
+ model_query_handler = lambda_handler('model-query')
1195
+
1196
+ __all__ = ["model_query_handler"]
1197
+ ```
1198
+
1199
+ ### Query modes
1200
+
1201
+ #### Aggregate pipeline
1202
+
1203
+ Pass a `pipeline` list to run a MongoDB aggregation:
1204
+
1205
+ ```python
1206
+ invoker.invoke("ServiceModelQuery-dev", payload={
1207
+ "session": self.session.to_dict(),
1208
+ "collection": "orders",
1209
+ "pipeline": [
1210
+ {"$match": {"status": "pending"}},
1211
+ {"$sort": {"created_at": -1}},
1212
+ {"$limit": 50},
1213
+ ],
1214
+ })
1215
+ ```
1216
+
1217
+ #### Find with filter
1218
+
1219
+ Pass `filter`, `fields`, `limit`, and/or `skip` to run a `find`:
1220
+
1221
+ ```python
1222
+ invoker.invoke("ServiceModelQuery-dev", payload={
1223
+ "session": self.session.to_dict(),
1224
+ "collection": "orders",
1225
+ "filter": {"customer_id": "abc123", "status": "active"},
1226
+ "fields": {"_id": 1, "total": 1, "status": 1}, # projection
1227
+ "limit": 20,
1228
+ "skip": 0,
1229
+ })
1230
+ ```
1231
+
1232
+ **Note:** `pipeline` and `fields` are mutually exclusive — the framework rejects payloads that include both.
1233
+
1234
+ ### Payload reference
1235
+
1236
+ | Field | Type | Required | Description |
1237
+ |-------|------|----------|-------------|
1238
+ | `session` | `dict` | Yes | Session dict from `self.session.to_dict()` — drives state-scoped DB routing |
1239
+ | `collection` | `str` | Yes | Collection name (must be in `allowed_collections`) |
1240
+ | `pipeline` | `list` | One of | MongoDB aggregation pipeline |
1241
+ | `filter` | `dict` | One of | MongoDB filter document (defaults to `{}`) |
1242
+ | `fields` | `dict` | No | MongoDB projection (only valid with `filter` mode) |
1243
+ | `limit` | `int` | No | Max documents to return |
1244
+ | `skip` | `int` | No | Documents to skip (default `0`) |
1245
+
1246
+ ### Response format
1247
+
1248
+ The Lambda always returns the standard framework envelope:
1249
+
1250
+ ```json
1251
+ {
1252
+ "success": true,
1253
+ "data": [ ... ]
1254
+ }
1255
+ ```
1256
+
1257
+ On validation error (unknown collection, invalid payload):
1258
+
1259
+ ```json
1260
+ {
1261
+ "success": false,
1262
+ "error": "Collection 'unknown' is not allowed. Allowed: orders, customers"
1263
+ }
1264
+ ```
1265
+
1266
+ ### Calling it from another microservice
1267
+
1268
+ ```python
1269
+ import os
1270
+ from aws_python_helper import LambdaInvoker, LambdaInvocationError
1271
+
1272
+ class OrdersPostAPI(API):
1273
+
1274
+ async def process(self):
1275
+ try:
1276
+ response = LambdaInvoker().invoke(
1277
+ os.getenv("ORDERS_MODEL_QUERY_LAMBDA_NAME"),
1278
+ payload={
1279
+ "session": self.session.to_dict(),
1280
+ "collection": "orders",
1281
+ "filter": {"status": "pending"},
1282
+ "limit": 100,
1283
+ },
1284
+ )
1285
+ except LambdaInvocationError as e:
1286
+ self.logger.error(f"model-query invocation failed: {e}")
1287
+ raise ValueError("Could not retrieve orders. Please try again later.")
1288
+
1289
+ if not response.get("success"):
1290
+ raise ValueError("Could not retrieve orders. Please try again later.")
1291
+
1292
+ orders = response.get("data", [])
1293
+ self.set_body({"orders": orders})
1294
+ ```
1295
+
1296
+ ### Session and database routing
1297
+
1298
+ The `session` field in the payload is required. The `ModelQueryLambda` uses `session.state` to resolve the target database — the same state-scoped routing used by all framework components. This means:
1299
+
1300
+ - `{"session": {"state": "connecticut"}, ...}` → queries the `connecticut` database
1301
+ - `{"session": {"state": "new_jersey"}, ...}` → queries the `new_jersey` database
1302
+
1303
+ Always pass `self.session.to_dict()` when calling from an API or Lambda to ensure state propagates correctly.
1304
+
1305
+ ---
1306
+
1011
1307
  ## 🏗️ Architecture Overview
1012
1308
 
1013
1309
  Typical flow for event-driven architectures using this framework:
@@ -1114,6 +1410,8 @@ environment_variables = {
1114
1410
  | `REQUIRE_AUTH` | Optional | Enable authentication middleware (`true`/`false`) |
1115
1411
  | `AUTH_DB_NAME` | If `REQUIRE_AUTH=true` | MongoDB database for token validation |
1116
1412
  | `AUTH_BYPASS_TOKEN` | Optional | Master token to bypass authentication |
1413
+ | `INTER_SERVICE_TOKEN` | Optional | Bearer token for `ApiClient` service-to-service calls |
1414
+ | `MICROSERVICE_URLS` | Optional | JSON map of service name → base URL used by `ApiClient` |
1117
1415
  | `ECS_CLUSTER` | Fargate only | ECS cluster name for `FargateExecutor` |
1118
1416
  | `ECS_SUBNETS` | Fargate only | Comma-separated subnet IDs for Fargate tasks |
1119
1417
  | `CONSTITUTION_STATE` | Fargate only (auto) | State injected automatically by `FargateExecutor` — do not set manually |