Backend development skill for Cloudflare Workers, D1 database, and REST API. Use when implementing API features.
Builds backend APIs with Cloudflare Workers, Hono, and D1 database using Drizzle ORM. Use when implementing REST endpoints with JWT authentication and Zod validation.
/plugin marketplace add shabaraba/shabaraba-cc-plugins/plugin install claude-org@shabaraba-cc-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Platform-specific knowledge for backend/API development.
| Component | Technology |
|---|---|
| Runtime | Cloudflare Workers |
| Framework | Hono |
| Database | D1 (SQLite) |
| ORM | Drizzle ORM |
| Validation | Zod |
| Auth | JWT / Cloudflare Access |
camelCasePascalCaseSCREAMING_SNAKE_CASEsnake_casekebab-casesrc/
├── index.ts # Entry point, Hono app
├── routes/
│ ├── users.ts # /api/users routes
│ └── posts.ts # /api/posts routes
├── services/
│ ├── user-service.ts
│ └── post-service.ts
├── db/
│ ├── schema.ts # Drizzle schema
│ └── migrations/
├── middleware/
│ ├── auth.ts
│ └── cors.ts
└── types/
└── index.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono<{ Bindings: Env }>()
// Route with validation
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
app.post('/users', zValidator('json', createUserSchema), async (c) => {
const data = c.req.valid('json')
const db = c.env.DB
const result = await db
.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
.bind(data.name, data.email)
.run()
return c.json({ id: result.lastRowId }, 201)
})
export default app
# Development
pnpm dev # or wrangler dev
# Deploy
pnpm deploy # or wrangler deploy
# Database migration
pnpm db:generate # Generate migration
pnpm db:migrate # Apply migration (local)
pnpm db:migrate:prod # Apply to production
# Type generation
pnpm cf-typegen
// src/db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`),
})
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').references(() => users.id),
title: text('title').notNull(),
content: text('content'),
})
import { drizzle } from 'drizzle-orm/d1'
import { eq } from 'drizzle-orm'
import * as schema from './db/schema'
// In route handler
const db = drizzle(c.env.DB, { schema })
// Select
const users = await db.select().from(schema.users).all()
// Select with where
const user = await db.select()
.from(schema.users)
.where(eq(schema.users.id, id))
.get()
// Insert
const result = await db.insert(schema.users)
.values({ name, email })
.returning()
// Update
await db.update(schema.users)
.set({ name: newName })
.where(eq(schema.users.id, id))
// Delete
await db.delete(schema.users)
.where(eq(schema.users.id, id))
GET /api/users # List users
POST /api/users # Create user
GET /api/users/:id # Get user
PUT /api/users/:id # Update user
DELETE /api/users/:id # Delete user
GET /api/users/:id/posts # List user's posts
// Success
{
"data": { ... },
"meta": {
"total": 100,
"page": 1,
"limit": 20
}
}
// Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": [...]
}
}
import { HTTPException } from 'hono/http-exception'
// Throw error
if (!user) {
throw new HTTPException(404, { message: 'User not found' })
}
// Global error handler
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: { message: err.message } }, err.status)
}
console.error(err)
return c.json({ error: { message: 'Internal Server Error' } }, 500)
})
import { jwt } from 'hono/jwt'
// Middleware
app.use('/api/*', jwt({ secret: c.env.JWT_SECRET }))
// Access payload
app.get('/api/me', (c) => {
const payload = c.get('jwtPayload')
return c.json({ userId: payload.sub })
})
// Generate token
import { sign } from 'hono/jwt'
const token = await sign(
{ sub: user.id, exp: Math.floor(Date.now() / 1000) + 60 * 60 },
env.JWT_SECRET
)
import { describe, it, expect } from 'vitest'
import app from './index'
describe('Users API', () => {
it('GET /api/users returns users', async () => {
const res = await app.request('/api/users')
expect(res.status).toBe(200)
const data = await res.json()
expect(Array.isArray(data.data)).toBe(true)
})
})
wrangler d1 migrations apply DBimport { cors } from 'hono/cors'
app.use('*', cors())
.dev.vars filewrangler secret put SECRET_NAMEThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.