HTTP
HTTP client and server APIs in Tish (feature flag: http).
Requires the http feature. Build tish with it for run: cargo run -p tishlang --features http -- run main.tish. For build: tish build main.tish -o app --feature http.
fetch(url, options?)
Perform an HTTP request. fetch returns a Promise that settles to a response object. Always await the result (or use .then) — there is no synchronous “already materialized” response.
import { fetch } from "http"
let res = await fetch("https://api.example.com/data")
console.log(res.status, res.ok)Parameters
- url (string): URL to fetch
- options (object, optional):
method:"GET","POST","PUT","DELETE","PATCH","HEAD"headers: Object of header name → valuebody: Request body string
Response object (client fetch)
status: Status code (number)ok: Boolean (true if 2xx)headers: Object of header name → valuebody: OpaqueReadableStream(not a string). Usebody.getReader()and a read loop, or consume the whole body withawait res.text()/await res.json()(see below).text: Function with no args → returns aPromise<string>that reads the entire body as UTF-8 text.json: Function with no args → returns aPromisethat reads the entire body andJSON.parses it.
Buffered JSON / text (small responses)
For typical JSON APIs, read the full body in one step:
import { fetch } from "http"
let res = await fetch("https://api.example.com/json")
if (res.ok) {
let data = await res.json()
console.log(data)
}
let res2 = await fetch("https://api.example.com/plain")
let txt = await res2.text()ReadableStream and getReader()
The response body is a Web-fetch–style readable stream:
- Call
let reader = res.body.getReader()(locks the body for streaming). - Loop:
let chunk = await reader.read(). - Each chunk is
{ done: boolean, value: number[] }:valueis the next slice of raw bytes (UTF-8 is not decoded for you; merge and decode as needed). - Stop when
doneis true.
Single-consumer rule: After getReader(), do not call await res.text() or await res.json() on the same response — the body can only be consumed one way. (Same idea as browsers: locked stream vs. fully buffered helpers.)
Streaming example (accumulate bytes / line-based protocols)
This pattern matches incremental SSE or other protocols where you decode UTF-8 and split frames yourself:
import { fetch } from "http"
let res = await fetch("https://example.com/stream", { method: "GET" })
if (!res.ok) {
console.log("error", res.status)
} else {
let reader = res.body.getReader()
let acc = []
while (true) {
let chunk = await reader.read()
if (chunk.done) {
break
}
let bytes = chunk.value
let i = 0
while (i < bytes.length) {
acc.push(bytes[i])
i = i + 1
}
}
// acc is number[] of UTF-8 bytes; decode to string in your app if needed
}The Tish runtime integration test fetch_readable_stream.rs (in the tish repo) exercises chunked bodies end-to-end.
fetchAll(requests)
fetchAll returns a Promise that settles to an array of response objects (same shape as fetch above). Use await fetchAll([...]).
Each request is a string URL or an object with url (and optional method, headers, body).
import { fetch, fetchAll } from "http"
let results = await fetchAll([
"https://api.example.com/a",
{ url: "https://api.example.com/b", method: "POST" },
])serve(port, handler)
Start an HTTP server. The handler receives a request object and returns a response object or string.
Request object
method: HTTP method (string)path: Path without query (string)url: Full URL (string)query: Query string (string)headers: Object of header name → valuebody: Request body (string on the server)
Response
Return an object:
status: Status code (number, default 200)body: Response body (string)headers: Object of header name → value (optional, e.g.{ contentType: "application/json" })
Or return a string (200, no headers).
Server-side body as string is intentional and not the same shape as client fetch res.body (opaque byte stream).
Example
fn handleRequest(req) {
if (req.path === "/health") {
return { status: 200, body: "OK" }
}
if (req.path === "/") {
return {
status: 200,
headers: { contentType: "application/json" },
body: JSON.stringify({ message: "Hello" }),
}
}
return { status: 404, body: "Not Found" }
}
let port = "PORT" in process.env ? parseInt(process.env.PORT) : 8080
serve(port, handleRequest)Promise and timers
With the http feature, Tish also provides:
- Promise (ECMA-262 §27.2):
Promise(executor),.then,.catch,.finally,Promise.resolve,Promise.reject,Promise.all,Promise.race - setTimeout(callback, delayMs, ...args) — Schedule callback to run after delay (non-blocking; returns immediately)
- setInterval(callback, delayMs, ...args) — Schedule callback to run repeatedly (non-blocking)
- clearTimeout(id), clearInterval(id) — Cancel timers
Use async fn / await for HTTP and other Promise-based APIs.