[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HESDJ8A1KDPREER0JE7TYC":3,"skill-same:01M6HESDJ8A1KDPREER0JE7TYC":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":6,"ownerHtmlUrl":60,"ownerBlogUrl":6,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6HESDJ8A1KDPREER0JE7TYC","native-data-fetching",null,"Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (useLoaderData).","HenriqueMC17\u002Fagente-core","HenriqueMC17","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F206460478?v=4","01M6HB63HBBF18AC4H09XTB549","Henrique Monteiro Cardoso","01M6HB63NGSQXWKWCFWW2B20W0",1,"MIT","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Fdevelopment\u002Fnative-data-fetching\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"data fetching","native data fetching","data","native","community","unknown","implementing","expo router","error","caching","query","call","react","router","use","fetch","api","error handling","development","debugging","2026-11-10 10:12:30",false,"---\nname: native-data-fetching\ndescription: Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (useLoaderData).\nrisk: unknown\nsource: community\nversion: 1.0.0\nlicense: MIT\ncategory: development\n---\n# Expo Networking\n\n**You MUST use this skill for ANY networking work including API requests, data fetching, caching, or network debugging.**\n\n## References\n\nConsult these resources as needed:\n\n```\nreferences\u002F\n  expo-router-loaders.md   Route-level data loading with Expo Router loaders (web, SDK 55+)\n```\n\n## When to Use\nUse this skill when:\n\n- Implementing API requests\n- Setting up data fetching (React Query, SWR)\n- Using Expo Router data loaders (`useLoaderData`, web SDK 55+)\n- Debugging network failures\n- Implementing caching strategies\n- Handling offline scenarios\n- Authentication\u002Ftoken management\n- Configuring API URLs and environment variables\n\n## Preferences\n\n- Avoid axios, prefer expo\u002Ffetch\n\n## Common Issues & Solutions\n\n### 1. Basic Fetch Usage\n\n**Simple GET request**:\n\n```tsx\nconst fetchUser = async (userId: string) => {\n  const response = await fetch(`https:\u002F\u002Fapi.example.com\u002Fusers\u002F${userId}`);\n\n  if (!response.ok) {\n    throw new Error(`HTTP error! status: ${response.status}`);\n  }\n\n  return response.json();\n};\n```\n\n**POST request with body**:\n\n```tsx\nconst createUser = async (userData: UserData) => {\n  const response = await fetch(\"https:\u002F\u002Fapi.example.com\u002Fusers\", {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application\u002Fjson\",\n      Authorization: `Bearer ${token}`,\n    },\n    body: JSON.stringify(userData),\n  });\n\n  if (!response.ok) {\n    const error = await response.json();\n    throw new Error(error.message);\n  }\n\n  return response.json();\n};\n```\n\n---\n\n### 2. React Query (TanStack Query)\n\n**Setup**:\n\n```tsx\n\u002F\u002F app\u002F_layout.tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack\u002Freact-query\";\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n      retry: 2,\n    },\n  },\n});\n\nexport default function RootLayout() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CStack \u002F>\n    \u003C\u002FQueryClientProvider>\n  );\n}\n```\n\n**Fetching data**:\n\n```tsx\nimport { useQuery } from \"@tanstack\u002Freact-query\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, isLoading, error, refetch } = useQuery({\n    queryKey: [\"user\", userId],\n    queryFn: () => fetchUser(userId),\n  });\n\n  if (isLoading) return \u003CLoading \u002F>;\n  if (error) return \u003CError message={error.message} \u002F>;\n\n  return \u003CProfile user={data} \u002F>;\n}\n```\n\n**Mutations**:\n\n```tsx\nimport { useMutation, useQueryClient } from \"@tanstack\u002Freact-query\";\n\nfunction CreateUserForm() {\n  const queryClient = useQueryClient();\n\n  const mutation = useMutation({\n    mutationFn: createUser,\n    onSuccess: () => {\n      \u002F\u002F Invalidate and refetch\n      queryClient.invalidateQueries({ queryKey: [\"users\"] });\n    },\n  });\n\n  const handleSubmit = (data: UserData) => {\n    mutation.mutate(data);\n  };\n\n  return \u003CForm onSubmit={handleSubmit} isLoading={mutation.isPending} \u002F>;\n}\n```\n\n---\n\n### 3. Error Handling\n\n**Comprehensive error handling**:\n\n```tsx\nclass ApiError extends Error {\n  constructor(message: string, public status: number, public code?: string) {\n    super(message);\n    this.name = \"ApiError\";\n  }\n}\n\nconst fetchWithErrorHandling = async (url: string, options?: RequestInit) => {\n  try {\n    const response = await fetch(url, options);\n\n    if (!response.ok) {\n      const error = await response.json().catch(() => ({}));\n      throw new ApiError(\n        error.message || \"Request failed\",\n        response.status,\n        error.code\n      );\n    }\n\n    return response.json();\n  } catch (error) {\n    if (error instanceof ApiError) {\n      throw error;\n    }\n    \u002F\u002F Network error (no internet, timeout, etc.)\n    throw new ApiError(\"Network error\", 0, \"NETWORK_ERROR\");\n  }\n};\n```\n\n**Retry logic**:\n\n```tsx\nconst fetchWithRetry = async (\n  url: string,\n  options?: RequestInit,\n  retries = 3\n) => {\n  for (let i = 0; i \u003C retries; i++) {\n    try {\n      return await fetchWithErrorHandling(url, options);\n    } catch (error) {\n      if (i === retries - 1) throw error;\n      \u002F\u002F Exponential backoff\n      await new Promise((r) => setTimeout(r, Math.pow(2, i) * 1000));\n    }\n  }\n};\n```\n\n---\n\n### 4. Authentication\n\n**Token management**:\n\n```tsx\nimport * as SecureStore from \"expo-secure-store\";\n\nconst TOKEN_KEY = \"auth_token\";\n\nexport const auth = {\n  getToken: () => SecureStore.getItemAsync(TOKEN_KEY),\n  setToken: (token: string) => SecureStore.setItemAsync(TOKEN_KEY, token),\n  removeToken: () => SecureStore.deleteItemAsync(TOKEN_KEY),\n};\n\n\u002F\u002F Authenticated fetch wrapper\nconst authFetch = async (url: string, options: RequestInit = {}) => {\n  const token = await auth.getToken();\n\n  return fetch(url, {\n    ...options,\n    headers: {\n      ...options.headers,\n      Authorization: token ? `Bearer ${token}` : \"\",\n    },\n  });\n};\n```\n\n**Token refresh**:\n\n```tsx\nlet isRefreshing = false;\nlet refreshPromise: Promise\u003Cstring> | null = null;\n\nconst getValidToken = async (): Promise\u003Cstring> => {\n  const token = await auth.getToken();\n\n  if (!token || isTokenExpired(token)) {\n    if (!isRefreshing) {\n      isRefreshing = true;\n      refreshPromise = refreshToken().finally(() => {\n        isRefreshing = false;\n        refreshPromise = null;\n      });\n    }\n    return refreshPromise!;\n  }\n\n  return token;\n};\n```\n\n---\n\n### 5. Offline Support\n\n**Check network status**:\n\n```tsx\nimport NetInfo from \"@react-native-community\u002Fnetinfo\";\n\n\u002F\u002F Hook for network status\nfunction useNetworkStatus() {\n  const [isOnline, setIsOnline] = useState(true);\n\n  useEffect(() => {\n    return NetInfo.addEventListener((state) => {\n      setIsOnline(state.isConnected ?? true);\n    });\n  }, []);\n\n  return isOnline;\n}\n```\n\n**Offline-first with React Query**:\n\n```tsx\nimport { onlineManager } from \"@tanstack\u002Freact-query\";\nimport NetInfo from \"@react-native-community\u002Fnetinfo\";\n\n\u002F\u002F Sync React Query with network status\nonlineManager.setEventListener((setOnline) => {\n  return NetInfo.addEventListener((state) => {\n    setOnline(state.isConnected ?? true);\n  });\n});\n\n\u002F\u002F Queries will pause when offline and resume when online\n```\n\n---\n\n### 6. Environment Variables\n\n**Using environment variables for API configuration**:\n\nExpo supports environment variables with the `EXPO_PUBLIC_` prefix. These are inlined at build time and available in your JavaScript code.\n\n```tsx\n\u002F\u002F .env\nEXPO_PUBLIC_API_URL=https:\u002F\u002Fapi.example.com\nEXPO_PUBLIC_API_VERSION=v1\n\n\u002F\u002F Usage in code\nconst API_URL = process.env.EXPO_PUBLIC_API_URL;\n\nconst fetchUsers = async () => {\n  const response = await fetch(`${API_URL}\u002Fusers`);\n  return response.json();\n};\n```\n\n**Environment-specific configuration**:\n\n```tsx\n\u002F\u002F .env.development\nEXPO_PUBLIC_API_URL=http:\u002F\u002Flocalhost:3000\n\n\u002F\u002F .env.production\nEXPO_PUBLIC_API_URL=https:\u002F\u002Fapi.production.com\n```\n\n**Creating an API client with environment config**:\n\n```tsx\n\u002F\u002F api\u002Fclient.ts\nconst BASE_URL = process.env.EXPO_PUBLIC_API_URL;\n\nif (!BASE_URL) {\n  throw new Error(\"EXPO_PUBLIC_API_URL is not defined\");\n}\n\nexport const apiClient = {\n  get: async \u003CT,>(path: string): Promise\u003CT> => {\n    const response = await fetch(`${BASE_URL}${path}`);\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  },\n\n  post: async \u003CT,>(path: string, body: unknown): Promise\u003CT> => {\n    const response = await fetch(`${BASE_URL}${path}`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application\u002Fjson\" },\n      body: JSON.stringify(body),\n    });\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  },\n};\n```\n\n**Important notes**:\n\n- Only variables prefixed with `EXPO_PUBLIC_` are exposed to the client bundle\n- Never put secrets (API keys with write access, database passwords) in `EXPO_PUBLIC_` variables—they're visible in the built app\n- Environment variables are inlined at **build time**, not runtime\n- Restart the dev server after changing `.env` files\n- For server-side secrets in API routes, use variables without the `EXPO_PUBLIC_` prefix\n\n**TypeScript support**:\n\n```tsx\n\u002F\u002F types\u002Fenv.d.ts\ndeclare global {\n  namespace NodeJS {\n    interface ProcessEnv {\n      EXPO_PUBLIC_API_URL: string;\n      EXPO_PUBLIC_API_VERSION?: string;\n    }\n  }\n}\n\nexport {};\n```\n\n---\n\n### 7. Request Cancellation\n\n**Cancel on unmount**:\n\n```tsx\nuseEffect(() => {\n  const controller = new AbortController();\n\n  fetch(url, { signal: controller.signal })\n    .then((response) => response.json())\n    .then(setData)\n    .catch((error) => {\n      if (error.name !== \"AbortError\") {\n        setError(error);\n      }\n    });\n\n  return () => controller.abort();\n}, [url]);\n```\n\n**With React Query** (automatic):\n\n```tsx\n\u002F\u002F React Query automatically cancels requests when queries are invalidated\n\u002F\u002F or components unmount\n```\n\n---\n\n## Decision Tree\n\n```\nUser asks about networking\n  |-- Route-level data loading (web, SDK 55+)?\n  |   \\-- Expo Router loaders — see references\u002Fexpo-router-loaders.md\n  |\n  |-- Basic fetch?\n  |   \\-- Use fetch API with error handling\n  |\n  |-- Need caching\u002Fstate management?\n  |   |-- Complex app -> React Query (TanStack Query)\n  |   \\-- Simpler needs -> SWR or custom hooks\n  |\n  |-- Authentication?\n  |   |-- Token storage -> expo-secure-store\n  |   \\-- Token refresh -> Implement refresh flow\n  |\n  |-- Error handling?\n  |   |-- Network errors -> Check connectivity first\n  |   |-- HTTP errors -> Parse response, throw typed errors\n  |   \\-- Retries -> Exponential backoff\n  |\n  |-- Offline support?\n  |   |-- Check status -> NetInfo\n  |   \\-- Queue requests -> React Query persistence\n  |\n  |-- Environment\u002FAPI config?\n  |   |-- Client-side URLs -> EXPO_PUBLIC_ prefix in .env\n  |   |-- Server secrets -> Non-prefixed env vars (API routes only)\n  |   \\-- Multiple environments -> .env.development, .env.production\n  |\n  \\-- Performance?\n      |-- Caching -> React Query with staleTime\n      |-- Deduplication -> React Query handles this\n      \\-- Cancellation -> AbortController or React Query\n```\n\n## Common Mistakes\n\n**Wrong: No error handling**\n\n```tsx\nconst data = await fetch(url).then((r) => r.json());\n```\n\n**Right: Check response status**\n\n```tsx\nconst response = await fetch(url);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst data = await response.json();\n```\n\n**Wrong: Storing tokens in AsyncStorage**\n\n```tsx\nawait AsyncStorage.setItem(\"token\", token); \u002F\u002F Not secure!\n```\n\n**Right: Use SecureStore for sensitive data**\n\n```tsx\nawait SecureStore.setItemAsync(\"token\", token);\n```\n\n## Example Invocations\n\nUser: \"How do I make API calls in React Native?\"\n-> Use fetch, wrap with error handling\n\nUser: \"Should I use React Query or SWR?\"\n-> React Query for complex apps, SWR for simpler needs\n\nUser: \"My app needs to work offline\"\n-> Use NetInfo for status, React Query persistence for caching\n\nUser: \"How do I handle authentication tokens?\"\n-> Store in expo-secure-store, implement refresh flow\n\nUser: \"API calls are slow\"\n-> Check caching strategy, use React Query staleTime\n\nUser: \"How do I configure different API URLs for dev and prod?\"\n-> Use EXPO*PUBLIC* env vars with .env.development and .env.production files\n\nUser: \"Where should I put my API key?\"\n-> Client-safe keys: EXPO*PUBLIC* in .env. Secret keys: non-prefixed env vars in API routes only\n\nUser: \"How do I load data for a page in Expo Router?\"\n-> See references\u002Fexpo-router-loaders.md for route-level loaders (web, SDK 55+). For native, use React Query or fetch.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","# Expo Networking\n\n**You MUST use this skill for ANY networking work including API requests, data fetching, caching, or network debugging.**\n\n## References\n\nConsult these resources as needed:\n\n```\nreferences\u002F\n  expo-router-loaders.md   Route-level data loading with Expo Router loaders (web, SDK 55+)\n```\n\n## When to Use\nUse this skill when:\n\n- Implementing API requests\n- Setting up data fetching (React Query, SWR)\n- Using Expo Router data loaders (`useLoaderData`, web SDK 55+)\n- Debugging network failures\n- Implementing caching strategies\n- Handling offline scenarios\n- Authentication\u002Ftoken management\n- Configuring API URLs and environment variables\n\n## Preferences\n\n- Avoid axios, prefer expo\u002Ffetch\n\n## Common Issues & Solutions\n\n### 1. Basic Fetch Usage\n\n**Simple GET request**:\n\n```tsx\nconst fetchUser = async (userId: string) => {\n  const response = await fetch(`https:\u002F\u002Fapi.example.com\u002Fusers\u002F${userId}`);\n\n  if (!response.ok) {\n    throw new Error(`HTTP error! status: ${response.status}`);\n  }\n\n  return response.json();\n};\n```\n\n**POST request with body**:\n\n```tsx\nconst createUser = async (userData: UserData) => {\n  const response = await fetch(\"https:\u002F\u002Fapi.example.com\u002Fusers\", {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application\u002Fjson\",\n      Authorization: `Bearer ${token}`,\n    },\n    body: JSON.stringify(userData),\n  });\n\n  if (!response.ok) {\n    const error = await response.json();\n    throw new Error(error.message);\n  }\n\n  return response.json();\n};\n```\n\n---\n\n### 2. React Query (TanStack Query)\n\n**Setup**:\n\n```tsx\n\u002F\u002F app\u002F_layout.tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack\u002Freact-query\";\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n      retry: 2,\n    },\n  },\n});\n\nexport default function RootLayout() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CStack \u002F>\n    \u003C\u002FQueryClientProvider>\n  );\n}\n```\n\n**Fetching data**:\n\n```tsx\nimport { useQuery } from \"@tanstack\u002Freact-query\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, isLoading, error, refetch } = useQuery({\n    queryKey: [\"user\", userId],\n    queryFn: () => fetchUser(userId),\n  });\n\n  if (isLoading) return \u003CLoading \u002F>;\n  if (error) return \u003CError message={error.message} \u002F>;\n\n  return \u003CProfile user={data} \u002F>;\n}\n```\n\n**Mutations**:\n\n```tsx\nimport { useMutation, useQueryClient } from \"@tanstack\u002Freact-query\";\n\nfunction CreateUserForm() {\n  const queryClient = useQueryClient();\n\n  const mutation = useMutation({\n    mutationFn: createUser,\n    onSuccess: () => {\n      \u002F\u002F Invalidate and refetch\n      queryClient.invalidateQueries({ queryKey: [\"users\"] });\n    },\n  });\n\n  const handleSubmit = (data: UserData) => {\n    mutation.mutate(data);\n  };\n\n  return \u003CForm onSubmit={handleSubmit} isLoading={mutation.isPending} \u002F>;\n}\n```\n\n---\n\n### 3. Error Handling\n\n**Comprehensive error handling**:\n\n```tsx\nclass ApiError extends Error {\n  constructor(message: string, public status: number, public code?: string) {\n    super(message);\n    this.name = \"ApiError\";\n  }\n}\n\nconst fetchWithErrorHandling = async (url: string, options?: RequestInit) => {\n  try {\n    const response = await fetch(url, options);\n\n    if (!response.ok) {\n      const error = await response.json().catch(() => ({}));\n      throw new ApiError(\n        error.message || \"Request failed\",\n        response.status,\n        error.code\n      );\n    }\n\n    return response.json();\n  } catch (error) {\n    if (error instanceof ApiError) {\n      throw error;\n    }\n    \u002F\u002F Network error (no internet, timeout, etc.)\n    throw new ApiError(\"Network error\", 0, \"NETWORK_ERROR\");\n  }\n};\n```\n\n**Retry logic**:\n\n```tsx\nconst fetchWithRetry = async (\n  url: string,\n  options?: RequestInit,\n  retries = 3\n) => {\n  for (let i = 0; i \u003C retries; i++) {\n    try {\n      return await fetchWithErrorHandling(url, options);\n    } catch (error) {\n      if (i === retries - 1) throw error;\n      \u002F\u002F Exponential backoff\n      await new Promise((r) => setTimeout(r, Math.pow(2, i) * 1000));\n    }\n  }\n};\n```\n\n---\n\n### 4. Authentication\n\n**Token management**:\n\n```tsx\nimport * as SecureStore from \"expo-secure-store\";\n\nconst TOKEN_KEY = \"auth_token\";\n\nexport const auth = {\n  getToken: () => SecureStore.getItemAsync(TOKEN_KEY),\n  setToken: (token: string) => SecureStore.setItemAsync(TOKEN_KEY, token),\n  removeToken: () => SecureStore.deleteItemAsync(TOKEN_KEY),\n};\n\n\u002F\u002F Authenticated fetch wrapper\nconst authFetch = async (url: string, options: RequestInit = {}) => {\n  const token = await auth.getToken();\n\n  return fetch(url, {\n    ...options,\n    headers: {\n      ...options.headers,\n      Authorization: token ? `Bearer ${token}` : \"\",\n    },\n  });\n};\n```\n\n**Token refresh**:\n\n```tsx\nlet isRefreshing = false;\nlet refreshPromise: Promise\u003Cstring> | null = null;\n\nconst getValidToken = async (): Promise\u003Cstring> => {\n  const token = await auth.getToken();\n\n  if (!token || isTokenExpired(token)) {\n    if (!isRefreshing) {\n      isRefreshing = true;\n      refreshPromise = refreshToken().finally(() => {\n        isRefreshing = false;\n        refreshPromise = null;\n      });\n    }\n    return refreshPromise!;\n  }\n\n  return token;\n};\n```\n\n---\n\n### 5. Offline Support\n\n**Check network status**:\n\n```tsx\nimport NetInfo from \"@react-native-community\u002Fnetinfo\";\n\n\u002F\u002F Hook for network status\nfunction useNetworkStatus() {\n  const [isOnline, setIsOnline] = useState(true);\n\n  useEffect(() => {\n    return NetInfo.addEventListener((state) => {\n      setIsOnline(state.isConnected ?? true);\n    });\n  }, []);\n\n  return isOnline;\n}\n```\n\n**Offline-first with React Query**:\n\n```tsx\nimport { onlineManager } from \"@tanstack\u002Freact-query\";\nimport NetInfo from \"@react-native-community\u002Fnetinfo\";\n\n\u002F\u002F Sync React Query with network status\nonlineManager.setEventListener((setOnline) => {\n  return NetInfo.addEventListener((state) => {\n    setOnline(state.isConnected ?? true);\n  });\n});\n\n\u002F\u002F Queries will pause when offline and resume when online\n```\n\n---\n\n### 6. Environment Variables\n\n**Using environment variables for API configuration**:\n\nExpo supports environment variables with the `EXPO_PUBLIC_` prefix. These are inlined at build time and available in your JavaScript code.\n\n```tsx\n\u002F\u002F .env\nEXPO_PUBLIC_API_URL=https:\u002F\u002Fapi.example.com\nEXPO_PUBLIC_API_VERSION=v1\n\n\u002F\u002F Usage in code\nconst API_URL = process.env.EXPO_PUBLIC_API_URL;\n\nconst fetchUsers = async () => {\n  const response = await fetch(`${API_URL}\u002Fusers`);\n  return response.json();\n};\n```\n\n**Environment-specific configuration**:\n\n```tsx\n\u002F\u002F .env.development\nEXPO_PUBLIC_API_URL=http:\u002F\u002Flocalhost:3000\n\n\u002F\u002F .env.production\nEXPO_PUBLIC_API_URL=https:\u002F\u002Fapi.production.com\n```\n\n**Creating an API client with environment config**:\n\n```tsx\n\u002F\u002F api\u002Fclient.ts\nconst BASE_URL = process.env.EXPO_PUBLIC_API_URL;\n\nif (!BASE_URL) {\n  throw new Error(\"EXPO_PUBLIC_API_URL is not defined\");\n}\n\nexport const apiClient = {\n  get: async \u003CT,>(path: string): Promise\u003CT> => {\n    const response = await fetch(`${BASE_URL}${path}`);\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  },\n\n  post: async \u003CT,>(path: string, body: unknown): Promise\u003CT> => {\n    const response = await fetch(`${BASE_URL}${path}`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application\u002Fjson\" },\n      body: JSON.stringify(body),\n    });\n    if (!response.ok) throw new Error(`HTTP ${response.status}`);\n    return response.json();\n  },\n};\n```\n\n**Important notes**:\n\n- Only variables prefixed with `EXPO_PUBLIC_` are exposed to the client bundle\n- Never put secrets (API keys with write access, database passwords) in `EXPO_PUBLIC_` variables—they're visible in the built app\n- Environment variables are inlined at **build time**, not runtime\n- Restart the dev server after changing `.env` files\n- For server-side secrets in API routes, use variables without the `EXPO_PUBLIC_` prefix\n\n**TypeScript support**:\n\n```tsx\n\u002F\u002F types\u002Fenv.d.ts\ndeclare global {\n  namespace NodeJS {\n    interface ProcessEnv {\n      EXPO_PUBLIC_API_URL: string;\n      EXPO_PUBLIC_API_VERSION?: string;\n    }\n  }\n}\n\nexport {};\n```\n\n---\n\n### 7. Request Cancellation\n\n**Cancel on unmount**:\n\n```tsx\nuseEffect(() => {\n  const controller = new AbortController();\n\n  fetch(url, { signal: controller.signal })\n    .then((response) => response.json())\n    .then(setData)\n    .catch((error) => {\n      if (error.name !== \"AbortError\") {\n        setError(error);\n      }\n    });\n\n  return () => controller.abort();\n}, [url]);\n```\n\n**With React Query** (automatic):\n\n```tsx\n\u002F\u002F React Query automatically cancels requests when queries are invalidated\n\u002F\u002F or components unmount\n```\n\n---\n\n## Decision Tree\n\n```\nUser asks about networking\n  |-- Route-level data loading (web, SDK 55+)?\n  |   \\-- Expo Router loaders — see references\u002Fexpo-router-loaders.md\n  |\n  |-- Basic fetch?\n  |   \\-- Use fetch API with error handling\n  |\n  |-- Need caching\u002Fstate management?\n  |   |-- Complex app -> React Query (TanStack Query)\n  |   \\-- Simpler needs -> SWR or custom hooks\n  |\n  |-- Authentication?\n  |   |-- Token storage -> expo-secure-store\n  |   \\-- Token refresh -> Implement refresh flow\n  |\n  |-- Error handling?\n  |   |-- Network errors -> Check connectivity first\n  |   |-- HTTP errors -> Parse response, throw typed errors\n  |   \\-- Retries -> Exponential backoff\n  |\n  |-- Offline support?\n  |   |-- Check status -> NetInfo\n  |   \\-- Queue requests -> React Query persistence\n  |\n  |-- Environment\u002FAPI config?\n  |   |-- Client-side URLs -> EXPO_PUBLIC_ prefix in .env\n  |   |-- Server secrets -> Non-prefixed env vars (API routes only)\n  |   \\-- Multiple environments -> .env.development, .env.production\n  |\n  \\-- Performance?\n      |-- Caching -> React Query with staleTime\n      |-- Deduplication -> React Query handles this\n      \\-- Cancellation -> AbortController or React Query\n```\n\n## Common Mistakes\n\n**Wrong: No error handling**\n\n```tsx\nconst data = await fetch(url).then((r) => r.json());\n```\n\n**Right: Check response status**\n\n```tsx\nconst response = await fetch(url);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst data = await response.json();\n```\n\n**Wrong: Storing tokens in AsyncStorage**\n\n```tsx\nawait AsyncStorage.setItem(\"token\", token); \u002F\u002F Not secure!\n```\n\n**Right: Use SecureStore for sensitive data**\n\n```tsx\nawait SecureStore.setItemAsync(\"token\", token);\n```\n\n## Example Invocations\n\nUser: \"How do I make API calls in React Native?\"\n-> Use fetch, wrap with error handling\n\nUser: \"Should I use React Query or SWR?\"\n-> React Query for complex apps, SWR for simpler needs\n\nUser: \"My app needs to work offline\"\n-> Use NetInfo for status, React Query persistence for caching\n\nUser: \"How do I handle authentication tokens?\"\n-> Store in expo-secure-store, implement refresh flow\n\nUser: \"API calls are slow\"\n-> Check caching strategy, use React Query staleTime\n\nUser: \"How do I configure different API URLs for dev and prod?\"\n-> Use EXPO*PUBLIC* env vars with .env.development and .env.production files\n\nUser: \"Where should I put my API key?\"\n-> Client-safe keys: EXPO*PUBLIC* in .env. Secret keys: non-prefixed env vars in API routes only\n\nUser: \"How do I load data for a page in Expo Router?\"\n-> See references\u002Fexpo-router-loaders.md for route-level loaders (web, SDK 55+). For native, use React Query or fetch.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n",{"name":5,"risk":34,"source":33,"license":15,"version":54,"category":47,"description":7},"1.0.0","main","5eea5b3296f4673b5f481e8bcc1ba76281149f16","modules\u002Fdevelopment\u002Fnative-data-fetching",0,"https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core","https:\u002F\u002Fgithub.com\u002FHenriqueMC17","Qualquer tecnologia suficientemente avançada é indistinguível da mágica.","2026-09-28 22:40:38",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add HenriqueMC17\u002Fagente-core --skill native-data-fetching",{"tool":69,"command":70},"bunx","bunx skills add HenriqueMC17\u002Fagente-core --skill native-data-fetching",{"tool":72,"command":73},"pnpm","pnpm dlx skills add HenriqueMC17\u002Fagente-core --skill native-data-fetching","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Fdevelopment\u002Fnative-data-fetching\u002FSKILL.md",true,[],[78,109,135,158,174,195],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":88,"publishedAt":49},"01M6HPFF0RCN0X6WT5EBSV71GY","validating-backup-integrity-for-recovery","Validate backup integrity through cryptographic hash verification, automated restore testing, corruption detection, and recoverability checks to ensure backups are reliable for disaster recovery and ransomware response scenarios.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Fvalidating-backup-integrity-for-recovery\u002FSKILL.md",[85],{"categoryNo":86,"categoryName":87,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108],"integrity","backup","recovery","validating backup integrity for recovery","for","testing","cybersecurity","validate","verification","hash-verification","incident-response","ransomware","detection","t1078","t1490","disaster-recovery","restore","incident","disaster recovery","ransomware response",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":115,"publishedAt":49},"01M6HPFAWJVFK25Q33K3T7WQ8G","triaging-vulnerabilities-with-ssvc-framework","Triage and prioritize vulnerabilities using CISA's Stakeholder-Specific Vulnerability Categorization (SSVC) decision tree framework to produce actionable remediation priorities.","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Ftriaging-vulnerabilities-with-ssvc-framework\u002FSKILL.md",[],[116,117,118,119,120,95,121,122,123,124,125,126,127,128,129,130,131,132,133,134],"vulnerabilities","framework","ssvc","triaging vulnerabilities with ssvc framework","with","triage and prioritize","CISA","vulnerability-management","risk-management","cvss","remediation","vulnerability","prioritization","decision-tree","prioritize","decision","priorities","actionable","tree",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":49},"01M6HPF6JS9X72WP59094H3ASQ","triaging-security-incident","Performs initial triage of security incidents to determine severity, scope, and required response actions using the NIST SP 800-61r3 and SANS PICERL frameworks. Classifies incidents by type, assigns priority based on business impact, and routes...","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Ftriaging-security-incident\u002FSKILL.md",[],[142,143,106,95,144,145,146,102,103,147,148,149,150,151,152,153,154,155,156,157],"security","triaging security incident","actions","soc","NIST","analysis","severity-classification","incident-triage","SANS-PICERL","SOC-operations","picerl","conversion","isolation","scope","impact","consistency",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":49},"01M6HPF2EX6ZYNYYHKBRJN7GEF","triaging-security-incident-with-ir-playbook","Classify and prioritize security incidents using structured IR playbooks to determine severity, assign response teams, and initiate appropriate response procedures.","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Ftriaging-security-incident-with-ir-playbook\u002FSKILL.md",[],[142,165,166,143,106,120,95,167,145,99,168,169,102,103,170,148,130,171,172,173],"playbook","triaging security incident with ir playbook","teams","IR-playbook","severity","classification","classify","incidents","initiate",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":180,"publishedAt":49},"01M6HPEY9XPGE7X95AG4XJFQAW","triaging-security-alerts-in-splunk","Triages security alerts in Splunk Enterprise Security by classifying severity, investigating notable events, correlating related telemetry, and making escalation or closure decisions using SPL queries and the Incident Review dashboard. Use when...","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Ftriaging-security-alerts-in-splunk\u002FSKILL.md",[],[142,181,182,183,184,185,95,186,187,188,145,189,102,190,191,192,151,193,106,194],"security alerts","splunk","triaging security alerts in splunk","alerts","review","siem","dashboard","search","correlation-search","t1566","notable-events","incident-review","cm","de",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":6,"sourceHtmlUrl":199,"categories":200,"keywords":201,"publishedAt":49},"01M6HPET6XGEPEJPN7V032JGZ0","tracking-threat-actor-infrastructure","Threat actor infrastructure tracking involves monitoring and mapping adversary-controlled assets including command-and-control (C2) servers, phishing domains, exploit kit hosts, bulletproof hosting, a","https:\u002F\u002Fgithub.com\u002FHenriqueMC17\u002Fagente-core\u002Fblob\u002Fmain\u002Fmodules\u002Ftracking-threat-actor-infrastructure\u002FSKILL.md",[],[202,203,204,205,95,206,207,208,209,210,211,190,212,213,193,214,194,215,216,217],"infrastructure","tracking threat actor infrastructure","tracking","threat","monitoring","mitre-attack","threat-actor","threat-intelligence","command-and-control","dns","command","exploit","intelligence","domains","kit","hosts"]