366 lines
7.5 KiB
Markdown
366 lines
7.5 KiB
Markdown
# API Migration Guide
|
|
|
|
**Version**: 1.0.0
|
|
**Last Updated**: 2025-11-16
|
|
|
|
Guide for migrating existing API consumers to IGNY8 API Standard v1.0.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The IGNY8 API v1.0 introduces a unified response format that standardizes all API responses. This guide helps you migrate existing code to work with the new format.
|
|
|
|
---
|
|
|
|
## What Changed
|
|
|
|
### Before (Legacy Format)
|
|
|
|
**Success Response**:
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Keyword",
|
|
"status": "active"
|
|
}
|
|
```
|
|
|
|
**Error Response**:
|
|
```json
|
|
{
|
|
"detail": "Not found."
|
|
}
|
|
```
|
|
|
|
### After (Unified Format v1.0)
|
|
|
|
**Success Response**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": 1,
|
|
"name": "Keyword",
|
|
"status": "active"
|
|
},
|
|
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
**Error Response**:
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Resource not found",
|
|
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Migration Steps
|
|
|
|
### Step 1: Update Response Parsing
|
|
|
|
#### Before
|
|
|
|
```python
|
|
response = requests.get(url, headers=headers)
|
|
data = response.json()
|
|
|
|
# Direct access
|
|
keyword_id = data['id']
|
|
keyword_name = data['name']
|
|
```
|
|
|
|
#### After
|
|
|
|
```python
|
|
response = requests.get(url, headers=headers)
|
|
data = response.json()
|
|
|
|
# Check success first
|
|
if data['success']:
|
|
# Extract data from unified format
|
|
keyword_data = data['data'] # or data['results'] for lists
|
|
keyword_id = keyword_data['id']
|
|
keyword_name = keyword_data['name']
|
|
else:
|
|
# Handle error
|
|
error_message = data['error']
|
|
raise Exception(error_message)
|
|
```
|
|
|
|
### Step 2: Update Error Handling
|
|
|
|
#### Before
|
|
|
|
```python
|
|
try:
|
|
response = requests.get(url, headers=headers)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
except requests.HTTPError as e:
|
|
if e.response.status_code == 404:
|
|
print("Not found")
|
|
elif e.response.status_code == 400:
|
|
print("Bad request")
|
|
```
|
|
|
|
#### After
|
|
|
|
```python
|
|
response = requests.get(url, headers=headers)
|
|
data = response.json()
|
|
|
|
if not data['success']:
|
|
# Unified error format
|
|
error_message = data['error']
|
|
field_errors = data.get('errors', {})
|
|
|
|
if response.status_code == 404:
|
|
print(f"Not found: {error_message}")
|
|
elif response.status_code == 400:
|
|
print(f"Validation error: {error_message}")
|
|
for field, errors in field_errors.items():
|
|
print(f" {field}: {', '.join(errors)}")
|
|
```
|
|
|
|
### Step 3: Update Pagination Handling
|
|
|
|
#### Before
|
|
|
|
```python
|
|
response = requests.get(url, headers=headers)
|
|
data = response.json()
|
|
|
|
results = data['results']
|
|
next_page = data['next']
|
|
count = data['count']
|
|
```
|
|
|
|
#### After
|
|
|
|
```python
|
|
response = requests.get(url, headers=headers)
|
|
data = response.json()
|
|
|
|
if data['success']:
|
|
# Paginated response format
|
|
results = data['results'] # Same field name
|
|
next_page = data['next'] # Same field name
|
|
count = data['count'] # Same field name
|
|
else:
|
|
# Handle error
|
|
raise Exception(data['error'])
|
|
```
|
|
|
|
### Step 4: Update Frontend Code
|
|
|
|
#### Before (JavaScript)
|
|
|
|
```javascript
|
|
const response = await fetch(url, { headers });
|
|
const data = await response.json();
|
|
|
|
// Direct access
|
|
const keywordId = data.id;
|
|
const keywordName = data.name;
|
|
```
|
|
|
|
#### After (JavaScript)
|
|
|
|
```javascript
|
|
const response = await fetch(url, { headers });
|
|
const data = await response.json();
|
|
|
|
// Check success first
|
|
if (data.success) {
|
|
// Extract data from unified format
|
|
const keywordData = data.data || data.results;
|
|
const keywordId = keywordData.id;
|
|
const keywordName = keywordData.name;
|
|
} else {
|
|
// Handle error
|
|
console.error('Error:', data.error);
|
|
if (data.errors) {
|
|
// Handle field-specific errors
|
|
Object.entries(data.errors).forEach(([field, errors]) => {
|
|
console.error(`${field}: ${errors.join(', ')}`);
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Helper Functions
|
|
|
|
### Python Helper
|
|
|
|
```python
|
|
def parse_api_response(response):
|
|
"""Parse unified API response format"""
|
|
data = response.json()
|
|
|
|
if data.get('success'):
|
|
# Return data or results
|
|
return data.get('data') or data.get('results')
|
|
else:
|
|
# Raise exception with error details
|
|
error_msg = data.get('error', 'Unknown error')
|
|
errors = data.get('errors', {})
|
|
|
|
if errors:
|
|
error_msg += f": {errors}"
|
|
|
|
raise Exception(error_msg)
|
|
|
|
# Usage
|
|
response = requests.get(url, headers=headers)
|
|
keyword_data = parse_api_response(response)
|
|
```
|
|
|
|
### JavaScript Helper
|
|
|
|
```javascript
|
|
function parseApiResponse(data) {
|
|
if (data.success) {
|
|
return data.data || data.results;
|
|
} else {
|
|
const error = new Error(data.error);
|
|
error.errors = data.errors || {};
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
const response = await fetch(url, { headers });
|
|
const data = await response.json();
|
|
try {
|
|
const keywordData = parseApiResponse(data);
|
|
} catch (error) {
|
|
console.error('API Error:', error.message);
|
|
if (error.errors) {
|
|
// Handle field-specific errors
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Breaking Changes
|
|
|
|
### 1. Response Structure
|
|
|
|
**Breaking**: All responses now include `success` field and wrap data in `data` or `results`.
|
|
|
|
**Migration**: Update all response parsing code to check `success` and extract `data`/`results`.
|
|
|
|
### 2. Error Format
|
|
|
|
**Breaking**: Error responses now use unified format with `error` and `errors` fields.
|
|
|
|
**Migration**: Update error handling to use new format.
|
|
|
|
### 3. Request ID
|
|
|
|
**New**: All responses include `request_id` for debugging.
|
|
|
|
**Migration**: Optional - can be used for support requests.
|
|
|
|
---
|
|
|
|
## Non-Breaking Changes
|
|
|
|
### 1. Pagination
|
|
|
|
**Status**: Compatible - same field names (`count`, `next`, `previous`, `results`)
|
|
|
|
**Migration**: No changes needed, but wrap in success check.
|
|
|
|
### 2. Authentication
|
|
|
|
**Status**: Compatible - same JWT Bearer token format
|
|
|
|
**Migration**: No changes needed.
|
|
|
|
### 3. Endpoint URLs
|
|
|
|
**Status**: Compatible - same endpoint paths
|
|
|
|
**Migration**: No changes needed.
|
|
|
|
---
|
|
|
|
## Testing Migration
|
|
|
|
### 1. Update Test Code
|
|
|
|
```python
|
|
# Before
|
|
def test_get_keyword():
|
|
response = client.get('/api/v1/planner/keywords/1/')
|
|
assert response.status_code == 200
|
|
assert response.json()['id'] == 1
|
|
|
|
# After
|
|
def test_get_keyword():
|
|
response = client.get('/api/v1/planner/keywords/1/')
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data['success'] == True
|
|
assert data['data']['id'] == 1
|
|
```
|
|
|
|
### 2. Test Error Handling
|
|
|
|
```python
|
|
def test_not_found():
|
|
response = client.get('/api/v1/planner/keywords/99999/')
|
|
assert response.status_code == 404
|
|
data = response.json()
|
|
assert data['success'] == False
|
|
assert data['error'] == "Resource not found"
|
|
```
|
|
|
|
---
|
|
|
|
## Migration Checklist
|
|
|
|
- [ ] Update response parsing to check `success` field
|
|
- [ ] Extract data from `data` or `results` field
|
|
- [ ] Update error handling to use unified format
|
|
- [ ] Update pagination handling (wrap in success check)
|
|
- [ ] Update frontend code (if applicable)
|
|
- [ ] Update test code
|
|
- [ ] Test all endpoints
|
|
- [ ] Update documentation
|
|
- [ ] Deploy and monitor
|
|
|
|
---
|
|
|
|
## Rollback Plan
|
|
|
|
If issues arise during migration:
|
|
|
|
1. **Temporary Compatibility Layer**: Add wrapper to convert unified format back to legacy format
|
|
2. **Feature Flag**: Use feature flag to toggle between formats
|
|
3. **Gradual Migration**: Migrate endpoints one module at a time
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
For migration support:
|
|
- Review [API Documentation](API-DOCUMENTATION.md)
|
|
- Check [Error Codes Reference](ERROR-CODES.md)
|
|
- Contact support with `request_id` from failed requests
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-11-16
|
|
**API Version**: 1.0.0
|
|
|