用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tools-only/X-Skills --skill migrate-api命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | migrate-api |
| description | Migrate API to new version with compatibility layers and automated scripts |
| shortcut | mig |
Orchestrate comprehensive API version migrations with automated compatibility layers, breaking change detection, and zero-downtime deployment strategies. This command manages the complete lifecycle of API evolution from initial analysis through deployment and deprecation.
Architecture Approach:
Alternatives Considered:
USE when:
DON'T USE when:
Required:
Recommended:
Step 1: Analysis and Impact Assessment
Step 2: Compatibility Layer Generation
Step 3: Migration Script Creation
Step 4: Routing and Deployment Configuration
Step 5: Validation and Monitoring
migration_plan:
api_name: "User Service API"
source_version: "v1"
target_version: "v2"
breaking_changes:
- endpoint: "/users"
change_type: "field_removed"
field: "username"
severity: "high"
affected_consumers: 15
- endpoint: "/users/{id}"
change_type: "response_structure"
details: "Nested address object"
severity: "medium"
affected_consumers: 8
compatibility_layer:
adapters_generated: 12
transformation_functions: 8
fallback_strategies: 5
migration_scripts:
- script: "001_add_email_unique_constraint.sql"
type: "database"
rollback: "001_rollback_email_constraint.sql"
- script: "002_backfill_address_objects.js"
type: "data_transformation"
estimated_time: "15 minutes"
// API v1 → v2 Migration: User endpoint restructure
// BREAKING: Flattened user object to nested structure
// Source: /api/v1/users/{id}
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105"
}
// Target: /api/v2/users/{id}
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105"
},
"metadata": {
"createdAt": "2024-01-15T10:30:00Z",
"version": "v2"
}
}
// Generated Compatibility Adapter
class UserV1ToV2Adapter {
transform(v1Response) {
return {
id: v1Response.id,
name: v1Response.name,
email: v1Response.email,
: {
: v1Response.,
: v1Response.,
: v1Response.,
: v1Response.
},
: {
: v1Response. || ().(),
:
}
};
}
() {
{
: v2Response.,
: v2Response.,
: v2Response.,
: v2Response.?. || ,
: v2Response.?. || ,
: v2Response.?. || ,
: v2Response.?. ||
};
}
}
routingRules = {
: {
: ,
: ,
: {
: ,
: ,
:
}
}
};
;
-- address table normalized structure
(
id ,
user_id (id),
street (),
city (),
state (),
postal_code (),
created_at ()
);
-- existing flat data to nested structure
(user_id, street, city, state, postal_code)
id, street, city, state, zip
users
street ;
-- foreign key to users table
users address_id (id);
users u
address_id = ua.
user_addresses ua
u. = ua.;
-- old (don
# Schema v1 (Deprecated)
type User {
id: ID!
username: String! # DEPRECATED: Replaced by email
email: String
fullName: String
}
type Query {
user(id: ID!): User
users: [User!]!
}
# Schema v2 (Current)
type Address {
street: String!
city: String!
state: String!
postalCode: String!
country: String!
}
type User {
id: ID!
email: String
String
UserProfile
Address
UserProfile
String
String
String
String
user ID, String User
users UserFilter User
UserFilter
String
String
String
User
ID
String
UserProfile
Address
const resolvers
async _, args, context >
const version context.apiVersion;
if version 'v1'
// Legacy lookup by username
const user await db.users.findByUsernameargs.id;
return
user,
user.username, // Still supported in v1
`{user.firstName {user.lastName`
;
else
// Modern lookup by email or ID
const user await db.users.findOne
args.email ? args.email args.id
;
return user;
,
// Compatibility field resolver for deprecated username
user, args, context >
if context.apiVersion 'v1'
return user.username;
// Add deprecation warning to response headers
context.res.set'Deprecation', 'username field is deprecated. Use email.';
return user.username user.email.split'@';
,
// Transform flat structure to nested for v2+
user >
user.firstName user.fullName?.split' ',
user.lastName user.fullName?.split' ',
user.fullName,
user.avatar
;
// Automated Compatibility Tests
describe'GraphQL Migration Tests', >
test'v1 clients can still with username', async >
const ` user username email `;
const result await executeQuery, 'v1' ;
expectresult.data.user.username.toBe'johndoe';
;
test'v2 clients receive nested profile structure', async >
const ` user profile firstName lastName `;
const result await executeQuery, 'v2' ;
expectresult.data.user.profile.firstName.toBe'John';
;
test'deprecated fields trigger warning headers', async >
const ` user username `;
const response await executeQueryWithHeaders, 'v2' ;
expectresponse.headers.get'Deprecation'.toContain'username field is deprecated';
;
;
// service_v1.proto (Deprecated)
syntax = "proto3";
package user.v1;
message User {
int32 id = 1;
string username = 2;
string email = 3;
string full_name = 4;
}
message GetUserRequest {
int32 id = 1;
}
message GetUserResponse {
User user = 1;
}
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
// service_v2.proto (Current)
syntax = "proto3";
package user.v2;
import "google/protobuf/timestamp.proto";
message Address {
string street = 1;
string city = 2;
string state = 3;
string postal_code = 4;
string country = 5;
}
message UserProfile {
string first_name = 1;
string last_name = 2;
string display_name = 3;
string avatar_url = 4;
}
message User {
int32 id = 1;
string email = 2;
UserProfile profile = 3;
Address address = 4;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp updated_at = 6;
}
message GetUserRequest {
oneof identifier {
int32 id = 1;
string email = 2;
}
}
message GetUserResponse {
User user = 1;
}
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
// Compatibility Bridge Service
package user.bridge;
import "user/v1/service.proto";
import "user/v2/service.proto";
class UserServiceBridge {
constructor() {
this.v2Service = new user.v2.UserServiceClient('localhost:50052');
}
// Implement v1 interface while calling v2 backend
async GetUser(call, callback) {
const v1Request = call.request;
// Transform v1 request to v2 format
const v2Request = {
id: v1Request.id
};
try {
const v2Response = await this.v2Service.GetUser(v2Request);
const v2User = v2Response.user;
// Transform v2 response back to v1 format
const v1User = {
id: v2User.id,
username: v2User.email.split('@')[0], // Synthesize username
email: v2User.email,
full_name: v2User.profile.display_name
};
callback(null, { user: v1User });
// Log deprecation warning
console.warn(`[DEPRECATED] v1 API used by client ${call.getPeer()}`);
} catch (error) {
callback(error);
}
}
}
// Envoy gRPC Gateway Configuration for Version Routing
static_resources:
listeners:
- name: user_service_listener
address:
socket_address:
address: 0.0.0.0
port_value: 50051
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: grpc_json
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: user_service
domains: ["*"]
routes:
# Route v1 requests to compatibility bridge
- match:
prefix: "/user.v1.UserService"
route:
cluster: user_service_v1_bridge
timeout: 30s
# Route v2 requests to native service
- match:
prefix: "/user.v2.UserService"
route:
cluster: user_service_v2
timeout: 30s
clusters:
- name: user_service_v1_bridge
connect_timeout: 1s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: user_service_v1_bridge
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: user-bridge-service
port_value: 50051
- name: user_service_v2
connect_timeout: 1s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: user_service_v2
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: user-service-v2
port_value: 50052
// Migration Testing Framework
describe('gRPC API Migration Tests', () => {
const v1Client = new user.v1.UserServiceClient('localhost:50051');
const v2Client = new user.v2.UserServiceClient('localhost:50052');
test('v1 clients receive compatible responses', async () => {
const request = new user.v1.GetUserRequest({ id: 123 });
const response = await v1Client.GetUser(request);
expect(response.user.username).toBeDefined();
expect(response.user.email).toBeDefined();
expect(response.user.full_name).toBeDefined();
});
test('v2 clients receive enhanced data structures', async () => {
const request = new user.v2.GetUserRequest({ email: 'john@example.com' });
const response = await v2Client.GetUser(request);
expect(response.user.profile).toBeDefined();
expect(response.user.profile.first_name).toBeDefined();
expect(response.user.address).toBeDefined();
});
test('data consistency between versions', async () => {
const userId = 123;
const v1Response = await v1Client.GetUser({ id: userId });
const v2Response = await v2Client.GetUser({ id: userId });
// Verify transformed data matches
expect(v1Response.user.email).toBe(v2Response.user.email);
expect(v1Response.user.full_name).toBe(v2Response.user.profile.display_name);
});
});
Basic Usage:
/migrate-api \
--source=v1 \
--target=v2 \
--api-spec=openapi.yaml \
--consumers=consumer-registry.json
Available Options:
--strategy <type> - Migration deployment strategy
canary - Gradual traffic shifting (default, safest)blue-green - Instant switchover with rollback capabilityrolling - Progressive deployment across instancesfeature-flag - Application-controlled version selectionparallel-run - Run both versions, compare results--compatibility-mode <mode> - Backward compatibility approach
adapter - Transform requests/responses between versions (default)proxy - Route old endpoints to new implementationshim - Minimal compatibility layer, consumers must adaptnone - No compatibility, hard cutover (dangerous)--deprecation-period <duration> - Support window for old version
3months - Short deprecation (minor changes)6months - Standard deprecation (default)12months - Extended support (major changes)custom:YYYY-MM-DD - Specific sunset date--breaking-changes-policy <policy> - How to handle breaking changes
require-adapters - Force compatibility layer generationwarn-consumers - Send notifications, allow migration timeblock-deployment - Prevent deploy until consumers updateddocument-only - Just update documentation--traffic-split <percentage> - Initial new version traffic
0 (dark launch)10 for 10% canary deployment--rollback-threshold <percentage> - Error rate trigger for auto-rollback
5 (5% error rate)2 for strict quality requirements--test-coverage-required <percentage> - Minimum test coverage before deploy
80--generate-migration-guide - Create consumer migration documentation
--dry-run - Simulate migration without making changes
Common Errors and Solutions:
Error: Breaking changes detected without compatibility layer
ERROR: 15 breaking changes detected in target API version
- Removed field: User.username (affects 12 endpoints)
- Changed type: Order.total (string → number)
- Renamed endpoint: /users/search → /users/find
Solution: Either:
1. Add --compatibility-mode=adapter to generate transformers
2. Create manual adapters in adapters/ directory
3. Use --breaking-changes-policy=warn-consumers for grace period
Error: Consumer test failures in compatibility mode
ERROR: 3 consumer integration tests failed with v2 adapter
- AcmeApp: Expected username field, received null
- BetaCorp: Response schema validation failed
- GammaInc: Authentication token format mismatch
Solution:
1. Review consumer test failures: npm run test:consumers
2. Update adapters to handle edge cases
3. Contact affected consumers for migration coordination
4. Use --traffic-split=0 for dark launch until resolved
Error: Database migration rollback required
ERROR: Migration script 003_add_foreign_keys.sql failed
Constraint violation: user_addresses.user_id references missing users
Solution:
1. Execute rollback: psql -f 003_rollback.sql
2. Fix data inconsistencies: npm run data:cleanup
3. Re-run migration with --validate-data flag
4. Check migration logs: tail -f logs/migration.log
Error: Traffic spike indicating rollback needed
WARNING: v1 traffic increased from 10% to 45% in 5 minutes
Possible rollback from consumers due to v2 issues
Solution:
1. Check v2 error rates: /metrics/api/v2/errors
2. Review recent v2 deployment logs
3. Pause traffic shift: kubectl patch deployment api-gateway --type=json -p='[{"op":"replace","path":"/spec/template/spec/containers/0/env/1/value","value":"10"}]'
4. Investigate root cause before continuing rollout
Error: Incompatible schema versions in distributed system
ERROR: Service A running v2 schema, Service B still on v1
Message deserialization failed: unknown field 'profile'
Solution:
1. Implement schema registry: npm install @kafkajs/schema-registry
2. Use forward-compatible schemas with optional fields
3. Deploy with version negotiation: --enable-version-negotiation
4. Coordinate deployment order across services
DO:
DON'T:
TIPS:
/api-contract-generator - Generate OpenAPI specs from code/api-versioning-manager - Manage multiple API versions/api-documentation-generator - Update docs for new versions/api-monitoring-dashboard - Track version adoption metrics/api-security-scanner - Audit security across versions/api-load-tester - Performance test both versions/api-sdk-generator - Create client libraries for v2Migration Performance Impact:
Optimization Strategies:
Capacity Planning:
Version Transition Security:
Security Checklist:
Issue: Consumers report intermittent failures after migration
Issue: Adapter performance degrading over time
Issue: Version metrics not appearing in dashboard
Issue: Rollback triggered unexpectedly
v1.0.0 (2024-01-15)
v1.1.0 (2024-02-10)
v1.2.0 (2024-03-05)
v1.3.0 (2024-04-20)
v2.0.0 (2024-06-15)
v2.1.0 (2024-08-30) - Current