@watchapi/analyzer
v1.0.11
Published
Analyzing tRPC API endpoint enforce best practices and consistency.
Downloads
1,225
Maintainers
Readme
API Analyzer
Analyzing tRPC API endpoint enforce best practices and consistency.
Quickstart
Run against a project root (defaults to current working directory):
npx @watchapi/analyzer -t trpc --root . --format tableOptions:
--include <globs...>: override the defaultsrc/server/api/**/*.{ts,tsx}search.--tsconfig <path>: custom tsconfig (relative to--root).--format table|json: choose human-readable table output or CI-friendly JSON.
✅ 1. Architecture Overview
Your analyzer can run as a CLI tool (like eslint, tsc, drizzle-kit). It scans the project, finds routers/procedures, builds a tree, applies rules.
Flow
File discovery
Scan /src/server/api/** or custom path.
Collect all *.ts files containing createTRPCRouter, publicProcedure, privateProcedure, protectedProcedure.
AST parsing
Use TypeScript AST (ts-morph or @typescript-eslint/parser).
Extract:
router names
procedure names
input schema
output types
middleware
errors
DB queries inside (Drizzle/Prisma)
Build a unified tree
interface TRPCNode { router: string; procedure: string; input: string | null; output: string | null; method: "query" | "mutation"; file: string; line: number; }
Run rule engine
Each rule is a small function receiving a node.
Returns a warning, error, or suggestion.
Output report
JSON + pretty console table
“Severity: info / warn / error”
✅ 2. What You Can Detect (High-Value Rules)
Here are rules developers would LOVE:
🔥 1. Missing input schemas
Bad:
publicProcedure.query(() => db.user.findMany());
Good:
publicProcedure.input(z.object({ limit: z.number() }))
Rule: If mutation/query has no .input() check → warn.
🔥 2. Inconsistent naming
Examples:
query named createUser
mutation named getAll
router names not pluralized (e.g. user vs users)
Rule: Enforce consistent:
queries = get, list, fetch
mutations = create, update, delete
🔥 3. Output type mismatch
Detect if:
Output type is implicit
Output type is any
Output doesn’t match Zod schema
🔥 4. Unhandled errors
Bad:
await db.user.create(...);
(inside mutation without try/catch or TRPCError)
Good:
if (!user) throw new TRPCError({ code: "NOT_FOUND" });
Rule: Warn when DB calls are unguarded.
🔥 5. Heavy logic inside mutations
Detect:
More than X LOC inside a resolver
Nested business logic
No service-layer extraction
🔥 6. No rate limiting on sensitive procedures
Example:
login
password reset
email verification
If using publicProcedure → error.
🔥 7. Router size
If router > 500 LOC → warn to split.
🔥 8. Unused routers / dead procedures
Scan imports in Next.js server components:
If router is never referenced → warn.
🔥 9. Side-effects inside queries
Queries must be pure — but some developers write:
query: () => { sendEmail(...) // ❌ return ... }
Static analysis can detect send / write / update.
🔥 10. Missing caching strategies (Next.js)
If expensive queries are not wrapped in:
revalidateTag()
unstable_cache
or external caching layer
Warn.
✅ 3. How Results Could Look (CLI report) Console example tRPC Analyzer — 18 findings
[WARN] users.createUser — missing input schema (src/server/api/users.ts:12) [ERROR] auth.login — public procedure without rate limiting (auth.ts:33) [INFO] posts.list — heavy logic (52 lines) consider extracting service (posts.ts:88) [WARN] orders.getAll — inconsistent naming (query name looks like mutation) [WARN] router “user” is singular; prefer plural
JSON output for CI { "summary": { "info": 4, "warn": 9, "error": 3 }, "issues": [ { "severity": "warn", "message": "Missing input schema", "router": "users", "procedure": "createUser", "file": "src/server/api/users.ts", "line": 12 } ] }
✅ 4. A Rule-Based Implementation (you can start today)
Use ts-morph to traverse:
import { Project } from "ts-morph";
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
const files = project.getSourceFiles("src/server/api/*/.ts");
files.forEach(file => { file.forEachDescendant(node => { // detect createTRPCRouter // detect publicProcedure, mutation, query // detect .input() }); });
Each rule is a simple function:
function checkMissingInput(node) { if (node.method === "mutation" && !node.input) { return warn("Missing input schema", node); } }
Make a rules folder:
/rules /naming.ts /missingInput.ts /outputType.ts /errorHandling.ts
✅ 5. Future: integrate into WatchAPI
Once it works, you can embed automatically:
Cloud-based Analyzer
User uploads repo or connects to GitHub → you run static analysis.
Benefits for WatchAPI:
Increase your footprint: not just monitoring, but API quality analysis
Add AI suggestions (“Fix this by refactoring your mutation into services…”)
One-click auto-fixes like ESLint (--fix)
This becomes a dev-tool suite, not just uptime monitoring.
