Skip to content

API Surface

The package exposes browser APIs under the WebAPI namespace. In normal application code, add the package to rescript.json:

{
"dependencies": ["@rescript/webapi"]
}

Use WebAPI.DOM.window for the browser window. Interface-specific methods live on public modules such as WebAPI.Window, WebAPI.Location, WebAPI.Document, WebAPI.Element, WebAPI.Request, and WebAPI.Response.

let location = WebAPI.DOM.window->WebAPI.Window.location
let href = location.href
location->WebAPI.Location.reload

The public API is flat under WebAPI. Use owner modules directly rather than generated helper modules.

let req: WebAPI.Request.t = WebAPI.Request.fromURL("https://example.com")
let headers = WebAPI.Headers.make()
let document = WebAPI.DOM.window->WebAPI.Window.document
let element = document->WebAPI.Document.createElement("button")

Generated implementation modules such as DomTypes, FetchTypes, EventTypes, and UiEventsTypes are internal. If you need to name a public type, use the public interface module’s t type when it has one, or use a dedicated public type module. Otherwise, let the value type be inferred from constructors and accessors.

Feature names select related bindings and their transitive dependencies. They do not add another namespace segment. For example, selecting WebAPI.Fetch enables flat modules such as WebAPI.Fetch, WebAPI.Request, WebAPI.Response, and WebAPI.Headers.

{
"dependencies": [
{
"name": "@rescript/webapi",
"features": ["WebAPI.Fetch", "WebAPI.HTML"]
}
]
}

The supported feature bundles are:

WebAPI.DOM WebAPI.Event WebAPI.DOMPlatform
WebAPI.DOMNodes WebAPI.File WebAPI.HTML
WebAPI.Window WebAPI.CSSOM WebAPI.CSSFontLoading
WebAPI.Geometry WebAPI.SVG WebAPI.Animation
WebAPI.Device WebAPI.Navigator WebAPI.Canvas
WebAPI.URL WebAPI.Fetch WebAPI.UIEvents
WebAPI.Observers WebAPI.Media WebAPI.WebAudio
WebAPI.Storage WebAPI.Messaging WebAPI.Workers
WebAPI.Crypto WebAPI.Performance WebAPI.ViewTransitions

The package owns each bundle as one internal source folder. Those internal folder feature names are an implementation detail; consumers should use only the qualified names above.

Use WebAPI.Fetch.fetch for string URLs and WebAPI.Fetch.fetchWithRequest when you already have a WebAPI.Request.t.

let response = await WebAPI.Fetch.fetch("https://example.com/api/users")
if response.ok {
let body = await response->WebAPI.Response.json
Console.log(body)
} else {
Console.log("Request failed")
Console.log(response.status)
}

For JSON-style POST requests, construct headers with HeadersInit and the body with BodyInit.

let response = await WebAPI.Fetch.fetch(
"https://example.com/api/users",
~init={
method: "POST",
headers: WebAPI.HeadersInit.fromDict(
dict{
"Accept": "application/json",
"Content-Type": "application/json",
},
),
body: WebAPI.BodyInit.fromString(`{"name":"ReScript"}`),
},
)
let text = await response->WebAPI.Response.text

BodyInit has constructors for the common JavaScript body shapes.

let params = WebAPI.URLSearchParams.fromString("q=rescript")
let formData = WebAPI.FormData.make()
let blob = WebAPI.Blob.make(~blobParts=[])
let stringBody = WebAPI.BodyInit.fromString("plain text")
let paramsBody = WebAPI.BodyInit.fromURLSearchParams(params)
let formDataBody = WebAPI.BodyInit.fromFormData(formData)
let blobBody = WebAPI.BodyInit.fromBlob(blob)

Use WebAPI.HeadersInit when a fetch, request, or response initializer expects the JavaScript HeadersInit shape. Use WebAPI.Headers when you want a mutable Headers object with methods.

let initHeaders = WebAPI.HeadersInit.fromDict(
dict{
"Accept": "application/json",
"X-Client": "web",
},
)
let response = await WebAPI.Fetch.fetch(
"https://example.com/api/users",
~init={headers: initHeaders},
)

Build and edit a Headers object directly when you need to append, set, inspect, or pass headers around.

let headers = WebAPI.Headers.fromDict(dict{"Accept": "application/json"})
headers->WebAPI.Headers.set(~name="X-Request-ID", ~value="abc-123")
headers->WebAPI.Headers.append(~name="X-Trace", ~value="enabled")
switch headers->WebAPI.Headers.get("Accept") {
| Null.Value(value) => Console.log(value)
| Null => Console.log("missing")
}
let hasTrace = headers->WebAPI.Headers.has("X-Trace")

Headers can also be created from key-value arrays or copied from another Headers object.

let fromPairs = WebAPI.Headers.fromKeyValueArray([
("Accept", "application/json"),
("X-Client", "web"),
])
let clonedHeaders = WebAPI.Headers.fromHeaders(fromPairs)
let initFromHeaders = WebAPI.HeadersInit.fromHeaders(clonedHeaders)

Response headers are normal Headers values.

let contentType = response.headers->WebAPI.Headers.get("Content-Type")
switch contentType {
| Null.Value(value) => Console.log(value)
| Null => Console.log("unknown content type")
}

Create requests from URLs, then pass them to fetch or clone/read them with request methods.

let req = WebAPI.Request.fromURL(
"https://example.com/form",
~init={
method: "POST",
headers: WebAPI.HeadersInit.fromDict(
dict{"Content-Type": "application/x-www-form-urlencoded"},
),
body: WebAPI.BodyInit.fromString("name=ReScript"),
},
)
let response = await WebAPI.Fetch.fetchWithRequest(req)
let cloned: WebAPI.Request.t = req->WebAPI.Request.clone
let bodyText = await cloned->WebAPI.Request.text

Use fromRequest to copy an existing request and override initializer fields.

let authenticatedHeaders = WebAPI.HeadersInit.fromDict(
dict{
"Accept": "application/json",
"Authorization": "Bearer token",
},
)
let authenticatedReq = WebAPI.Request.fromRequest(
req,
~init={headers: authenticatedHeaders},
)
let authenticatedResponse = await WebAPI.Fetch.fetchWithRequest(authenticatedReq)

Request body readers mirror the browser API and return promises.

let requestText = await req->WebAPI.Request.text
let requestJson = await req->WebAPI.Request.json
let requestBlob = await req->WebAPI.Request.blob

Construct responses with the body constructor that matches the body value.

let created = WebAPI.Response.fromString(
"created",
~init={
status: 201,
statusText: "Created",
headers: WebAPI.HeadersInit.fromDict(dict{"Content-Type": "text/plain"}),
},
)
let empty = WebAPI.Response.fromNull(~init={status: 204})

Responses expose record fields for status metadata and methods for cloning or reading the body.

if created.ok {
let text = await created->WebAPI.Response.text
Console.log(text)
} else {
Console.log(created.statusText)
}
let copy = created->WebAPI.Response.clone
let copyText = await copy->WebAPI.Response.text

Use the static helpers for browser response factories.

let failed = WebAPI.Response.error()
let redirect = WebAPI.Response.redirect(~url="/login", ~status=302)

DOM values are operated on through public interface modules.

let document = WebAPI.DOM.window->WebAPI.Window.document
let maybeButton = document
->WebAPI.Document.querySelector("button")
->Null.toOption
switch maybeButton {
| Some(button) =>
switch button->WebAPI.Element.getAttribute("data-user-id") {
| Null.Value(id) => Console.log(id)
| Null => Console.log("anonymous")
}
| None => Console.log("button not found")
}

Use conversion helpers when moving between related DOM interface types.

let element = document->WebAPI.Document.createElement("div")
let node = element->WebAPI.Element.asNode

Event libraries and browser APIs sometimes return a type that is broader than the value present at runtime. A React form event target, for example, does not tell ReScript whether the target is an input, select, or another element. Calling an input-specific API then requires narrowing that value.

Use classify instead of repeating an instanceof binding and an unchecked Obj.magic cast. The classifier performs the runtime check and returns the correctly typed value as an option.

let readInputValue = target =>
switch target->WebAPI.HTMLInputElement.classify {
| Some(input) => Some(input.value)
| None => None
}

The classifier parameter is polymorphic, so a library-owned target can be passed directly without an identity cast to a WebAPI type first. Element.classify, Document.classify, HTMLElement.classify, HTMLInputElement.classify, HTMLImageElement.classify, and HTMLCanvasElement.classify follow the same contract. Their matching isInstanceOf functions are available when only a boolean check is needed.

Classification uses the corresponding constructor from globalThis. It returns None rather than raising when that constructor is unavailable in a server or worker environment. Like JavaScript instanceof, it only recognizes values created in the current realm; values from another window or iframe may not match. Enable the WebAPI.HTML feature for HTML-specific classifiers and WebAPI.Canvas for HTMLCanvasElement.classify.

Window.visualViewport returns a nullable WebAPI.VisualViewport.t.

let maybeViewport = WebAPI.DOM.window
->WebAPI.Window.visualViewport
->Null.toOption
switch maybeViewport {
| Some(viewport) =>
Console.log(Float.toString(viewport.width))
Console.log(Float.toString(viewport.scale))
| None => Console.log("visual viewport unavailable")
}

URL helpers expose constructors and static functions on the same public module.

let url = WebAPI.URL.make(~url="/docs", ~base="https://rescript-lang.org")
let canParse = WebAPI.URL.canParse(~url="/docs", ~base="https://rescript-lang.org")
let json = url->WebAPI.URL.toJSON

URLSearchParams values can be used directly as fetch request bodies through BodyInit.

let params = WebAPI.URLSearchParams.fromString("search=rescript")
let response = await WebAPI.Fetch.fetch(
"https://example.com/search",
~init={
method: "POST",
body: WebAPI.BodyInit.fromURLSearchParams(params),
},
)

Canvas constructors use make and semantic from* names.

let path = WebAPI.Path2D.make()
path->WebAPI.Path2D.moveTo(~x=0., ~y=0.)
path->WebAPI.Path2D.lineTo(~x=100., ~y=100.)
let copiedPath = WebAPI.Path2D.fromPath2D(path)
let svgPath = WebAPI.Path2D.fromString("M0 0 L10 10")

Generic Web API types stay typed through their public owner modules.

let stream: WebAPI.ReadableStream.t<string> = WebAPI.ReadableStream.make()
let reader = stream->WebAPI.ReadableStream.getReader