# 12. Networking & Web

## HTTP client
Fetch from the web and combine with `json_parse` to call APIs:
```stencil
let page = http_get("https://example.com");
print(len(page), "characters");

let body = http_post("https://httpbin.org/post", "hello=world");
print(body);
```
Both return the response body as a string, or `null` on failure.

## Web server — build a backend
`serve(port, handler)` starts an HTTP server. For every request it calls your
`handler` function with a request dict and uses the returned value as the response.
```stencil
func route(req) {
    let path = req["path"];        // also: req["method"], req["body"]
    if (path == "/") {
        return "<h1>Hello from STencil!</h1>";
    }
    elif (path == "/json") {
        return {
            "type": "application/json",
            "body": json_str({ "ok": true, "lang": "STencil" })
        };
    }
    else {
        return { "status": 404, "body": "Not found: " + path };
    }
}

serve(8080, route);   // blocks, serving forever
```
Run it, then open http://localhost:8080 in a browser.

### The request dict
| Key | Value |
|-----|-------|
| `method` | "GET", "POST", ... |
| `path` | the URL path, e.g. "/json" |
| `body` | request body as a string |

### The response
- Return a **string** → sent as the body with status 200, `text/html`.
- Return a **dict** with optional `status` (int), `body` (string), `type` (content-type).

### Reading the port from the environment (for hosting)
Hosts like Railway provide the port in `$PORT`:
```stencil
let port = env("PORT");
serve(int(port), route);
```

## Raw TCP sockets
Lower-level networking when HTTP isn't enough:
```stencil
let sock = tcp_connect("example.com", 80);   // returns an id, or -1 on failure
if (sock > 0) {
    tcp_send(sock, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
    let response = tcp_recv(sock);            // read available bytes (5s timeout)
    print(split(response, "\r\n")[0]);        // HTTP/1.1 200 OK
    tcp_close(sock);
}
```

Next: [Graphics & Games](13_graphics_and_games.md)
