Message framing

Frame JSON messages

Send newline-delimited JSON, validate the parsed shape, and return errors without crashing the server.

10 minute lesson

~~~

Lines and length prefixes solve framing but leave the payload unstructured. JSON gives values structure — objects, strings, numbers — but does not frame a TCP stream. You need both.

Newline-delimited JSON combines them: one compact JSON document per line. It works because compact JSON never contains a literal newline — inside strings, JSON escapes them as \n. Framing comes from the newline, structure comes from JSON, and the line buffer you already built keeps working unchanged.

Only the line handler changes. Handle one parsed message:

function handleLine(line) {
  try {
    const message = JSON.parse(line)
    if (message.type !== 'echo' || typeof message.text !== 'string') {
      return socket.write('{"error":"invalid message"}\n')
    }
    socket.write(JSON.stringify({ text: message.text }) + '\n')
  } catch {
    socket.write('{"error":"invalid json"}\n')
  }
}

There are two distinct failure layers here. The catch handles bytes that aren’t JSON at all. The if handles valid JSON with the wrong shape — a missing type, a numeric text. Both get a bounded, structured error reply, and the connection stays open.

The try is load-bearing. JSON.parse() throws on malformed input, and one bad line from one client must never crash a server with a hundred others connected. This is the single most common way naive TCP servers die.

Test all three cases

Send valid JSON, malformed JSON, and a valid object with the wrong shape:

nc 127.0.0.1 4000
{"type":"echo","text":"hi"}
{"text":"hi"}
{"type":"echo"}
{"error":"invalid message"}
not json at all
{"error":"invalid json"}

The first line you type gets echoed back as {"text":"hi"}. The wrong-shape object and the garbage line each get exactly one error reply. Every case should receive one bounded reply — never silence, never a crash, never two replies for one message.

Machine-readable errors pay off quickly: a client can branch on the error field the same way it branched on 400 in the text protocol.

Parsing proves syntax only. A message can be perfect JSON, the right shape, and still ask for something this client isn’t allowed to do. Validate types, lengths, and authorization before acting.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →