This implementation adds the /api/exports endpoint for the GrantFox FWC26 (Stellar Wave) campaign, providing access to materialized export artifacts with signed download URLs.
- Created a new Express router for the
/api/exportsendpoint - Implements
GET /api/exportswith the following features:- Authentication required (bearer token)
- Input validation using Zod schema
- Pagination support (limit: 1-100, offset: >=0)
- Format filtering (csv or json)
- Developer profile verification
- Signed download URL generation with configurable TTL
- Standardized error envelope
Key Features:
- Returns paginated list of export artifacts for the authenticated developer
- Each export includes: id, developerId, format, exportedAt, expiresAt, downloadUrl
- Download URLs are signed and expire per
EXPORT_SIGNED_URL_TTL_SECONDS(default: 900s / 15 minutes) - Non-admin users can only access their own exports
- Proper error handling with standardized error codes
- Added import for
createExportsRouterfrom./exports.js - Added
ReportExporterServicetoApiRouterDepsinterface - Mounted
/api/exportsrouter when bothreportExporterServiceanddeveloperRepositorydependencies are available - Registered after
/api/exports/schedulesto ensure proper route matching order
- Added
/api/exportsendpoint definition with:- Comprehensive request/response schemas
- Example request and response bodies
- Security requirements (bearerAuth)
- Query parameter definitions
- Error response definitions
- References to existing ErrorResponse schema
Endpoint Specification:
GET /api/exports
Query Parameters:
- limit (optional, default: 20, max: 100): Maximum records to return
- offset (optional, default: 0): Pagination offset
- developerId (optional): Filter by developer ID (admin-only)
- format (optional): Filter by format ('csv' or 'json')
Response:
{
"data": [
{
"id": "uuid",
"developerId": "string",
"format": "csv" | "json",
"exportedAt": "ISO-8601 timestamp",
"expiresAt": "ISO-8601 timestamp",
"downloadUrl": "signed URL"
}
],
"pagination": {
"limit": number,
"offset": number,
"total": number
}
}
- Created comprehensive test suite with 7 test cases:
- Returns 401 when not authenticated
- Returns 403 when user has no developer profile
- Returns 200 with empty data when no exports exist
- Returns 200 with export records when they exist
- Filters by format when specified
- Respects pagination parameters
- Has standardized error envelope
Test Coverage:
- Authentication and authorization validation
- Developer profile verification
- Empty state handling
- Data retrieval and transformation
- Format filtering
- Pagination
- Error response structure
- Requires valid bearer token (via
requireAuthmiddleware) - Verifies developer profile exists for authenticated user
- Non-admin users can only access their own exports
- Uses standardized error codes (UNAUTHORIZED, DEVELOPER_NOT_FOUND)
- S3 credentials are never returned in responses
- Download URLs are signed with limited TTL (configurable via
EXPORT_SIGNED_URL_TTL_SECONDS) - Sensitive data is properly redacted
- All query parameters are validated using Zod schema
- Limit is constrained to 1-100 range
- Offset must be >= 0
- Format must be 'csv' or 'json'
Basic Request:
curl -X GET \
https://api.callora.dev/api/exports \
-H 'Authorization: Bearer YOUR_TOKEN'With Pagination:
curl -X GET \
'https://api.callora.dev/api/exports?limit=10&offset=0' \
-H 'Authorization: Bearer YOUR_TOKEN'Filter by Format:
curl -X GET \
'https://api.callora.dev/api/exports?format=csv' \
-H 'Authorization: Bearer YOUR_TOKEN'Success (200 OK):
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"developerId": "dev-123",
"format": "csv",
"exportedAt": "2026-06-01T00:00:00.000Z",
"expiresAt": "2026-06-08T00:00:00.000Z",
"downloadUrl": "https://s3.example.com/exports/dev-123/2026-06-01.csv?expires=1234567890&signature=abc123"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1
}
}Error (401 Unauthorized):
{
"code": "UNAUTHORIZED",
"message": "Authentication required",
"requestId": "req-abc123def456"
}Error (403 Forbidden):
{
"code": "DEVELOPER_NOT_FOUND",
"message": "No developer profile found for this account",
"requestId": "req-abc123def456"
}The endpoint respects the following environment variables:
EXPORT_SIGNED_URL_TTL_SECONDS: TTL for signed download URLs (default: 900 / 15 minutes)
The endpoint requires the following services to be configured:
ReportExporterService: For listing exports and generating signed URLsDeveloperRepository: For verifying developer profiles
✅ Security:
- Input validation at boundary
- Standardized error envelope
- Signed URLs with limited TTL
- No credential exposure
✅ Testing:
- Focused test suite with 7 test cases
- Covers all major code paths
- Validates error handling
✅ Documentation:
- OpenAPI specification with examples
- Inline code comments
- Clear request/response examples
✅ Code Quality:
- Follows existing code patterns
- Type-safe with TypeScript
- Proper error handling
- Structured logging ready (uses requestId)
src/routes/exports.ts(NEW)src/routes/exports.test.ts(NEW)src/routes/index.ts(MODIFIED)docs/openapi.json(MODIFIED)
IMPLEMENTATION_SUMMARY_ISSUE_770.md(THIS FILE)
To fully enable this endpoint in production:
- Ensure
ReportExporterServiceis instantiated and passed tocreateApiRouter - Configure
EXPORT_SIGNED_URL_TTL_SECONDSas needed - Verify object storage credentials are properly configured
- Run the daily export worker to generate export artifacts
- Closes #770
- Related to #398 (scheduled developer report exports)