For the complete Netlify documentation index, see llms.txt. Markdown versions of any documentation page are available by appending .md to its URL. Because your server is a long-lived process, it can hold connections open with visitors for as long as they need. Use WebSockets for two-way communication, such as chat or multiplayer, and Server-Sent Events or a streamed response to send updates as they happen, such as a model's output.
Builds a working version of this demo on your own site.
WebSockets
Section titled “WebSockets”How you accept WebSocket connections depends on what your entry file exports.
With a handler or a Fetchable
Section titled “With a handler or a Fetchable”Upgrade the request with upgradeWebSocket. You get back a browser-style WebSocket to talk to the client, and a Response to return from your handler:
import { upgradeWebSocket } from "@netlify/server";
export default async (req: Request) => { if (req.headers.get("upgrade") === "websocket") { const { socket, response } = upgradeWebSocket(req);
socket.onmessage = (event) => { socket.send(`Echo: ${event.data}`); };
return response; }
return new Response("Connect with a WebSocket client");};With a framework or a server
Section titled “With a framework or a server”Attach a WebSocket library such as ws or Socket.IO to your server, as you would anywhere else:
import express from "express";import { WebSocketServer } from "ws";
const app = express();const server = app.listen(process.env.PORT);const wss = new WebSocketServer({ server });
wss.on("connection", (ws) => { ws.on("message", (data) => ws.send(data));});An app you export directly, such as an Express app, can't accept WebSocket connections, and upgrade requests to it are answered with a 501. Start it with listen() instead, as in the example above.
Server-Sent Events
Section titled “Server-Sent Events”To send a stream of updates, respond with a text/event-stream body and keep writing to it. Netlify sends each event to the visitor as soon as your server writes it.
export default async (req: Request) => { const encoder = new TextEncoder(); let timer: ReturnType<typeof setInterval>;
const stream = new ReadableStream({ start(controller) { timer = setInterval(() => { controller.enqueue(encoder.encode(`data: ${new Date().toISOString()}\n\n`)); }, 1000); }, cancel() { clearInterval(timer); }, });
return new Response(stream, { headers: { "content-type": "text/event-stream" }, });};Any other streamed response works the same way, such as a model's output streamed as it's generated.
Reconnecting
Section titled “Reconnecting”WebSocket and Server-Sent Events connections both close when they reach a limit or when an instance stops, so make your clients reconnect. The browser's EventSource reconnects to a Server-Sent Events stream for you. A WebSocket doesn't, so reconnect when it closes:
function connect() { const socket = new WebSocket("wss://example.com/api/chat");
socket.addEventListener("close", (event) => { // 1012 means the server is restarting, so reconnect right away. setTimeout(connect, event.code === 1012 ? 0 : 1000); });
return socket;}The new connection reaches a running instance, which can be a new one if the previous instance stopped.
When an instance shuts down, its open connections can stay open for up to 90 seconds. Close them in a shutdown hook instead, so clients move to a new instance straight away. With upgradeWebSocket, getWebSockets returns every open connection:
import { getWebSockets, onShutdown } from "@netlify/server";
onShutdown(() => { for (const socket of getWebSockets()) { socket.close(1012, "Server restarting"); }});With a library such as ws, close the connections it keeps track of instead, such as wss.clients.
Connections and limits
Section titled “Connections and limits”The two kinds of connection count differently toward an instance's limits:
- A Server-Sent Events stream is a response in progress, so it takes up one of the requests an instance handles at once until it ends.
- A WebSocket connection doesn't. WebSockets have a separate limit of a couple of hundred per instance.
Either kind of open connection:
- Keeps the instance from stopping for inactivity.
- Is closed when it reaches the request duration limit.
- Is closed when the instance shuts down, for example because a newer deploy replaced it, after up to 90 seconds unless you close it sooner.
Each instance holds its own connections. If clients need to see the same state, such as the messages in a chat room, keep that state in Netlify Database or Netlify Blobs rather than only in an instance's memory, so it survives the instance being replaced.
Did you find this doc useful?
Your feedback helps us improve our docs.