Overview
Validate API endpoints with curl and jq. No Jest or Playwright - use CLI tools for testing and manual verification for UI.
What it is
API testing patterns using curl for requests, jq for JSON parsing, and systematic manual verification for UI interactions.
Why we use it
Fast feedback loop, no test framework overhead, and realistic testing against actual endpoints.
When to use
After implementing API endpoints, before deployment, and when debugging issues.
Key Features
- API testing with curl and jq
- Response schema validation
- Systematic manual UI testing
- Testing error paths and edge cases
Quick Start
Test with curl
Basic API testing patterns with curl.
# API Testing with curl
# Test a GET endpoint
curl http://localhost:3000/api/v1/projects | jq
# Test with authentication
curl -X GET http://localhost:3000/api/v1/projects \
-H "Authorization: Bearer ${TOKEN}" | jq
# Test a POST endpoint
curl -X POST http://localhost:3000/api/v1/projects \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{"name": "Test Project", "programId": "prg_123"}' | jqPatterns
Validation Testing
Test that validation errors are returned correctly.
# Testing validation errors
# Missing required field
curl -X POST http://localhost:3000/api/v1/projects \
-H "Content-Type: application/json" \
-d '{}' | jq
# Expected response:
# {
# "ok": false,
# "error": {
# "code": "VALIDATION_ERROR",
# "message": "Validation failed",
# "details": [
# { "path": ["name"], "message": "Required" }
# ]
# }
# }
# Invalid field type
curl -X POST http://localhost:3000/api/v1/projects \
-H "Content-Type: application/json" \
-d '{"name": 123}' | jqAuthentication Testing
Test authenticated and unauthenticated requests.
# Authentication testing
# Test without auth header
curl http://localhost:3000/api/v1/projects | jq
# Expected: { "ok": false, "error": { "code": "UNAUTHORIZED" } }
# Test with invalid token
curl http://localhost:3000/api/v1/projects \
-H "Authorization: Bearer invalid_token" | jq
# Expected: { "ok": false, "error": { "code": "UNAUTHORIZED" } }
# Test with valid token
curl http://localhost:3000/api/v1/projects \
-H "Authorization: Bearer ${VALID_TOKEN}" | jq
# Expected: { "ok": true, "data": [...] }Error Path Testing
Test 404, 403, 429 and other error responses.
# Testing error paths
# Test 404 Not Found
curl http://localhost:3000/api/v1/projects/nonexistent_id | jq
# Expected: { "ok": false, "error": { "code": "NOT_FOUND" } }
# Test 403 Forbidden (wrong user)
curl http://localhost:3000/api/v1/projects/other_users_project \
-H "Authorization: Bearer ${TOKEN}" | jq
# Expected: { "ok": false, "error": { "code": "FORBIDDEN" } }
# Test 429 Rate Limited (send many requests)
for i in {1..100}; do
curl http://localhost:3000/api/v1/ai/generate \
-H "Authorization: Bearer ${TOKEN}"
done | tail -1 | jq
# Expected: { "ok": false, "error": { "code": "RATE_LIMITED" } }Schema Validation
Validate response structure with jq.
# Schema validation with jq
# Define expected schema
expected_keys='["ok", "data"]'
# Validate response structure
response=$(curl -s http://localhost:3000/api/v1/projects)
# Check for required keys
echo $response | jq "keys | sort == ($expected_keys | sort)"
# Validate data array
echo $response | jq '.data | type == "array"'
# Validate item structure
echo $response | jq '.data[0] | has("id", "name", "createdAt")'Manual Verification
Systematic checklist for UI testing.
# Manual verification checklist # 1. Start dev server pnpm dev # 2. Monitor server logs in terminal # Watch for errors, warnings, slow queries # 3. Test in browser # - Check network tab for API responses # - Verify data displays correctly # - Test error states (disconnect network) # 4. Test forms # - Submit with valid data # - Submit with invalid data # - Submit empty form # - Test loading states
Watch Out
Only testing happy paths
Don't
// Only testing happy path
test('creates project', async () => {
const result = await createProject(validData);
expect(result.ok).toBe(true);
});
// What about invalid data? Auth errors?Do
// Testing error paths
# Test validation error
curl -X POST /api/projects -d '{}' | jq
# Verify: 400, VALIDATION_ERROR
# Test auth error
curl /api/projects # No auth header
# Verify: 401, UNAUTHORIZED
# Test not found
curl /api/projects/invalid_id
# Verify: 404, NOT_FOUNDUsing production data in tests
Don't
// Using production data in tests
const user = await getUser('real_user_id');
// NEVER! Production data might:
// - Expose PII
// - Have unexpected state
// - Change between testsDo
// Using test data
# Create test-specific data
curl -X POST /api/v1/test/seed -d '{
"users": 5,
"projects": 10
}'
# Or use local development database
# with seeded test data- Deploying without any testing
- Tests that pass inconsistently