ハイブリッドモード、段階的コンポーネント書き換え、依存性注入更新を使用してAngularJSからAngularへ移行します。AngularJSアプリケーションのアップグレード、フレームワーク移行の計画、またはレガシーAngularコードのモダナイゼーション時に使用します。
中文简介正在准备中。当前显示原文说明。
開発者を喜ばせる直感的でスケーラブルで保守可能なAPIを構築するために、RESTとGraphQL APIの設計原則をマスターします。新しいAPIの設計、API仕様のレビュー、またはAPI設計標準の確立時に使用してください。
中文简介正在准备中。当前显示原文说明。
查看安装方法安装前可以查看交给 AI 代理的指令内容。
English | 日本語
開発者を喜ばせ、時間の試練に耐えるRESTとGraphQL APIの設計原則をマスターします。
リソース指向アーキテクチャ
HTTPメソッドのセマンティクス:
GET: リソースの取得(冪等、安全)POST: 新しいリソースの作成PUT: リソース全体の置換(冪等)PATCH: リソースの部分更新DELETE: リソースの削除(冪等)スキーマファースト開発
クエリ構造:
URLバージョニング:
/api/v1/users
/api/v2/users
ヘッダーバージョニング:
Accept: application/vnd.api+json; version=1
クエリパラメータバージョニング:
/api/users?version=1
# 良い: リソース指向エンドポイント
GET /api/users # ユーザーリスト(ページネーション付き)
POST /api/users # ユーザー作成
GET /api/users/{id} # 特定のユーザーを取得
PUT /api/users/{id} # ユーザーを置換
PATCH /api/users/{id} # ユーザーフィールドを更新
DELETE /api/users/{id} # ユーザーを削除
# ネストされたリソース
GET /api/users/{id}/orders # ユーザーの注文を取得
POST /api/users/{id}/orders # ユーザーの注文を作成
# 悪い: アクション指向エンドポイント(避ける)
POST /api/createUser
POST /api/getUserById
POST /api/deleteUser
from typing import List, Optional
from pydantic import BaseModel, Field
class PaginationParams(BaseModel):
page: int = Field(1, ge=1, description="Page number")
page_size: int = Field(20, ge=1, le=100, description="Items per page")
class FilterParams(BaseModel):
status: Optional[str] = None
created_after: Optional[str] = None
search: Optional[str] = None
class PaginatedResponse(BaseModel):
items: List[dict]
total: int
page: int
page_size: int
pages: int
@property
def has_next(self) -> bool:
return self.page < self.pages
@property
def has_prev(self) -> bool:
return self.page > 1
# FastAPIエンドポイント例
from fastapi import FastAPI, Query, Depends
app = FastAPI()
@app.get("/api/users", response_model=PaginatedResponse)
async def list_users(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
status: Optional[str] = Query(None),
search: Optional[str] = Query(None)
):
# フィルターを適用
query = build_query(status=status, search=search)
# 合計をカウント
total = await count_users(query)
# ページを取得
offset = (page - 1) * page_size
users = await fetch_users(query, limit=page_size, offset=offset)
return PaginatedResponse(
items=users,
total=total,
page=page,
page_size=page_size,
pages=(total + page_size - 1) // page_size
)
from fastapi import HTTPException, status
from pydantic import BaseModel
class ErrorResponse(BaseModel):
error: str
message: str
details: Optional[dict] = None
timestamp: str
path: str
class ValidationErrorDetail(BaseModel):
field: str
message: str
value: Any
# 一貫したエラーレスポンス
STATUS_CODES = {
"success": 200,
"created": 201,
"no_content": 204,
"bad_request": 400,
"unauthorized": 401,
"forbidden": 403,
"not_found": 404,
"conflict": 409,
"unprocessable": 422,
"internal_error": 500
}
def raise_not_found(resource: str, id: str):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"error": "NotFound",
"message": f"{resource} not found",
"details": {"id": id}
}
)
def raise_validation_error(errors: List[ValidationErrorDetail]):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail={
"error": "ValidationError",
"message": "Request validation failed",
"details": {"errors": [e.dict() for e in errors]}
}
)
# 使用例
@app.get("/api/users/{user_id}")
async def get_user(user_id: str):
user = await fetch_user(user_id)
if not user:
raise_not_found("User", user_id)
return user
class UserResponse(BaseModel):
id: str
name: str
email: str
_links: dict
@classmethod
def from_user(cls, user: User, base_url: str):
return cls(
id=user.id,
name=user.name,
email=user.email,
_links={
"self": {"href": f"{base_url}/api/users/{user.id}"},
"orders": {"href": f"{base_url}/api/users/{user.id}/orders"},
"update": {
"href": f"{base_url}/api/users/{user.id}",
"method": "PATCH"
},
"delete": {
"href": f"{base_url}/api/users/{user.id}",
"method": "DELETE"
}
}
)
# schema.graphql
# 明確な型定義
type User {
id: ID!
email: String!
name: String!
createdAt: DateTime!
# リレーションシップ
orders(
first: Int = 20
after: String
status: OrderStatus
): OrderConnection!
profile: UserProfile
}
type Order {
id: ID!
status: OrderStatus!
total: Money!
items: [OrderItem!]!
createdAt: DateTime!
# 逆参照
user: User!
}
# ページネーションパターン (Relayスタイル)
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# 型安全のための列挙型
enum OrderStatus {
PENDING
CONFIRMED
SHIPPED
DELIVERED
CANCELLED
}
# カスタムスカラー
scalar DateTime
scalar Money
# クエリルート
type Query {
user(id: ID!): User
users(
first: Int = 20
after: String
search: String
): UserConnection!
order(id: ID!): Order
}
# ミューテーションルート
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
createOrder(input: CreateOrderInput!): CreateOrderPayload!
}
# ミューテーション用の入力型
input CreateUserInput {
email: String!
name: String!
password: String!
}
# ミューテーション用のペイロード型
type CreateUserPayload {
user: User
errors: [Error!]
}
type Error {
field: String
message: String!
}
from typing import Optional, List
from ariadne import QueryType, MutationType, ObjectType
from dataclasses import dataclass
query = QueryType()
mutation = MutationType()
user_type = ObjectType("User")
@query.field("user")
async def resolve_user(obj, info, id: str) -> Optional[dict]:
"""IDで単一ユーザーを解決。"""
return await fetch_user_by_id(id)
@query.field("users")
async def resolve_users(
obj,
info,
first: int = 20,
after: Optional[str] = None,
search: Optional[str] = None
) -> dict:
"""ページネーションされたユーザーリストを解決。"""
# カーソルをデコード
offset = decode_cursor(after) if after else 0
# ユーザーを取得
users = await fetch_users(
limit=first + 1, # hasNextPageをチェックするために1つ余分に取得
offset=offset,
search=search
)
# ページネーション
has_next = len(users) > first
if has_next:
users = users[:first]
edges = [
{
"node": user,
"cursor": encode_cursor(offset + i)
}
for i, user in enumerate(users)
]
return {
"edges": edges,
"pageInfo": {
"hasNextPage": has_next,
"hasPreviousPage": offset > 0,
"startCursor": edges[0]["cursor"] if edges else None,
"endCursor": edges[-1]["cursor"] if edges else None
},
"totalCount": await count_users(search=search)
}
@user_type.field("orders")
async def resolve_user_orders(user: dict, info, first: int = 20) -> dict:
"""ユーザーの注文を解決 (DataLoaderでN+1防止)。"""
# DataLoaderを使用してリクエストをバッチ処理
loader = info.context["loaders"]["orders_by_user"]
orders = await loader.load(user["id"])
return paginate_orders(orders, first)
@mutation.field("createUser")
async def resolve_create_user(obj, info, input: dict) -> dict:
"""新しいユーザーを作成。"""
try:
# 入力を検証
validate_user_input(input)
# ユーザーを作成
user = await create_user(
email=input["email"],
name=input["name"],
password=hash_password(input["password"])
)
return {
"user": user,
"errors": []
}
except ValidationError as e:
return {
"user": None,
"errors": [{"field": e.field, "message": e.message}]
}
from aiodataloader import DataLoader
from typing import List, Optional
class UserLoader(DataLoader):
"""IDでユーザーをバッチロード。"""
async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:
"""単一クエリで複数のユーザーをロード。"""
users = await fetch_users_by_ids(user_ids)
# 結果を入力順序にマップ
user_map = {user["id"]: user for user in users}
return [user_map.get(user_id) for user_id in user_ids]
class OrdersByUserLoader(DataLoader):
"""ユーザーIDで注文をバッチロード。"""
async def batch_load_fn(self, user_ids: List[str]) -> List[List[dict]]:
"""単一クエリで複数ユーザーの注文をロード。"""
orders = await fetch_orders_by_user_ids(user_ids)
# user_idで注文をグループ化
orders_by_user = {}
for order in orders:
user_id = order["user_id"]
if user_id not in orders_by_user:
orders_by_user[user_id] = []
orders_by_user[user_id].append(order)
# 入力順序で返す
return [orders_by_user.get(user_id, []) for user_id in user_ids]
# コンテキストセットアップ
def create_context():
return {
"loaders": {
"user": UserLoader(),
"orders_by_user": OrdersByUserLoader()
}
}
/users、/userではなく)@deprecatedディレクティブを使用还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
ハイブリッドモード、段階的コンポーネント書き換え、依存性注入更新を使用してAngularJSからAngularへ移行します。AngularJSアプリケーションのアップグレード、フレームワーク移行の計画、またはレガシーAngularコードのモダナイゼーション時に使用します。
中文简介正在准备中。当前显示原文说明。
Migrate from AngularJS to Angular using hybrid mode, incremental component rewriting, and dependency injection updates. Use when upgrading AngularJS applications, planning framework migrations, or modernizing legacy Angular code.
中文简介正在准备中。当前显示原文说明。
Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers. Use when designing new APIs, reviewing API specifications, or establishing API design standards.
中文简介正在准备中。当前显示原文说明。
Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.
中文简介正在准备中。当前显示原文说明。
クリーンアーキテクチャ、ヘキサゴナルアーキテクチャ、ドメイン駆動設計を含む実証済みのバックエンドアーキテクチャパターンを実装します。複雑なバックエンドシステムの設計や、保守性向上のための既存アプリケーションのリファクタリング時に使用します。
中文简介正在准备中。当前显示原文说明。
Python asyncio、並行プログラミング、高性能アプリケーション向けの非同期/awaitパターンをマスター。非同期API、並行システム、ノンブロッキング操作が必要なI/OバウンドアプリケーションをPythonで構築する際に使用。
中文简介正在准备中。当前显示原文说明。