expo-api-audit
Comprehensive audit of Expo/React Native app API integration layer. Use when asked to: (1) Review API interactions, auth handling, or token management, (2) Find hardcoded data or screens bypassing API, (3) Verify user interactions properly sync to backend, (4) Analyze offline behavior and caching, (5) Audit Orval/OpenAPI code generation, (6) Check for API security issues. Supports TanStack Query, Zustand, axios, Expo Router, expo-secure-store, and expo-constants patterns.
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o expo-api-audit.zip https://jpskill.com/download/17838.zip && unzip -o expo-api-audit.zip && rm expo-api-audit.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17838.zip -OutFile "$d\expo-api-audit.zip"; Expand-Archive "$d\expo-api-audit.zip" -DestinationPath $d -Force; ri "$d\expo-api-audit.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
expo-api-audit.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
expo-api-auditフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Expo API 統合監査
概要
このスキルは、Expo (React Native) TypeScript アプリの API 統合レイヤーを監査し、ギャップ、ハードコードされたデータ、認証の問題、オフライン動作の問題を特定します。Expo Router、expo-secure-store、および expo-constants を使用するアプリ向けに設計されています。
入力
開始する前に、ユーザーから以下を収集します。
- 既知の問題 (オプション): 破損している疑いのある特定の画面またはフロー
- 出力形式の優先度: markdown レポート、JSON の調査結果、修正 PR、またはタスクリスト
- 範囲: 完全な監査または特定の焦点 (認証のみ、オフラインのみなど)
ツールに関する注意: ripgrep
rg (ripgrep) が利用可能な場合は、grep の代わりに使用してください。大幅に高速であり、node_modules/.git を自動的に無視します。このスキルのすべての grep コマンドには、rg の同等のコマンドがあります。
# ripgrep が利用可能かどうかを確認します
which rg && echo "Use rg commands" || echo "Falling back to grep"
# 同等のコマンド:
# grep -rn "pattern" --include="*.ts" | grep -v node_modules
# rg "pattern" -t ts
# grep -rln "pattern" --include="*.ts" | grep -v node_modules
# rg -l "pattern" -t ts
フェーズ 1: 発見
監査の前にメンタルモデルを構築します。これらのコマンドを実行して、キーファイルを見つけます。
# Orval の設定と生成されたフック
find . -name "orval.config.*" -o -name "*.orval.ts" 2>/dev/null | head -5
find . -type d -name "generated" | xargs -I{} ls {} 2>/dev/null | head -20
# API クライアント/ミューテーター
rg -l "customInstance|axios\.create|baseURL" -t ts | head -10
# Zustand ストア
rg -l "create\(" -t ts | xargs rg -l "zustand|devtools" 2>/dev/null | head -20
# OpenAPI 仕様
find . -name "openapi.json" -o -name "openapi.yaml" -o -name "swagger.json" 2>/dev/null
# 認証インフラストラクチャ
rg -l "TokenManager|refreshToken|Bearer|interceptor" -t ts
# Expo の設定 (環境変数、API URL)
cat app.config.js 2>/dev/null || cat app.config.ts 2>/dev/null || cat app.json
rg "expoConfig|Constants\.manifest" -t ts
キーファイルのマッピング
| コンポーネント | 典型的な場所 | チェックする内容 |
|---|---|---|
| Orval の設定 | orval.config.ts |
クライアントタイプ、ミューテーターパス、出力ディレクトリ |
| 生成されたフック | /api/generated/ または /src/api/ |
OpenAPI 仕様との完全性 |
| Axios クライアント | /api/ または /services/ |
インターセプター、基本設定 |
| トークンマネージャー | /services/auth/ |
expo-secure-store を使用する必要がある |
| Zustand ストア | /stores/ |
サーバーの状態を保持しているか (違反?) |
| OpenAPI 仕様 | /docs/api/ または root |
最終更新日、バージョン |
| Expo の設定 | app.config.js または app.json |
extra の API URL、環境変数 |
| 画面 | /app/ (Expo Router) |
ファイルベースのルーティング |
フェーズ 2: API レイヤーの監査
2.1 Orval の生成の健全性
# 生成されたコードが仕様と一致するかどうかを確認します
npx orval --dry-run 2>&1 | head -50
# エンドポイントの数を比較します
jq '.paths | keys | length' docs/api/openapi.json # 仕様のエンドポイント
find ./api/generated -name "*.ts" -exec grep -l "useQuery\|useMutation" {} \; | wc -l
確認事項:
- [ ] 生成された型が OpenAPI スキーマと一致する
- [ ] すべての仕様のエンドポイントに対応するフックがある
- [ ] カスタムミューテーターが認証ヘッダーを挿入する
- [ ] クエリのデフォルトが適切である (staleTime、gcTime、retry)
2.2 認証トークンの処理
次のパターンについて axios クライアントを検査します。
// 必須: リクエストインターセプターがトークンを挿入する
config.headers.Authorization = `Bearer ${token}`
// 必須: 有効期限が切れる前に事前更新する
if (tokenExpiresWithin(600)) await refreshToken()
// 必須: 401 レスポンスが更新をトリガーする
if (error.response?.status === 401) { /* refresh logic */ }
// 必須: 更新の重複排除
if (isRefreshing) return pendingRefreshPromise
// 必須: 更新の失敗がログアウトをトリガーする
clearTokens(); navigate('/auth')
Expo 固有のチェック:
# トークンのストレージ - AsyncStorage ではなく、expo-secure-store を使用する必要があります
rg "AsyncStorage.*token|token.*AsyncStorage" -t ts -i # 発見された場合は BAD
rg "SecureStore|expo-secure-store" -t ts # GOOD - 存在するはず
# 環境変数 - expo-constants または app.config.js を使用する必要があります
rg "process\.env\." -t ts # Expo では BAD (本番環境では動作しません)
rg "Constants\.expoConfig|Constants\.manifest" -t ts # GOOD - Expo の方法
grep -l "extra:" app.config.* 2>/dev/null # 設定ベースの環境変数
危険信号:
- ソースコードにハードコードされたトークンまたは API キー
- AsyncStorage のトークン (
expo-secure-storeを使用する必要がある) process.envを使用する API URL (expo-constantsを代わりに使用する)- 更新の重複排除がない (並列更新呼び出し)
- 無限更新ループ (更新エンドポイントが 401 を返す)
- トークンチェックとリクエスト間の競合状態
2.3 直接的な API 違反
Orval で生成されたフックをバイパスする呼び出しを見つけます。
# 生の fetch (生成されたフックを使用する必要があります)
rg "fetch\(" -t ts -t tsx --glob '!*.d.ts'
# 直接的な axios (orval ミューテーターを使用する必要があります)
rg "axios\.|axios\(" -t ts -t tsx --glob '!*orval*'
# 手動の useQuery (生成されたものを使用する必要があります)
rg "useQuery\(|useMutation\(" -t ts -t tsx --glob '!*generated*'
# ハードコードされた URL
rg "https?://[^\"']*api" -t ts -t tsx
# Expo 固有: process.env の使用 (Expo 本番ビルドでは動作しません)
rg "process\.env\." -t ts -t tsx # 代わりに Constants.expoConfig.extra を使用する必要があります
各調査結果を分類します。
- 正当: サードパーティ API、ファイルアップロード、WebSocket
- 違反: 生成されたフックを使用していないバックエンド呼び出し
- ハードコード:
expo-constantsを使用する必要がある URL - Env エラー:
process.envの使用 (Expo 本番環境で中断)
フェーズ 3: 画面データ監査
/app/ (Expo Router) または /screens/ の各画面について:
3.1 データソースの分類
| カテゴリ | パターン | ステータス |
|---|---|---|
| API データ | useGet*(), use*Query() |
✓ 正しい |
| キャッシュ | React Query が古いデータを提供 | ✓ 予想される |
| Zustand | ストア内のビジネスデータ | ⚠️ サーバーの状態であるべきか? |
| ハードコード | モック配列、プレースホルダーオブジェクト | ❌ フラグ |
| 派生 | API データからの計算 | ⚠️ バックエンドで計算する必要があるか確認する |
# API フックのない画面を見つける (疑わしい)
for
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Expo API Integration Audit
Overview
This skill audits an Expo (React Native) TypeScript app's API integration layer to identify gaps, hardcoded data, auth issues, and offline behavior problems. Designed for apps using Expo Router, expo-secure-store, and expo-constants.
Inputs
Before starting, gather from the user:
- Known issues (optional): Specific screens or flows suspected to be broken
- Output format preference: markdown report, JSON findings, fix PRs, or task list
- Scope: Full audit or specific focus (auth only, offline only, etc.)
Tool Note: ripgrep
If rg (ripgrep) is available, use it instead of grep—it's significantly faster and automatically ignores node_modules/.git. All grep commands in this skill have rg equivalents:
# Check if ripgrep is available
which rg && echo "Use rg commands" || echo "Falling back to grep"
# Equivalents:
# grep -rn "pattern" --include="*.ts" | grep -v node_modules
# rg "pattern" -t ts
# grep -rln "pattern" --include="*.ts" | grep -v node_modules
# rg -l "pattern" -t ts
Phase 1: Discovery
Build a mental model before auditing. Run these commands to locate key files:
# Orval config and generated hooks
find . -name "orval.config.*" -o -name "*.orval.ts" 2>/dev/null | head -5
find . -type d -name "generated" | xargs -I{} ls {} 2>/dev/null | head -20
# API client/mutator
rg -l "customInstance|axios\.create|baseURL" -t ts | head -10
# Zustand stores
rg -l "create\(" -t ts | xargs rg -l "zustand|devtools" 2>/dev/null | head -20
# OpenAPI spec
find . -name "openapi.json" -o -name "openapi.yaml" -o -name "swagger.json" 2>/dev/null
# Auth infrastructure
rg -l "TokenManager|refreshToken|Bearer|interceptor" -t ts
# Expo config (environment variables, API URLs)
cat app.config.js 2>/dev/null || cat app.config.ts 2>/dev/null || cat app.json
rg "expoConfig|Constants\.manifest" -t ts
Key File Mapping
| Component | Typical Location | What to Check |
|---|---|---|
| Orval config | orval.config.ts |
client type, mutator path, output dir |
| Generated hooks | /api/generated/ or /src/api/ |
completeness vs OpenAPI spec |
| Axios client | /api/ or /services/ |
interceptors, base config |
| Token manager | /services/auth/ |
must use expo-secure-store |
| Zustand stores | /stores/ |
which hold server state (violation?) |
| OpenAPI spec | /docs/api/ or root |
last modified, version |
| Expo config | app.config.js or app.json |
API URLs in extra, env vars |
| Screens | /app/ (Expo Router) |
file-based routing |
Phase 2: API Layer Audit
2.1 Orval Generation Health
# Check if generated code matches spec
npx orval --dry-run 2>&1 | head -50
# Compare endpoint counts
jq '.paths | keys | length' docs/api/openapi.json # endpoints in spec
find ./api/generated -name "*.ts" -exec grep -l "useQuery\|useMutation" {} \; | wc -l
Verify:
- [ ] Generated types match OpenAPI schemas
- [ ] All spec endpoints have corresponding hooks
- [ ] Custom mutator injects auth headers
- [ ] Query defaults sensible (staleTime, gcTime, retry)
2.2 Auth Token Handling
Inspect the axios client for these patterns:
// REQUIRED: Request interceptor injects token
config.headers.Authorization = `Bearer ${token}`
// REQUIRED: Pre-emptive refresh before expiry
if (tokenExpiresWithin(600)) await refreshToken()
// REQUIRED: 401 response triggers refresh
if (error.response?.status === 401) { /* refresh logic */ }
// REQUIRED: Refresh deduplication
if (isRefreshing) return pendingRefreshPromise
// REQUIRED: Failed refresh triggers logout
clearTokens(); navigate('/auth')
Expo-specific checks:
# Token storage - MUST use expo-secure-store, not AsyncStorage
rg "AsyncStorage.*token|token.*AsyncStorage" -t ts -i # BAD if found
rg "SecureStore|expo-secure-store" -t ts # GOOD - should exist
# Environment variables - should use expo-constants or app.config.js
rg "process\.env\." -t ts # BAD for Expo (won't work in production)
rg "Constants\.expoConfig|Constants\.manifest" -t ts # GOOD - Expo way
grep -l "extra:" app.config.* 2>/dev/null # Config-based env vars
Red flags:
- Hardcoded tokens or API keys in source
- Tokens in AsyncStorage (must use
expo-secure-store) - API URLs using
process.env(useexpo-constantsinstead) - No refresh deduplication (parallel refresh calls)
- Infinite refresh loops (refresh endpoint returns 401)
- Race conditions between token check and request
2.3 Direct API Violations
Find calls bypassing Orval-generated hooks:
# Raw fetch (should use generated hooks)
rg "fetch\(" -t ts -t tsx --glob '!*.d.ts'
# Direct axios (should use orval mutator)
rg "axios\.|axios\(" -t ts -t tsx --glob '!*orval*'
# Manual useQuery (should use generated)
rg "useQuery\(|useMutation\(" -t ts -t tsx --glob '!*generated*'
# Hardcoded URLs
rg "https?://[^\"']*api" -t ts -t tsx
# Expo-specific: process.env usage (won't work in Expo production builds)
rg "process\.env\." -t ts -t tsx # Should use Constants.expoConfig.extra instead
Classify each finding:
- Legitimate: Third-party APIs, file uploads, WebSocket
- Violation: Backend calls not using generated hooks
- Hardcoded: URLs that should use
expo-constants - Env error:
process.envusage (breaks in Expo production)
Phase 3: Screen Data Audit
For each screen in /app/ (Expo Router) or /screens/:
3.1 Data Source Classification
| Category | Pattern | Status |
|---|---|---|
| API Data | useGet*(), use*Query() |
✓ Correct |
| Cached | React Query serving stale | ✓ Expected |
| Zustand | Business data in store | ⚠️ Should be server state? |
| Hardcoded | Mock arrays, placeholder objects | ❌ Flag |
| Derived | Calculations from API data | ⚠️ Check if backend should compute |
# Find screens with no API hooks (suspicious)
for f in $(find ./app -name "*.tsx" | grep -v "_layout"); do
if ! grep -q "use.*Query\|use.*Mutation\|useGet\|usePost\|usePut\|useDelete" "$f"; then
echo "NO API HOOKS: $f"
fi
done
# Find hardcoded arrays/objects (rg version)
rg "useState\(\[" -t tsx
rg "const.*=.*\[\{" -t tsx
3.2 User Interaction Audit
Every user action that modifies data must trigger a mutation:
| Action Type | Required Pattern |
|---|---|
| Form submit | useMutation + onSuccess invalidation |
| Toggle/switch | Mutation or debounced mutation |
| Delete | Mutation with optimistic update or confirmation |
| Drag/reorder | Mutation on drop |
| Settings change | Mutation (not just Zustand) |
# Forms without mutations (suspicious)
for f in $(rg -l "onSubmit|handleSubmit" -t tsx); do
if ! rg -q "useMutation|usePost|usePut|usePatch" "$f"; then
echo "FORM WITHOUT MUTATION: $f"
fi
done
# Button handlers to audit
rg "onPress=|onClick=" -t tsx | head -50
3.3 Zustand Store Audit
Zustand should hold client-only state. Flag if stores contain:
- Data that should come from API (users, items, records)
- Business logic calculations (should be server-side)
- Duplicates of React Query cache
# List all Zustand stores and their state shape
rg "interface.*State|type.*State" stores/ -t ts
# Check for persist middleware (may duplicate RQ cache)
rg "persist\(" stores/ -t ts
Valid Zustand uses: auth state, UI preferences, navigation state, draft forms Invalid: fetched entities, computed business data, anything with an API endpoint
Phase 4: Offline Behavior Audit
4.1 Network Mode Configuration
# Check query client defaults
rg "networkMode" -t ts
# Check for offline detection (Expo supports both)
rg "NetInfo|@react-native-community/netinfo" -t ts # Community package
rg "expo-network|Network\.getNetworkStateAsync" -t ts # Expo native package
rg "isConnected|isInternetReachable" -t ts
Expected patterns:
- Queries:
networkMode: 'offlineFirst'(serve stale when offline) - Mutations:
networkMode: 'online'or queue implementation
4.2 Offline Scenarios to Test
| Scenario | Expected Behavior | Check |
|---|---|---|
| Load screen offline | Shows cached data or empty state | isLoading vs isFetching |
| Submit form offline | Queue or clear error message | Mutation error handling |
| Token refresh offline | Graceful failure, retry on reconnect | Interceptor error path |
| App backgrounded then offline | Cache persists | AsyncStorage/MMKV check |
| Reconnect after offline | Automatic refetch | refetchOnReconnect |
# Check for offline queue implementation
rg "offlineQueue|pendingMutations|syncQueue" -t ts
# Check cache persistence
rg "persistQueryClient|createAsyncStoragePersister|MMKV" -t ts
4.3 Error Boundary Coverage
# Find error boundaries
rg "ErrorBoundary|errorElement|onError" -t tsx
# Check query error handling
rg "isError|error:" -t tsx | head -30
Phase 5: Report Generation
Structure findings by severity:
Critical (Auth/Security)
- Hardcoded credentials
- Tokens in AsyncStorage (must use
expo-secure-store) process.envfor secrets (won't work in Expo builds)- Auth bypass possibilities
- Missing 401 handling
Major (Data Integrity)
- Forms not syncing to API
- User actions not persisted
- Business logic in frontend
- Stale data served as fresh
- API URLs not using
expo-constants
Medium (Reliability)
- Missing error handling
- No offline fallback
- Race conditions
- Missing loading states
Minor (Code Quality)
- Direct API calls (should use generated)
- Zustand holding server state
- Inconsistent patterns
Output Templates
Markdown Report
# API Integration Audit Report
## Executive Summary
- X critical issues, Y major, Z medium
## Findings
### [CRITICAL] Tokens Stored in AsyncStorage
**File**: `services/auth/tokenStorage.ts:15`
**Issue**: JWT tokens stored in AsyncStorage instead of expo-secure-store
**Fix**: Migrate to `import * as SecureStore from 'expo-secure-store'`
### [CRITICAL] process.env in Production Code
**File**: `api/client.ts:8`
**Issue**: `process.env.API_URL` won't work in Expo production builds
**Fix**: Use `Constants.expoConfig?.extra?.apiUrl` from expo-constants
### [MAJOR] Profile Form Not Syncing
**File**: `app/profile/edit.tsx`
**Issue**: Form saves to Zustand only, no API call
**Fix**: Add `useUpdateProfile` mutation on submit
JSON Findings
{
"summary": { "critical": 2, "major": 2, "medium": 5 },
"findings": [
{
"severity": "critical",
"category": "auth",
"file": "services/auth/tokenStorage.ts",
"line": 15,
"issue": "Tokens in AsyncStorage instead of expo-secure-store",
"fix": "Migrate to SecureStore.setItemAsync/getItemAsync"
},
{
"severity": "critical",
"category": "config",
"file": "api/client.ts",
"line": 8,
"issue": "process.env.API_URL won't work in Expo builds",
"fix": "Use Constants.expoConfig.extra.apiUrl"
}
]
}
Quick Reference Commands
# Full audit (grep version)
echo "=== Direct fetch ===" && grep -rn "fetch(" --include="*.ts" --include="*.tsx" | grep -v node_modules | grep -v ".d.ts"
echo "=== Direct axios ===" && grep -rn "axios\." --include="*.ts" --include="*.tsx" | grep -v node_modules | grep -v orval
echo "=== Hardcoded URLs ===" && grep -rn "http://\|https://" --include="*.ts" --include="*.tsx" | grep -v node_modules
echo "=== useState arrays ===" && grep -rn "useState\(\[" --include="*.tsx" | grep -v node_modules
echo "=== Forms ===" && grep -rn "onSubmit" --include="*.tsx" | grep -v node_modules
# Full audit (ripgrep version - much faster)
echo "=== Direct fetch ===" && rg "fetch\(" -t ts -t tsx --glob '!*.d.ts'
echo "=== Direct axios ===" && rg "axios\." -t ts -t tsx --glob '!*orval*'
echo "=== Hardcoded URLs ===" && rg "https?://" -t ts -t tsx
echo "=== useState arrays ===" && rg "useState\(\[" -t tsx
echo "=== Forms ===" && rg "onSubmit" -t tsx
Dependencies
If commands fail, install:
# ripgrep (highly recommended - 10x faster than grep)
brew install ripgrep # or apt-get install ripgrep, cargo install ripgrep
# jq for JSON parsing
brew install jq # or apt-get install jq
# For Orval dry-run
npm install -g orval # or use npx
Installation
To use this skill with Claude Code, add it to your project's skills/ directory:
my-expo-app/
├── app/ # Expo Router screens
├── stores/
├── api/
├── skills/
│ └── expo-api-audit/
│ └── SKILL.md
├── app.config.js # Expo config
├── package.json
└── ...
Claude Code auto-discovers skills in this directory. Once installed, trigger the audit with prompts like:
- "Run an API integration audit"
- "Check for hardcoded data in my screens"
- "Audit my auth token handling"
- "Find forms that aren't syncing to the API"
- "Check if I'm using expo-secure-store correctly"