audit-fe-api — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited audit-fe-api (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
full audit of frontend API calls against backend implementation to ensure correctness, optimize performance, and improve reliability.
Before auditing, discover the project's API architecture from its source code.
Read package.json (or equivalent) and extract:
| Dependency | Technology |
|---|---|
@tanstack/react-query | TanStack Query (React Query) |
swr | SWR |
@reduxjs/toolkit with createApi | RTK Query |
axios | Axios HTTP client |
ky | Ky HTTP client |
ofetch or $fetch | Nuxt/ofetch |
@trpc/client | tRPC (type-safe RPC) |
graphql-request or @apollo/client | GraphQL |
openapi-fetch or openapi-typescript | OpenAPI typed client |
Look for backend indicators:
Glob: **/app/api/**/route.ts → Next.js App Router API routes
Glob: **/pages/api/**/*.ts → Next.js Pages Router API routes
Glob: **/src/routes/**/*.ts → Express/Hono/Fastify routes
Glob: **/server/api/**/*.ts → Nuxt server routes
Glob: **/src/app.py → Flask/Django
Glob: **/main.go → Go backendGlob: **/services/*api*.ts → API service files
Glob: **/services/*service*.ts → Service files
Glob: **/api/*.ts → API client files
Glob: **/hooks/use*.ts → Custom hooks (may contain API calls)
Glob: **/lib/api*.ts → API client config
Glob: **/features/*/api.* → Feature-specific API filesGrep: "dev" in package.json scripts → extract port (3000, 3001, 5173, 8080, etc.)
Grep: "PORT" in .env or .env.localAPI LAYER DISCOVERY:
- HTTP client: [axios/fetch/ky/ofetch]
- State management: [TanStack Query/SWR/RTK Query/none]
- API style: [REST/GraphQL/tRPC]
- Backend framework: [Next.js API routes/Express/Hono/etc.]
- API service files: [list paths]
- API hooks: [list paths]
- Dev server: http://localhost:[port]
- API docs: [path or URL if detected]Fetch docs for the detected data-fetching library:
CallMcpTool(server: "context7", toolName: "resolve-library-id", arguments: {
"libraryName": "<DETECTED_LIBRARY>",
"query": "caching deduplication error handling best practices"
})CallMcpTool(server: "context7", toolName: "query-docs", arguments: {
"libraryId": "<RESOLVED_ID>",
"query": "staleTime cacheTime retry error handling optimistic updates"
})Run for each major dependency (e.g., @tanstack/react-query, axios, zod).
CallMcpTool(server: "user-firecrawl", toolName: "firecrawl_search", arguments: {
"query": "<FRAMEWORK> API integration best practices [current year]",
"limit": 5,
"sources": [{ "type": "web" }]
})Additional searches:
| Topic | Query |
|---|---|
| Caching | <state library> caching strategy staleTime production |
| Error handling | <framework> API error handling patterns retry |
| Type safety | <framework> type-safe API client OpenAPI Zod |
| Performance | frontend API request optimization batching deduplication |
CallMcpTool(server: "plugin-sentry-sentry", toolName: "search_issues", arguments: {
"organizationSlug": "<ORG_SLUG>",
"naturalLanguageQuery": "API errors 4xx 5xx fetch axios network from the last 14 days",
"projectSlugOrId": "<PROJECT_SLUG>",
"regionUrl": "<REGION_URL>",
"limit": 25
})CallMcpTool(server: "plugin-sentry-sentry", toolName: "search_events", arguments: {
"organizationSlug": "<ORG_SLUG>",
"projectSlugOrId": "<PROJECT_SLUG>",
"regionUrl": "<REGION_URL>",
"query": "transaction.op:http.client",
"sort": "-duration",
"limit": 15
})For each Sentry error:
Grep to find the frontend code making that callUse Grep (not bash grep) to find API call patterns:
Grep: pattern "fetch\(|axios\.|api\.(get|post|patch|put|delete)" glob "*.{ts,tsx,js,jsx}"
Grep: pattern "useQuery|useMutation|useInfiniteQuery|useSuspenseQuery" glob "*.{ts,tsx}"
Grep: pattern "createApi|injectEndpoints" glob "*.{ts,tsx}"For each API call found, record:
| File | Function/Hook | Method | Endpoint | Params | Used By |
|---|---|---|---|---|---|
services/user.ts | getUsers | GET | /api/users | page, limit | UserList.tsx |
hooks/useUser.ts | useUser | GET | /api/users/:id | id | UserProfile.tsx |
For each frontend API call, verify the backend route exists:
Grep: pattern "<ENDPOINT_PATH>" glob "*.{ts,js,py,go,rb}" — in backend sourceIf using Next.js App Router, check that app/api/<path>/route.ts exists.
| Check | How |
|---|---|
| Required params sent | Compare FE call params with BE validation schema (Zod, Yup, Joi) |
| Naming convention match | FE camelCase vs BE snake_case — check if transform exists |
| Type match | FE sends string but BE expects number, or vice versa |
| Body shape match | Compare FE request body with BE expected schema |
If the backend reads from Supabase, verify the DB schema matches what the frontend expects:
CallMcpTool(server: "plugin-supabase-supabase", toolName: "execute_sql", arguments: {
"project_id": "<PROJECT_ID>",
"query": "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = '<TABLE>' ORDER BY ordinal_position"
})Compare column names and types against frontend TypeScript interfaces.
| Issue | Impact | Detection |
|---|---|---|
| Endpoint does not exist | 404 errors | Grep backend for route — not found |
| Missing required parameter | 500/422 errors | Compare FE params with BE schema |
| Wrong HTTP method | 405 errors | FE uses GET, BE expects POST |
| Auth header missing | 401 errors | Check API client interceptor |
| Type mismatch | Runtime errors | FE interface vs BE response shape |
| Issue | Impact | Detection |
|---|---|---|
| Parameter name mismatch | Silently ignored params | camelCase vs snake_case comparison |
| Deprecated endpoint | Future breakage | Version markers in BE routes |
| Missing error handling | Poor UX | useQuery without error state handling |
| No loading state | Poor UX | No isLoading / isPending check |
| Issue | Impact | Detection |
|---|---|---|
| Duplicate requests | Wasted bandwidth | Same queryKey in multiple components |
| No caching configured | Slow UX | staleTime not set (default 0) |
| No request batching | Too many requests | N+1 query pattern in lists |
| Missing prefetch | Slow navigation | No prefetchQuery on hover/focus |
| No optimistic updates | Slow mutations | Mutation waits for server response |
// Per-query staleTime based on data freshness needs
const { data } = useQuery({
queryKey: ['user', userId],
queryFn: () => getUser(userId),
staleTime: 1000 * 60 * 10, // User data: 10 minutes
});
const { data: settings } = useQuery({
queryKey: ['settings'],
queryFn: getSettings,
staleTime: Infinity, // Settings rarely change
});const mutation = useMutation({
mutationFn: updateUser,
onMutate: async (newData) => {
await queryClient.cancelQueries({ queryKey: ['user', userId] });
const previous = queryClient.getQueryData(['user', userId]);
queryClient.setQueryData(['user', userId], newData);
return { previous };
},
onError: (_err, _newData, context) => {
queryClient.setQueryData(['user', userId], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['user', userId] });
},
});const prefetchUser = (userId: string) => {
queryClient.prefetchQuery({
queryKey: ['user', userId],
queryFn: () => getUser(userId),
});
};
// On hover or focus
<Link onMouseEnter={() => prefetchUser(userId)} to={`/users/${userId}`}>
View User
</Link>const { data, error, isError, isLoading } = useQuery({
queryKey: ['users'],
queryFn: getUsers,
retry: 3,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
});
if (isLoading) return <Skeleton />;
if (isError) return <ErrorDisplay error={error} />;
if (!data?.length) return <EmptyState message="No users found" />;import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string(),
createdAt: z.string().datetime(),
});
type User = z.infer<typeof UserSchema>;
const getUser = async (id: string): Promise<User> => {
const response = await api.get(`/api/users/${id}`);
return UserSchema.parse(response.data);
};## Frontend API Audit Report
**Audited:** [date]
**Framework:** [detected framework]
**API client:** [detected client]
**State management:** [detected library]
---
### Production Error Summary (Sentry)
| Endpoint | Error | Frequency | Has Error Handling |
|----------|-------|-----------|-------------------|
| [endpoint] | [error type] | [events/week] | [YES/NO] |
---
### Critical Issues (Must Fix)
#### 1. [Endpoint/File] — [Issue Type]
- **Current:** `[current implementation]`
- **Problem:** [description]
- **Fix:** `[correct implementation]`
---
### Warnings (Should Fix)
#### 1. [Issue description]
- **File:** `[file path]`
- **Impact:** [what could go wrong]
- **Recommendation:** [how to fix]
---
### Optimization Opportunities
#### 1. Caching
- **Missing staleTime:** [list endpoints]
- **Recommendation:** [suggested values per data type]
#### 2. Prefetching
- **Candidates:** [navigation links that could prefetch]
#### 3. Batching
- **N+1 patterns found:** [list]
- **Backend batch endpoint exists:** [YES/NO]
---
### API Inventory
| Endpoint | Method | Frontend File | Backend Route | Status | Notes |
|----------|--------|---------------|---------------|--------|-------|
| `/api/users` | GET | `user-service.ts` | `app/api/users/route.ts` | VALID | — |
| `/api/reports` | GET | `report-hook.ts` | NOT FOUND | MISSING | Remove or implement |
---
### Type Safety Status
| Service | Typed Response | Zod Validation | Notes |
|---------|---------------|----------------|-------|
| `user-service.ts` | YES | NO | Add runtime validation |
| `auth-service.ts` | Partial | NO | Missing error response types |
---
### Research Findings Applied
- [Pattern]: [how it applies]
- [Best practice]: [gap identified]
---
### Next Steps
1. [ ] Fix critical: [list]
2. [ ] Add missing parameters: [list]
3. [ ] Configure caching: [list with suggested staleTime values]
4. [ ] Add error handling: [files]
5. [ ] Add Zod validation: [services]This skill works in conjunction with:
Workflow:
fe-api-audit to find potential issues in FE codefe-be-integration-debug to analyze backend logs for errors~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.