Follow API development conventions including RESTful design, Fastify patterns, Zod validation, error handling, and versioning. Use when building API endpoints, adding routes, or working with API code.
Scanned 2/12/2026
Install to Claude Code
npx -y skills add majiayu000/claude-skill-registry --skill api-style --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Api Style?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/majiayu000-api-style)More formats (shields.io, HTML) on the badges page.
---
name: innozverse-api-style
description: Follow API development conventions including RESTful design, Fastify patterns, Zod validation, error handling, and versioning. Use when building API endpoints, adding routes, or working with API code.
---
# innozverse API Development Style
When developing API endpoints for innozverse, follow these patterns and conventions.
## Fastify Basics
### Route Registration
```typescript
// apps/api/src/routes/v1/users.ts
import { FastifyInstance } from 'fastify';
export async function usersRoutes(fastify: FastifyInstance) {
fastify.get('/users', async (request, reply) => {
return { users: [] };
});
fastify.get('/users/:id', async (request, reply) => {
const { id } = request.params as { id: string };
return { user: { id } };
});
fastify.post('/users', async (request, reply) => {
const body = request.body;
return reply.code(201).send({ user: body });
});
}
```
### Register in Index
```typescript
// apps/api/src/index.ts
import { usersRoutes } from './routes/v1/users';
fastify.register(usersRoutes, { prefix: '/v1' });
```
## Response Patterns
### Success Response
```typescript
return reply.code(200).send({
status: 'ok',
data: { /* ... */ }
});
```
### Created Response
```typescript
return reply.code(201).send({
status: 'created',
data: { id: newId }
});
```
### Error Response
```typescript
return reply.code(400).send({
error: 'ValidationError',
message: 'Invalid input',
statusCode: 400
});
```
## Type Safety
### Define Types in @innozverse/shared
```typescript
// packages/shared/src/types.ts
export interface User {
id: string;
name: string;
email: string;
}
export interface UserResponse {
status: 'ok';
data: User;
}
```
### Use in API
```typescript
import { User, UserResponse } from '@innozverse/shared';
fastify.get<{ Reply: UserResponse }>('/users/:id', async (request, reply) => {
const user: User = { /* ... */ };
return reply.send({
status: 'ok',
data: user
});
});
```
## Validation with Zod
### Define Schema in @innozverse/shared
```typescript
// packages/shared/src/schemas.ts
import { z } from 'zod';
export const userSchema = z.object({
name: z.string().min(1),
email: z.string().email()
});
```
### Use in API
```typescript
import { userSchema } from '@innozverse/shared';
fastify.post('/users', async (request, reply) => {
try {
const validated = userSchema.parse(request.body);
// Use validated data
return reply.code(201).send({ data: validated });
} catch (error) {
return reply.code(400).send({
error: 'ValidationError',
message: error.message
});
}
});
```
## Error Handling
### Global Error Handler
```typescript
// apps/api/src/index.ts
fastify.setErrorHandler((error, request, reply) => {
fastify.log.error(error);
reply.status(error.statusCode || 500).send({
error: error.name || 'InternalServerError',
message: error.message || 'Something went wrong',
statusCode: error.statusCode || 500
});
});
```
### Throwing Errors
```typescript
fastify.get('/users/:id', async (request, reply) => {
const user = await findUser(id);
if (!user) {
return reply.code(404).send({
error: 'NotFound',
message: 'User not found',
statusCode: 404
});
}
return { data: user };
});
```
## Async/Await
Always use async/await, never callbacks:
```typescript
// ✅ Good
fastify.get('/users', async (request, reply) => {
const users = await getUsers();
return { users };
});
// ❌ Bad
fastify.get('/users', (request, reply) => {
getUsers((err, users) => {
reply.send({ users });
});
});
```
## RESTful Conventions
### Resource Naming
- Plural nouns: `/users`, `/posts`
- Nested resources: `/users/:userId/posts`
### HTTP Methods
- `GET /resource` - List all
- `GET /resource/:id` - Get one
- `POST /resource` - Create
- `PUT /resource/:id` - Replace
- `PATCH /resource/:id` - Update
- `DELETE /resource/:id` - Delete
### Status Codes
- `200` - Success (GET, PUT, PATCH, DELETE)
- `201` - Created (POST)
- `204` - No Content (DELETE)
- `400` - Bad Request (validation error)
- `401` - Unauthorized
- `403` - Forbidden
- `404` - Not Found
- `409` - Conflict (duplicate)
- `500` - Internal Server Error
## Versioning
Always version API routes:
```typescript
// ✅ Good
fastify.register(v1Routes, { prefix: '/v1' });
fastify.register(v2Routes, { prefix: '/v2' });
// ❌ Bad
fastify.register(routes); // No version
```
## CORS Configuration
```typescript
import cors from '@fastify/cors';
await fastify.register(cors, {
origin: process.env.CORS_ORIGIN || '*',
credentials: true
});
```
## Environment Variables
```typescript
const PORT = parseInt(process.env.PORT || '8080', 10);
const NODE_ENV = process.env.NODE_ENV || 'development';
// Never hardcode secrets
const DB_URL = process.env.DATABASE_URL; // ✅
const API_KEY = process.env.API_KEY; // ✅
```
## Logging
```typescript
// Use Fastify's built-in logger
fastify.log.info('Server starting');
fastify.log.error({ err: error }, 'Error occurred');
fastify.log.debug({ data }, 'Debug info');
```
## Health Check
Always maintain a health check endpoint:
```typescript
fastify.get('/health', async () => ({
status: 'ok',
timestamp: new Date().toISOString(),
version: process.env.API_VERSION || '1.0.0'
}));
```
## Testing (Future)
```typescript
// apps/api/src/routes/__tests__/users.test.ts
import { buildServer } from '../../index';
describe('Users API', () => {
let fastify;
beforeAll(async () => {
fastify = await buildServer();
});
afterAll(async () => {
await fastify.close();
});
test('GET /v1/users returns users list', async () => {
const response = await fastify.inject({
method: 'GET',
url: '/v1/users'
});
expect(response.statusCode).toBe(200);
expect(response.json()).toHaveProperty('users');
});
});
```
## Database Patterns (Future)
When adding database:
```typescript
// Use a connection pool
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
// Close connections gracefully
fastify.addHook('onClose', async () => {
await pool.end();
});
// Use in routes
fastify.get('/users/:id', async (request, reply) => {
const { rows } = await pool.query(
'SELECT * FROM users WHERE id = $1',
[request.params.id]
);
if (rows.length === 0) {
return reply.code(404).send({ error: 'Not found' });
}
return { data: rows[0] };
});
```
## Best Practices
### Single Responsibility
Each route file handles one resource:
```
routes/v1/
├── users.ts # User management
├── posts.ts # Post management
└── comments.ts # Comment management
```
### DRY Principles
Extract common logic:
```typescript
// utils/auth.ts
export async function requireAuth(request, reply) {
const token = request.headers.authorization;
if (!token) {
return reply.code(401).send({ error: 'Unauthorized' });
}
// Verify token
}
// routes/v1/users.ts
fastify.get('/users/me', {
preHandler: requireAuth
}, async (request, reply) => {
return { user: request.user };
});
```
### Graceful Shutdown
```typescript
process.on('SIGTERM', async () => {
await fastify.close();
process.exit(0);
});
```
## Anti-Patterns to Avoid
❌ Don't use `any` types:
```typescript
// Bad
fastify.get('/users', async (request: any, reply: any) => {
```
❌ Don't block the event loop:
```typescript
// Bad
fastify.get('/heavy', async () => {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return { result };
});
```
❌ Don't ignore errors:
```typescript
// Bad
fastify.get('/users', async () => {
const users = await getUsers().catch(() => []);
return { users };
});
```
❌ Don't expose internal errors to clients:
```typescript
// Bad
return reply.code(500).send({ error: error.stack });
// Good
fastify.log.error(error);
return reply.code(500).send({ error: 'Internal Server Error' });
```
## References
- [Fastify Documentation](https://www.fastify.io/)
- [Zod Documentation](https://zod.dev/)
- [RESTful API Design](https://restfulapi.net/)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!