Fluxio
← Back to the blog

Stream AI Replies in Go with SSE, Step by Step

SSE & APIsEN

§1 Map the Go-to-browser streaming path

A streaming AI endpoint should deliver useful text before the model finishes its answer. In a Go service, the request path is straightforward: accept a chat request, open an upstream AI stream, and forward incremental text to the browser through server-sent events, or SSE.

Keep the upstream API key on the server. Your browser should call your own endpoint, such as /api/chat/stream, rather than contacting the AI provider directly. This gives your Go service control over authentication, spending limits, cancellation, and error handling.

§2 Configure an upstream streaming request

Fluxio fits this architecture when you want a streaming-first, pay-per-token AI relay behind one API key. Its OpenAI-compatible API base is https://api.fluxioapi.com/v1. For a direct HTTP integration, send your chat completion request to /chat/completions under that base.

Create the upstream request with http.NewRequestWithContext, using the incoming request context. Add your bearer token from an environment variable, set the content type to application/json, and include messages, a supported model identifier, and stream set to true. For example, you could select Claude Sonnet 5 after checking its exact API identifier in the service's model catalog.

Reuse an http.Client and transport rather than constructing a fresh transport for every request. Configure connection and response-header timeouts, but avoid a short whole-request timeout that would terminate healthy, long-running streams.

§3 Prepare the Go response for SSE

Before opening the upstream connection, confirm that your response writer supports flushing. With modern Go, http.NewResponseController provides flush support, including through wrappers that expose their underlying writer correctly. Make sure your middleware preserves that capability.

Validate the upstream HTTP status before committing the downstream streaming response. If authentication fails or the upstream returns a rate limit, your handler can still send an appropriate normal HTTP error.

Once the upstream succeeds, set Content-Type to text/event-stream and Cache-Control to no-cache. Where relevant, set X-Accel-Buffering to no to discourage reverse-proxy buffering. Flush the headers so the browser can recognize the stream without waiting for generated text.

§4 Decode upstream events and forward text deltas

Read the upstream body incrementally instead of calling io.ReadAll. An SSE event ends at a blank line; collect its data fields before decoding the payload. Do not assume that every network read contains one complete event or that every event fits on one line.

If you use bufio.Scanner, increase its buffer limit deliberately and handle scanner errors. A buffered reader is another option when you need explicit control over event size limits.

For OpenAI-compatible chat completion chunks, inspect the choices and forward available delta content. Ignore chunks that only announce a role, and handle the upstream completion marker separately. Streaming chunks are not guaranteed to correspond to individual tokens.

Expose a small downstream protocol with delta, done, and error events. JSON-encode each payload, write the event name and data fields, terminate with a blank line, and flush. JSON encoding keeps generated newlines from accidentally breaking SSE framing.

§5 Consume the stream with browser fetch

For a chat endpoint that accepts a JSON POST body, use fetch and read its response stream. The browser's native EventSource interface does not support arbitrary POST bodies. Decode bytes incrementally, preserve partial events between reads, and append text only after parsing a complete delta event.

Use an AbortController for the stop button. When the browser disconnects, the incoming Go request context is canceled; the upstream request should inherit that cancellation. Always close the upstream response body when the handler exits.

§6 Test buffering, failures, and streaming latency

  • – Verify that your reverse proxy and compression middleware do not hold small events until a buffer fills.
  • – Test client disconnects, upstream errors, malformed events, and streams that stop without a completion marker.
  • – Measure time to first text delta separately from total response duration.
  • – Apply concurrency limits and request budgets without logging API keys or sensitive conversation content.

After streaming headers are committed, report failures through your error event rather than attempting to change the HTTP status. Avoid automatic retries after visible output, because they can duplicate text and increase charges.

Sign up for Fluxio to build your first live AI stream.

Create a Fluxio account →

More from the current