Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/landing/src/components/Features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const features = [
desc: <>Scalar API reference at <code>/docs</code> and an OpenAPI spec at <code>/openapi.json</code> &mdash; generated, never stale.</>,
},
{
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>,
title: "Just net/http",
desc: <>Implements <code>http.Handler</code>. Works with any middleware, any router, any test framework. Zero lock-in.</>,
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="M16 6l4 6-4 6"/></svg>,
title: "Real-Time",
desc: <><code>HandleSSE</code> gives you typed server push with an <code>sse</code> helper. <code>HandleWS</code> adds typed bidirectional WebSockets with a <code>connect</code> helper.</>,
},
];

Expand Down
11 changes: 11 additions & 0 deletions apps/landing/src/content/docs/docs/core-concepts/handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,15 @@ shiftapi.Handle(api, "POST /users", createUser,

`WithError`, `WithMiddleware`, and `WithResponseHeader` are `Option` values — they work at all levels (API, group, and route). `WithStatus` and `WithRouteInfo` are route-only options. See [Options](/docs/core-concepts/options) for the full option system and composition.

## Other handler types

ShiftAPI provides specialized handler functions for different use cases:

| Function | Use case |
|----------|----------|
| `Handle` | JSON API endpoints (this page) |
| [`HandleSSE`](/docs/core-concepts/server-sent-events) | Server-Sent Events with a typed writer and auto-generated TypeScript `sse` helper |
| [`HandleWS`](/docs/core-concepts/websockets) | Bidirectional WebSocket with typed send/receive, auto upgrade, and auto-generated TypeScript `websocket` helper |
| [`HandleRaw`](/docs/core-concepts/raw-handlers) | File downloads, custom framing, and other responses that need direct `ResponseWriter` access |

See also: [Middleware](/docs/core-concepts/middleware), [Error Handling](/docs/core-concepts/error-handling), [Options](/docs/core-concepts/options).
17 changes: 10 additions & 7 deletions apps/landing/src/content/docs/docs/core-concepts/raw-handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ sidebar:
order: 7
---

ShiftAPI's `Handle` function owns the response lifecycle — it JSON-encodes whatever your handler returns. But some responses can't be expressed as a typed struct: Server-Sent Events, file downloads, WebSocket upgrades, chunked streaming, and more. `HandleRaw` gives you full control over the `http.ResponseWriter` while keeping typed input parsing, validation, and middleware.
ShiftAPI's `Handle` function owns the response lifecycle — it JSON-encodes whatever your handler returns. But some responses can't be expressed as a typed struct: file downloads, chunked streaming, and more. `HandleRaw` gives you full control over the `http.ResponseWriter` while keeping typed input parsing, validation, and middleware.

For SSE and WebSocket use cases, prefer [`HandleSSE`](/docs/core-concepts/server-sent-events) and [`HandleWS`](/docs/core-concepts/websockets) which provide typed abstractions. Use `HandleRaw` when you need custom framing or non-standard behavior.

## Registering raw handlers

```go
shiftapi.HandleRaw(api, "GET /events", sseHandler)
shiftapi.HandleRaw(api, "GET /files/{id}", downloadHandler)
shiftapi.HandleRaw(api, "GET /ws", wsHandler)
```

## Handler signature
Expand Down Expand Up @@ -186,10 +187,10 @@ The schema is generated using the same reflection logic as typed handlers — st

### No WithContentType (default)

A `HandleRaw` route with no `WithContentType` produces a response with only a description. This is appropriate for WebSocket upgrades or other routes where the response format isn't meaningful to document:
A `HandleRaw` route with no `WithContentType` produces a response with only a description. This is appropriate for routes where the response format isn't meaningful to document:

```go
shiftapi.HandleRaw(api, "GET /ws", wsHandler)
shiftapi.HandleRaw(api, "GET /proxy", proxyHandler)
```

Produces:
Expand Down Expand Up @@ -237,14 +238,16 @@ shiftapi.HandleRaw(api, "GET /events", sseHandler,
| `WithMiddleware(mw...)` | Apply HTTP middleware |
| `WithResponseHeader(name, value)` | Set a static response header |

## When to use HandleRaw vs Handle
## When to use HandleRaw vs Handle vs HandleSSE vs HandleWS

| Use case | Recommendation |
|----------|---------------|
| JSON API endpoint | `Handle` — let the framework encode the response |
| Server-Sent Events (SSE) | `HandleRaw` + `WithContentType("text/event-stream")` |
| Server-Sent Events (SSE) | [`HandleSSE`](/docs/core-concepts/server-sent-events) — typed writer, auto headers, typed TS client |
| Bidirectional WebSocket | [`HandleWS`](/docs/core-concepts/websockets) — typed send/receive, auto upgrade, typed TS client |
| SSE with custom framing | `HandleRaw` + `WithContentType("text/event-stream")` |
| File download | `HandleRaw` + `WithContentType("application/octet-stream")` |
| WebSocket upgrade | `HandleRaw` (no `WithContentType` needed) |
| Custom WebSocket framing | `HandleRaw` (no `WithContentType` needed) |
| Streaming response | `HandleRaw` with `Flusher` access |
| Proxy / passthrough | `HandleRaw` with `struct{}` input to preserve `r.Body` |
| JSON but custom content type | `Handle` + `WithContentType("application/vnd.api+json")` |
Loading
Loading