innozverse-api-style

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.

16 stars

Best use case

innozverse-api-style is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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.

Teams using innozverse-api-style should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/innozverse-api-style/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/backend/innozverse-api-style/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/innozverse-api-style/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How innozverse-api-style Compares

Feature / Agentinnozverse-api-styleStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# 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/)

Related Skills

37signals-rails-style

16
from diegosouzapw/awesome-omni-skill

Apply 37signals/DHH Rails conventions when writing Ruby on Rails code. Use when building Rails applications, reviewing Rails code, or making architectural decisions. Covers various aspects of Rails application architecture, design and dependencies.

eos-style

16
from diegosouzapw/awesome-omni-skill

Strunk & White style review using the 21 reminders from "Elements of Style" Chapter V. Use when editing prose, reviewing drafts, or improving writing clarity and tone.

academic-writing-style

16
from diegosouzapw/awesome-omni-skill

Personalized academic writing assistant for university assignments in Chinese and English. Use when users need help writing/revising academic reports, project docs, technical analyses, research reviews, or case studies. Produces natural prose avoiding AI markers. Triggers: academic writing, assignment, report, technical analysis, research review, case study. | 个性化学术写作助手,适用于中英文大学作业。触发词:学术写作、作业、报告、技术分析、研究综述、案例研究、项目文档。

button-styles

16
from diegosouzapw/awesome-omni-skill

Sistema de estilos de botones consistentes para iqEngi (Cards, CTAs, Formularios)

api-style-guide

16
from diegosouzapw/awesome-omni-skill

Style and formatting rules for API/SDK documentation, samples, and tutorials.

bgo

10
from diegosouzapw/awesome-omni-skill

Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.

Coding & Development

obsidian-daily

16
from diegosouzapw/awesome-omni-skill

Manage Obsidian Daily Notes via obsidian-cli. Create and open daily notes, append entries (journals, logs, tasks, links), read past notes by date, and search vault content. Handles relative dates like "yesterday", "last Friday", "3 days ago".

obsidian-additions

16
from diegosouzapw/awesome-omni-skill

Create supplementary materials attached to existing notes: experiments, meetings, reports, logs, conspectuses, practice sessions, annotations, AI outputs, links collections. Two-step process: (1) create aggregator space, (2) create concrete addition in base/additions/. INVOKE when user wants to attach any supplementary material to an existing note. Triggers: "addition", "create addition", "experiment", "meeting notes", "report", "conspectus", "log", "practice", "annotations", "links", "link collection", "аддишн", "конспект", "встреча", "отчёт", "эксперимент", "практика", "аннотации", "ссылки", "добавь к заметке".

observe

16
from diegosouzapw/awesome-omni-skill

Query and manage Observe using the Observe CLI. Use when the user wants to run OPAL queries, list datasets, manage objects, or interact with their Observe tenant from the command line.

observability-review

16
from diegosouzapw/awesome-omni-skill

AI agent that analyzes operational signals (metrics, logs, traces, alerts, SLO/SLI reports) from observability platforms (Prometheus, Datadog, New Relic, CloudWatch, Grafana, Elastic) and produces practical, risk-aware triage and recommendations. Use when reviewing system health, investigating performance issues, analyzing monitoring data, evaluating service reliability, or providing SRE analysis of operational metrics. Distinguishes between critical issues requiring action, items needing investigation, and informational observations requiring no action.

nvidia-nim

16
from diegosouzapw/awesome-omni-skill

NVIDIA NIM inference microservices for deploying AI models with OpenAI-compatible APIs, self-hosted or cloud

numpy-string-ops

16
from diegosouzapw/awesome-omni-skill

Vectorized string manipulation using the char module and modern string alternatives, including cleaning and search operations. Triggers: string operations, numpy.char, text cleaning, substring search.