Loading...
Loading...
Review Vendure GraphQL resolvers for missing RequestContext, improper permissions, DTO violations, and schema extension issues. Use when reviewing GraphQL PRs or auditing API quality.
npx skill4agent add meriley/claude-code-skills vendure-graphql-reviewing# Find resolver files
find . -name "*.resolver.ts"
# Find schema files
find . -name "schema.ts" -o -name "*.graphql"# === CRITICAL VIOLATIONS ===
# Missing @Ctx() RequestContext
grep -rn "@Query\|@Mutation" --include="*.resolver.ts" -A 5 | grep -v "@Ctx()"
# Missing @Resolver decorator
grep -rn "export class.*Resolver" --include="*.resolver.ts" | grep -v "@Resolver"
# Missing @Allow permissions
grep -rn "@Query\|@Mutation" --include="*.resolver.ts" -A 3 | grep -v "@Allow"
# === HIGH PRIORITY ===
# Direct entity returns (should use DTOs or proper types)
grep -rn "Promise<.*Entity>" --include="*.resolver.ts"
# Missing @Transaction on mutations
grep -rn "@Mutation" --include="*.resolver.ts" -A 2 | grep -v "@Transaction"
# InputMaybe not handled correctly
grep -rn "!== undefined" --include="*.service.ts" | grep -v "&& .* !== null"
# === MEDIUM PRIORITY ===
# Hardcoded permission strings
grep -rn "@Allow(['\"]" --include="*.resolver.ts"
# Missing error handling
grep -rn "async.*@Ctx" --include="*.resolver.ts" -A 10 | grep -v "throw\|catch\|try"@Query()
@Allow(Permission.ReadSettings)
async myQuery(): Promise<MyEntity[]> { // No ctx!
return this.service.findAll();
}@Query()
@Allow(Permission.ReadSettings)
async myQuery(@Ctx() ctx: RequestContext): Promise<MyEntity[]> {
return this.service.findAll(ctx);
}@Query()
async myQuery(@Ctx() ctx: RequestContext): Promise<MyEntity[]> {
// No @Allow - anyone can call this!
return this.service.findAll(ctx);
}@Query()
@Allow(Permission.ReadSettings) // Explicit permission
async myQuery(@Ctx() ctx: RequestContext): Promise<MyEntity[]> {
return this.service.findAll(ctx);
}// Only checks undefined, null passes through!
if (input.name !== undefined) {
entity.name = input.name;
}// Check both undefined AND null
if (input.name !== undefined && input.name !== null) {
entity.name = input.name;
}@Mutation()
@Allow(Permission.UpdateSettings)
async updateData(@Ctx() ctx: RequestContext, @Args() args): Promise<MyEntity> {
// No @Transaction - partial updates possible on error!
await this.service.updateA(ctx, args);
await this.service.updateB(ctx, args); // If this fails, A is updated
}@Mutation()
@Transaction() // Atomic operation
@Allow(Permission.UpdateSettings)
async updateData(@Ctx() ctx: RequestContext, @Args() args): Promise<MyEntity> {
await this.service.updateA(ctx, args);
await this.service.updateB(ctx, args);
}// Shop schema
const shopSchema = gql`
type User {
id: ID!
email: String!
internalNotes: String! # Admin-only field exposed!
}
`;// Shop schema - limited fields
const shopSchema = gql`
type User {
id: ID!
email: String!
# internalNotes excluded
}
`;# All-in-one GraphQL audit
echo "=== CRITICAL: Missing @Ctx ===" && \
grep -rn "@Query\|@Mutation" --include="*.resolver.ts" -A 5 | grep -v "@Ctx" | head -20 && \
echo "" && \
echo "=== HIGH: Missing @Allow ===" && \
grep -rn "@Query\|@Mutation" --include="*.resolver.ts" -A 3 | grep -v "@Allow" | head -20 && \
echo "" && \
echo "=== MEDIUM: InputMaybe issues ===" && \
grep -rn "!== undefined" --include="*.ts" | grep -v "&& .* !== null" | head -20## GraphQL Review: [Component Name]
### Summary
[Overview of GraphQL code quality]
### Critical Issues (Must Fix)
- [ ] [Issue] - `file:line`
### High Priority
- [ ] [Issue] - `file:line`
### Passed Checks
- [x] All resolvers have @Resolver decorator
- [x] RequestContext passed consistently
- [x] Permissions declared
### Recommendations
- [Suggestions]