Skip to content
Back to skills

1228 Patterns A864302c

ASecurity

Non-blocking tasks after response sent (analytics, cleanup, webhooks):

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 11, 2026
testingtypescriptgonextjstestingdebuggingapi

Works with

  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 1228-patterns_a864302c --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 1228 Patterns A864302c?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 1228 Patterns A864302c
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-1228-patterns-a864302c/badge)](https://www.skillsdirectory.com/skills/tools-only-1228-patterns-a864302c)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Common Patterns

## Background Tasks (waitUntil)

Non-blocking tasks after response sent (analytics, cleanup, webhooks):

```typescript
export async function onRequest(ctx: EventContext<Env>) {
  const res = Response.json({ success: true })

  ctx.waitUntil(ctx.env.KV.put('last-visit', new Date().toISOString()))
  ctx.waitUntil(
    Promise.all([
      ctx.env.ANALYTICS.writeDataPoint({ event: 'view' }),
      fetch('https://webhook.site/...', { method: 'POST' }),
    ]),
  )

  return res // Returned immediately
}
```

## Middleware & Auth

```typescript
// functions/_middleware.js (global) or functions/users/_middleware.js (scoped)
export async function onRequest(ctx) {
  try {
    return await ctx.next()
  } catch (err) {
    return new Response(err.message, { status: 500 })
  }
}

// Chained: export const onRequest = [errorHandler, auth, logger];

// Auth
async function auth(ctx: EventContext<Env>) {
  const token = ctx.request.headers.get('authorization')?.replace('Bearer ', '')
  if (!token) return new Response('Unauthorized', { status: 401 })
  const session = await ctx.env.KV.get(`session:${token}`)
  if (!session) return new Response('Invalid', { status: 401 })
  ctx.data.user = JSON.parse(session)
  return ctx.next()
}
```

## CORS & Rate Limiting

```typescript
// CORS middleware
const cors = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' }
export async function onRequestOptions() {
  return new Response(null, { headers: cors })
}
export async function onRequest(ctx) {
  const res = await ctx.next()
  Object.entries(cors).forEach(([k, v]) => res.headers.set(k, v))
  return res
}

// Rate limiting (KV-based)
async function rateLimit(ctx: EventContext<Env>) {
  const ip = ctx.request.headers.get('CF-Connecting-IP') || 'unknown'
  const count = parseInt((await ctx.env.KV.get(`rate:${ip}`)) || '0')
  if (count >= 100) return new Response('Rate limited', { status: 429 })
  await ctx.env.KV.put(`rate:${ip}`, (count + 1).toString(), { expirationTtl: 3600 })
  return ctx.next()
}
```

## Forms, Caching, Redirects

```typescript
// JSON & file upload
export async function onRequestPost(ctx) {
  const ct = ctx.request.headers.get('content-type') || ''
  if (ct.includes('application/json')) return Response.json(await ctx.request.json())
  if (ct.includes('multipart/form-data')) {
    const file = (await ctx.request.formData()).get('file') as File
    await ctx.env.BUCKET.put(file.name, file.stream())
    return Response.json({ uploaded: file.name })
  }
}

// Cache API
export async function onRequest(ctx) {
  let res = await caches.default.match(ctx.request)
  if (!res) {
    res = new Response('Data')
    res.headers.set('Cache-Control', 'public, max-age=3600')
    ctx.waitUntil(caches.default.put(ctx.request, res.clone()))
  }
  return res
}

// Redirects
export async function onRequest(ctx) {
  if (new URL(ctx.request.url).pathname === '/old') {
    return Response.redirect(new URL('/new', ctx.request.url), 301)
  }
  return ctx.next()
}
```

## Testing

**Unit tests** (Vitest + cloudflare:test):

```typescript
import { env } from 'cloudflare:test'
import { it, expect } from 'vitest'
import { onRequest } from '../functions/api'

it('returns JSON', async () => {
  const req = new Request('http://localhost/api')
  const ctx = { request: req, env, params: {}, data: {} } as EventContext
  const res = await onRequest(ctx)
  expect(res.status).toBe(200)
})
```

**Integration:** `wrangler pages dev` + Playwright/Cypress

## Advanced Mode (\_worker.js)

Use `_worker.js` for complex routing (replaces `/functions`):

```typescript
interface Env {
  ASSETS: Fetcher
  KV: KVNamespace
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url)
    if (url.pathname.startsWith('/api/')) {
      return Response.json({ data: await env.KV.get('key') })
    }
    return env.ASSETS.fetch(request) // Static files
  },
} satisfies ExportedHandler<Env>
```

**When:** Existing Worker, framework-generated (Next.js/SvelteKit), custom routing logic

**See also:** [api.md](./api.md) for `env.ASSETS.fetch()` | [gotchas.md](./gotchas.md) for debugging

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.