nextjs-api-tester — Claude Code command: what it does, how to install
Inspects Next.js API routes and generates complete test suites. Useful for verifying authentication, payload validation, and errors across App and Pages Router.
npx claude-code-templates@latest --command nextjs-vercel/nextjs-api-testerWhat nextjs-api-tester does
The command inspects your project setup by scanning @app/api/ or @pages/api/. It checks the Next.js version via package.json, reads .env.local, and locates tsconfig.json alongside either jest.config.js or vitest.config.js. Then it inspects the route file to understand supported HTTP verbs, dynamic parameters, middleware, validation schemas, and database models.
From this analysis, it writes test cases covering common failure points. It creates checks for valid responses, missing JWT tokens returning 401 or 403, invalid JSON payloads, timeouts, and simulated database errors returning 500. It also outlines concurrency checks and rate-limit handling with status 429, plus manual curl calls for http://localhost:3000/.
The command requests Read, Write, Edit, and Bash tool permissions. Without Jest or Vitest pre-configured in the repository, the generated test files will fail to execute directly.
What it helps with
- generate unit tests for GET, POST, PUT, DELETE, and PATCH endpoints
- verify route protection against missing or invalid JWT tokens
- test input schema validation against malformed payloads
- produce cURL command snippets for local manual testing
Example prompts for the agent
- /nextjs-api-tester users --method=GET
- /nextjs-api-tester auth/login --method=POST --data='{"email":"[email protected]"}'
- /nextjs-api-tester orders --method=POST --headers='{"Authorization":"Bearer test"}'
How to install nextjs-api-tester
1claude-code-templates
npx claude-code-templates@latest --command nextjs-vercel/nextjs-api-tester2Manually: put the files in place
.claude/commands/nextjs-api-tester.mdCall it with /nextjs-api-tester. For all projects use ~/.claude/commands/
Manually: put the files in place
~/.codex/prompts/nextjs-api-tester.mdCall it with /prompts:nextjs-api-tester. Codex marks this format deprecated in favour of skills
Manually: put the files in place
.github/prompts/nextjs-api-tester.prompt.mdManually: put the files in place
.gemini/commands/nextjs-api-tester.tomlGemini stores commands as TOML: the prompt text goes into the prompt field
Questions about nextjs-api-tester
How to install nextjs-api-tester in Claude Code?
Run npx claude-code-templates@latest --command nextjs-vercel/nextjs-api-tester. This creates .claude/commands/nextjs-api-tester.md in your repository.
What does nextjs-api-tester do?
It analyzes Next.js API route implementations across App Router and Pages Router. It then generates test files covering auth, input validation, database failure handling, and concurrency.
Which test runners does it support?
It checks for either jest.config.js or vitest.config.js in the project root. Generated test syntax aligns with Jest and Vitest conventions.
Source text
nextjs-api-tester.md · MIT
---
allowed-tools: Read, Write, Edit, Bash
argument-hint: "[route-path] [--method=GET] [--data='{}'] [--headers='{}']"
description: Test and validate Next.js API routes with comprehensive test scenarios
---
## Next.js API Route Tester
**API Route**: $ARGUMENTS
## Current Project Analysis
### API Routes Detection
- App Router API: @app/api/
- Pages Router API: @pages/api/
- API configuration: @next.config.js
- Environment variables: @.env.local
### Project Context
- Next.js version: !`grep '"next"' package.json | head -1`
- TypeScript config: @tsconfig.json (if exists)
- Testing framework: @jest.config.js or @vitest.config.js (if exists)
## API Route Analysis
### Route Discovery
Based on the provided route path, analyze:
- **Route File**: Locate the actual route file
- **HTTP Methods**: Supported methods (GET, POST, PUT, DELETE, PATCH)
- **Route Parameters**: Dynamic segments and query parameters
- **Middleware**: Applied middleware functions
- **Authentication**: Required authentication/authorization
### Route Implementation Review
- Route handler implementation: @app/api/[route-path]/route.ts or @pages/api/[route-path].ts
- Type definitions: @types/ or inline types
- Validation schemas: @lib/validations/ or inline validation
- Database models: @lib/models/ or @models/
## Test Generation Strategy
### 1. Basic Functionality Tests
```javascript
// Basic API route test template
describe('API Route: /api/[route-path]', () => {
describe('GET requests', () => {
test('should return 200 for valid request', async () => {
const response = await fetch('/api/[route-path]');
expect(response.status).toBe(200);
});
test('should return valid JSON response', async () => {
const response = await fetch('/api/[route-path]');
const data = await response.json();
expect(data).toBeDefined();
expect(typeof data).toBe('object');
});
});
describe('POST requests', () => {
test('should create resource with valid data', async () => {
const testData = { name: 'Test', email: '[email protected]' };
const response = await fetch('/api/[route-path]', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(testData)
});
expect(response.status).toBe(201);
const result = await response.json();
expect(result.name).toBe(testData.name);
});
test('should reject invalid data', async () => {
const invalidData = { invalid: 'field' };
const response = await fetch('/api/[route-path]', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(invalidData)
});
expect(response.status).toBe(400);
});
});
});
```
### 2. Authentication Tests
```javascript
describe('Authentication', () => {
test('should require authentication for protected routes', async () => {
const response = await fetch('/api/protected-route');
expect(response.status).toBe(401);
});
test('should allow authenticated requests', async () => {
const token = 'valid-jwt-token';
const response = await fetch('/api/protected-route', {
headers: { 'Authorization': `Bearer ${token}` }
});
expect(response.status).not.toBe(401);
});
test('should validate JWT token format', async () => {
const invalidToken = 'invalid-token';
const response = await fetch('/api/protected-route', {
headers: { 'Authorization': `Bearer ${invalidToken}` }
});
expect(response.status).toBe(403);
});
});
```
### 3. Input Validation Tests
```javascript
describe('Input Validation', () => {
const validationTests = [
{ field: 'email', invalid: 'not-an-email', valid: '[email protected]' },
{ field: 'phone', invalid: '123', valid: '+1234567890' },
{ field: 'age', invalid: -1, valid: 25 },
{ field: 'name', invalid: '', valid: 'John Doe' }
];
validationTests.forEach(({ field, invalid, valid }) => {
test(`should validate ${field} field`, async () => {
const invalidData = { [field]: invalid };
const validData = { [field]: valid };
// Test invalid data
const invalidResponse = await fetch('/api/[route-path]', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(invalidData)
});
expect(invalidResponse.status).toBe(400);
// Test valid data
const validResponse = await fetch('/api/[route-path]', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(validData)
});
expect(validResponse.status).not.toBe(400);
});
});
});
```
### 4. Error Handling Tests
```javascript
describe('Error Handling', () => {
test('should handle malformed JSON', async () => {
const response = await fetch('/api/[route-path]', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: 'invalid-json'
});
expect(response.status).toBe(400);
});
test('should handle missing Content-Type header', async () => {
const response = await fetch('/api/[route-path]', {
method: 'POST',
body: JSON.stringify({ test: 'data' })
});
expect(response.status).toBe(400);
});
test('should handle request timeout', async () => {
// Mock slow endpoint
jest.setTimeout(5000);
const response = await fetch('/api/slow-endpoint');
// Test appropriate timeout handling
}, 5000);
test('should handle database connection errors', async () => {
// Mock database failure
const mockDbError = jest.spyOn(db, 'connect').mockRejectedValue(new Error('DB Error'));
const response = await fetch('/api/[route-path]');
expect(response.status).toBe(500);
mockDbError.mockRestore();
});
});
```
### 5. Performance Tests
```javascript
describe('Performance', () => {
test('should respond within acceptable time', async () => {
const startTime = Date.now();
const response = await fetch('/api/[route-path]');
const endTime = Date.now();
expect(response.status).toBe(200);
expect(endTime - startTime).toBeLessThan(1000); // 1 second
});
test('should handle concurrent requests', async () => {
const promises = Array.from({ length: 10 }, () =>
fetch('/api/[route-path]')
);
const responses = await Promise.all(promises);
responses.forEach(response => {
expect(response.status).toBe(200);
});
});
test('should implement rate limiting', async () => {
const requests = Array.from({ length: 100 }, () =>
fetch('/api/[route-path]')
);
const responses = await Promise.all(requests);
const rateLimitedResponses = responses.filter(r => r.status === 429);
expect(rateLimitedResponses.length).toBeGreaterThan(0);
});
});
```
## Manual Testing Commands
### cURL Commands Generation
```bash
# GET request
curl -X GET "http://localhost:3000/api/[route-path]" \
-H "Accept: application/json"
# POST request with data
curl -X POST "http://localhost:3000/api/[route-path]" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"key": "value"}'
# Authenticated request
curl -X GET "http://localhost:3000/api/protected-route" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Accept: application/json"
# Upload file
curl -X POST "http://localhost:3000/api/upload" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-F "file=@path/to/file.jpg"
```
### HTTPie Commands
```bash
# GET request
http GET localhost:3000/api/[route-path]
# POST request with JSON
http POST localhost:3000/api/[route-path] key=value
# Authenticated request
http GET localhost:3000/api/protected-route Authorization:"Bearer TOKEN"
# Custom headers
http GET localhost:3000/api/[route-path] X-Custom-Header:value
```
## Interactive Testing Tools
### Postman Collection Generation
```json
{
"info": {
"name": "Next.js API Tests",
"description": "Generated API tests for [route-path]"
},
"item": [
{
"name": "GET [route-path]",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/[route-path]",
"host": ["{{baseUrl}}"],
"path": ["api", "[route-path]"]
}
}
},
{
"name": "POST [route-path]",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"key\": \"value\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/[route-path]",
"host": ["{{baseUrl}}"],
"path": ["api", "[route-path]"]
}
}
}
]
}
```
### Thunder Client Collection
```json
{
"client": "Thunder Client",
"collectionName": "Next.js API Tests",
"dateExported": "2024-01-01",
"version": "1.1",
"folders": [],
"requests": [
{
"name": "Test API Route",
"url": "localhost:3000/api/[route-path]",
"method": "GET",
"headers": [
{
"name": "Accept",
"value": "application/json"
}
]
}
]
}
```
## Test Data Management
### Test Fixtures
```typescript
// test/fixtures/apiTestData.ts
export const validUserData = {
name: 'John Doe',
email: '[email protected]',
age: 30,
role: 'user'
};
export const invalidUserData = {
name: '',
email: 'invalid-email',
age: -1,
role: 'invalid-role'
};
export const testHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'API-Test-Suite/1.0'
};
```
### Mock Data Generation
```typescript
// test/utils/mockData.ts
export function generateMockUser() {
return {
id: Math.random().toString(36).substr(2, 9),
name: `User ${Math.floor(Math.random() * 1000)}`,
email: `user${Date.now()}@example.com`,
createdAt: new Date().toISOString()
};
}
export function generateBulkTestData(count: number) {
return Array.from({ length: count }, generateMockUser);
}
```
## Test Environment Setup
### Jest Configuration
```javascript
// jest.config.js for API testing
module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
testMatch: ['**/__tests__/**/*.test.js', '**/?(*.)+(spec|test).js'],
collectCoverageFrom: [
'pages/api/**/*.{js,ts}',
'app/api/**/*.{js,ts}',
'!**/*.d.ts',
],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
}
};
```
### Test Setup
```javascript
// test/setup.js
import { createMocks } from 'node-mocks-http';
import { testDb } from './testDatabase';
// Global test setup
beforeAll(async () => {
// Setup test database
await testDb.connect();
});
afterAll(async () => {
// Cleanup test database
await testDb.disconnect();
});
beforeEach(async () => {
// Reset database state
await testDb.reset();
});
// Helper function for API testing
global.createAPITest = (handler) => {
return (method, url, options = {}) => {
const { req, res } = createMocks({
method,
url,
...options
});
return handler(req, res);
};
};
```
## Automated Testing Integration
### GitHub Actions Workflow
```yaml
name: API Tests
on: [push, pull_request]
jobs:
test-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run test:api
- name: Upload coverage
uses: codecov/codecov-action@v3
```
### Continuous Testing
```bash
# Watch mode for development
npm run test:api -- --watch
# Coverage reporting
npm run test:api -- --coverage
# Specific route testing
npm run test:api -- --testNamePattern="api/users"
```
## Test Results Analysis
Generate comprehensive test report including:
1. **Test Coverage**: Line, branch, function coverage percentages
2. **Performance Metrics**: Response times, throughput
3. **Security Analysis**: Authentication, authorization, input validation
4. **Error Handling**: Exception scenarios and error responses
5. **Compatibility**: Cross-environment testing results
Provide actionable recommendations for improving API reliability, performance, and security.
© davila7 · MIT · davila7/claude-code-templates