Oxygen is a worker-based runtime for hosting [Hydrogen](/docs/storefronts/headless/hydrogen/getting-started) storefronts at the edge.
[Worker runtimes](https://workers.js.org/) are JavaScript-based HTTP servers that are designed for serverless or edge-computing contexts. Workers adapt web browser APIs for server-side applications, so you can use JavaScript-standard [Fetch](#fetch-api), [Streams](#streams-apis), [URL](#urls), [Cache-Control](#cache-api), and [Web Crypto](#web-crypto-api) APIs when you deploy to Oxygen.
## Runtime mirroring
Hydrogen projects include a local development server [as a dependency](/docs/storefronts/headless/hydrogen/fundamentals#packages-and-dependencies) by default. The local development server closely replicates Oxygen's production runtime, so you can be confident that functionality that works in development will also work in production. Both the Oxygen development and production servers are based on Cloudflare's open-source [`workerd`](https://blog.cloudflare.com/workerd-open-source-workers-runtime) project.
In addition to runtime parity, Hydrogen's bundled development server also returns the same headers sent by Oxygen in production. It also serves static assets, such as images, from a separate `localhost` domain. This more closely resembles how [Shopify's CDN](https://cdn.shopify.com) works in production, and can help [debug Hydrogen](/docs/storefronts/headless/hydrogen/debugging) cross-origin resource permission issues earlier in your development process.
To start the development server locally, run the Shopify CLI command `shopify hydrogen dev` in your Hydrogen project. Consult the [Shopify CLI command reference](/docs/api/shopify-cli/hydrogen) for a complete list of commands and development options.
> Note
> Hydrogen versions prior to v2024-01 used a Node.js-based development server by default, with lower compatibility. To continue using this version of the development server, pass the `--legacy-runtime` flag when you run the `dev` command.
## Worker runtime APIs
This reference provides a list of the functions that you can use with worker runtime APIs in Oxygen. These APIs are similar to the APIs that are provided by web browsers or Node.js projects.
## Cache API
Oxygen provides the [`Cache API`](https://developer.mozilla.org/en-US/docs/Web/API/Cache) that's also supported in major web browsers. The contents of the cache aren't accessible outside of the originating data center.
For developers who are familiar with Cloudflare's Cache API, Oxygen doesn't support the `caches.default` API.
You can create instances of the [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache) object by using the [caches.open](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/open) method:
### HTTP Headers
The cache API supports the following HTTP headers in the HTTP response that's passed to `put()`:
- `Cache-Control`: Refer to the Cloudflare documentation on [cache-control directives](https://developers.cloudflare.com/cache/about/cache-control#cache-control-directives). For a list of HTTP response codes and their TTL when cache-control directives aren't present, refer to [Edge TTL](https://developers.cloudflare.com/cache/how-to/create-page-rules/).
- `ETag`: Allows `cache.match()` to evaluate conditional requests that contain `If-None-Match`.
- `Expires`: A string that contains a date in the [RFC2616](https://www.rfc-editor.org/rfc/rfc2616) format, and specifies when the resource becomes invalid. You can use strings that are generated from an instance of `Date` being passed to the `.toUTCSring()` method.
- `Last-Modified`: Allows `cache.match()` to evaluate conditional requests with `If-Modified-Since`.
Unlike the cache API for Oxygen, the cache API in web browsers ignores HTTP headers on HTTP requests or responses.
HTTP responses that contain `Set-Cookie` headers aren't cached. To store an HTTP response that contains a `Set-Cookie` header, you need to use one of the following methods before you use `cache.put()`:
- Delete the HTTP header from the HTTP response.
- Add a `Cache-Control: private=Set-Cookie` HTTP header in the HTTP response.
### Cache methods
You can use the methods that are described in this section to interact with the cache API in Oxygen.
#### cache.put(request, response)
Adds a response to the cache.
`cache.put` doesn't support the `stale-while-revalidate` and `stale-if-error` directives.
##### cache.put parameters
- `request` (string or `Request` object): Used as the lookup key to the request. If `request` is a string, then `request` is used as the URL for a new `Request` object.
- `response` (`Response` object): A `Response` object that's stored as the value for the `request` key.
##### cache.put returns
Returns a promise that resolves to `undefined` when the cache stores the response.
##### cache.put errors
`cache.put` will throw an error if any of the following conditions are met:
- the request passed is a method other than GET.
- the response passed has a status of 206 Partial Content.
`cache.put` returns a 413 error if any of the following conditions are met:
- the `Cache-Control` value is set not to cache.
- the `Response` object is too large.
#### cache.match(request, options)
Checks for a matching response in the cache.
`cache.match` supports the following HTTP headers in the string or `Request` object that's passed as the `request` parameter:
- `Range`: Results in a 206 response if a matching response that contains a `Content-Length` HTTP header is found. Your Cloudflare cache respects range requests, even when an `Accept-Ranges` HTTP header is in the response.
- `If-Modified-Since`: Results in a 304 response if a matching response contains a `Last-Modified` HTTP header that contains a value that specifies a time after the time that was specified in the `If-Modified-Since` HTTP header.
- `If-None-Match`: Results in a 304 response if a matching response contains an `ETag` HTTP header with a value that matches the value that was specified in the `If-None-Match` HTTP header.
`cache.match` never sends a subrequest to the origin.
`cache.match` doesn't support the `stale-while-revalidate` and `stale-if-error` directives.
##### cache.match parameters
- `request` (string or `Request` object): Used as the lookup key to the request. If `request` is a string, then `request` is used as the URL for a new `Request` object.
- `options` (`ignoreMethod` option): When `ignoreMethod` evaluates to `true`, the request is interpreted as a `GET` request, regardless of the actual value of the request. The `ignoreSearch` or `ignoreVary` options aren't supported. If you need to use these options, then you can remove query strings or HTTP headers when you use [`cache.put`](##cache-put-request-response).
##### cache.match returns
Returns a promise that wraps the `Response` object that's keyed to the `Request` object. If a matching response isn't found, then the promise for `undefined` is returned.
##### cache.match errors
`cache.match` returns a 504 error when the content is stale.
#### cache.delete(request, options)
Deletes the `Response` object from the cache.
##### cache.delete parameters
- `request` (string or `Request` object): Used as the lookup key to the request. If `request` is a string, then `request` is used as the URL for a new `Request` object.
- `options`: (`ignoreMethod` option): When `ignoreMethod` evaluates to `true`, the request is interpreted as a `GET` request, regardless of the actual value of the request. The `ignoreSearch` or `ignoreVary` options aren't supported. If you need to use these options, then you can remove query strings or HTTP headers when you use [`cache.put`](##cache-put-request-response).
##### cache.delete returns
Returns a `Promise` for one of the following Boolean responses:
- `true`: The response was cached, and is now deleted.
- `false`: The response wasn't cached at the time of the `cache.delete` call.
## Encoding API
Oxygen supports the following Web APIs:
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/TextEncoder)
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder)
`TextEncoder` and `TextDecoder` only support UTF-8 encoding.
## Fetch API
Oxygen supports the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), which enables you to fetch resources over the network, and provides generic definitions of the `Request`, `Response`, `Headers`, and other primitives that are used in network requests.
### fetch method
Oxygen supports the top-level [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) method, which is used to fetch resources over the network. The `fetch` method returns a promise, which is fulfilled when the response is available. Asynchronous `fetch` calls aren't executed at the top level in a worker script. You can only make calls in the asynchronous request handler method of your worker.
## Headers
`Headers` is a globally-available constructor that represents HTTP headers in the [Fetch API](#fetch-api).
The Oxygen implementation of `Headers` is based on the [Headers API](https://developer.mozilla.org/en-US/docs/Web/API/Headers).
### Differences from the standard headers API
- The `Headers.getAll` method in the standard headers API is deprecated, but you should still use this method with the `Set-Cookie` HTTP header, because cookies often contain date strings, which can be difficult to parse. If you use `Headers.getAll` with other HTTP headers, then an error is thrown.
- The `Headers.get` method returns a `USVString` instead of a `ByteString`. Unlike a `ByteString`, which represents a sequence of bytes, a `USVString` represents a valid sequence of bytes that are convenient to process, and never contain surrogate code points or unpaired surrogate code units.
### Custom headers
Oxygen supports custom HTTP headers that contain meta information about the incoming request. These headers map to [Cloudflare's standard fields](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/standard-fields/#ipsrc).
You can access the following HTTP headers on incoming external instances of the `Request` object:
- `oxygen-buyer-ip`: Customer's IP address
- `oxygen-buyer-latitude`: Customer's geographical latitude
- `oxygen-buyer-longitude`: Customer's geographical longitude
- `oxygen-buyer-continent`: Customer's continent
- `oxygen-buyer-country`: Customer's country
- `oxygen-buyer-region`: Customer's region
- `oxygen-buyer-region-code`: Customer's region code
- `oxygen-buyer-city`: Customer's city
- `oxygen-buyer-timezone`: Customer's timezone
- `oxygen-buyer-postal-code`: Customer's postal code
- `oxygen-buyer-metro-code`: Customer's metro code (Designated Market Area)
- `oxygen-buyer-is-eu-country`: If the customer's country is in the EU (any non-empty value means yes)
For example, to bind the value of one of the custom HTTP headers to a `userCountry` constant, you can use the line of code below:
> Note: Header values can be undefined if their corresponding information isn't available.
### Request interface
The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface represents a resource request. All properties of an incoming `Request` object are read-only.
To modify a request, create a new instance of a `Request` object and pass the options to the `Request` object's constructor to modify.
Oxygen supports the following methods on instances of the `Request` object:
- [`clone`](https://developer.mozilla.org/en-US/docs/Web/API/Request/clone)
- [`arrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/API/Request/arrayBuffer)
- [`formData`](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData)
- [`json`](https://developer.mozilla.org/en-US/docs/Web/API/Request/json)
- [`text`](https://developer.mozilla.org/en-US/docs/Web/API/Request/text)
For example, to create an instance of a `Response` object, you can use the following code:
#### Response(body, init)
The [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) interface represents a response that's sent to the client.
##### Response Properties
- `body` ([SourceBuffer](https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer), [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData), [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream), [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams), or [USVString](https://developer.mozilla.org/en-US/docs/Web/API/USVString))
- `init` (Optional) ([Response](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#init) object)
- The following of `Response` properties are specific to Oxygen:
- `encodeBody`: Workers need to compress data according to the `content-encoding` HTTP header when transmitting data across the network. To serve data that's already compressed, set `encodeBody` to `manual`. By default, `encodeBody` is set to `auto`.
##### Response returns
An instance of a `Response` object.
##### Response methods
You can use the following methods on instances of `Response` objects.
- [`arrayBuffer()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer)
- [`formData()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/formData)
- [`json()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/json)
- [`text()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/text)
### Streams APIs
Oxygen provides APIs for accessing and processing streams of data, which are a subset of the [Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API).
Workers don't need to prepare a complete response body before they return an instance of a `Response` object. You can use the [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) interface to stream a response body after sending the HTTP status and line headers. When response bodies are larger than the memory limit of workers, you'll need to stream data.
To maintain the streaming behavior, only modify the response body by using the methods in the Streams APIs.
If your worker only forwards subrequest responses to the client without reading their body text, then the body handling is already optimized, and you won't benefit from using the Streams API.
For a basic stream usage example, refer to the following code:
#### ReadableStream
The `readable` property in [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) returns a [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). You can also construct a new `ReadableStream` by using the `new` keyword.
The implementation of `ReadableStream` that's provided by Oxygen supports the following methods:
##### pipeTo(destination, options)
Pipes the readable stream to a given writable stream destination and returns a promise that's fulfilled when the write operation succeeds. If the operation fails, then the instance of a promise is rejected.
###### pipeTo parameters
- `destination`: Refer to the [Cloudflare documentation on `destination`](https://developers.cloudflare.com/logs/get-started/enable-destinations).
- `options` Refer to the [Cloudflare documentation on `destination`](https://developers.cloudflare.com/logs/get-started/enable-destinations). The Oxygen implementation of `options` supports the following properties:
- `preventClose`: When `true`, closure of the source, `ReadableStream`, won't cause the destination, `WritableStream`, to be closed.
- `preventAbort`: When `true`, errors in the source, `ReadableStream`, won't abort the destination, `WritableStream`.
###### pipeTo returns
A promise that resolves when the piping process has completed, or rejects with the error from the source or any error that occurred while aborting the destination.
##### getReader(options)
Gets an instance of `ReadableStreamDefaultReader` and locks the `ReadableStream` to that reader instance.
For an example of how to create a `ReadableStreamBYOBReader`, refer to the following code:
###### getReader parameters
- `options` (key-value pair, where `mode` is the key): An object that specifies options. The only option that's supported is `mode`, which you can set to `'byob'`. the `'byob'` mode creates a `ReadableStreamBYOBReader`.
###### getReader returns
A [ReadableStreamDefaultReader](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader) or [ReadableStreamBYOBReader](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader) object instance, depending on the mode value.
#### ReadableStreamBYOBReader
A [ReadableStreamBYOBReader](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader)
enables you to read into a buffer that's provided by a developer. BYOB stands for "bring your own buffer", minimizing the amount of data that's copied in-memory.
An instance of `ReadableStreamBYOBReader` is similar to the `ReadableStreamDefaultReader` with the exception of the `read` method.
You can create an instance of a `ReadableStreamBYOBReader` by retrieving it from a `ReadableStream`.
For an example of how to retrieve a `ReadableStreamBYOBReader`, refer to the following code:
##### Methods
- `read(buffer)`: Returns a promise with the next available chunk of data read into a passed-in buffer. The promise doesn't resolve until at least `minBytes` have been read.
- `read`: Similar to `read(buffer)`, without a minimum number of bytes that should be read into the buffer. For example, if you allocate 1 MB buffer, then the kernel can fulfill the read with a single byte, whether an EOF follows or not. `read` usually fills only 1% of the provided buffer.
- `readAtLeast(minBytes, buffer)`: A non-standard extension to the [streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which enables users to specify the `minBytes` needs to be read into the buffer before resolving the read.
##### Returns
Returns a promise with the next available chunk of data that was read into a passed-in buffer.
#### ReadableStreamDefaultReader
A [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader)
is used when you want to read from a `ReadableStream`, rather than piping its output to a `WritableStream`.
A `ReadableStreamDefaultReader` is retrieved from a `ReadableStream`, rather than being created through its constructor.
For an example of how to retrieve a `ReadableStreamDefaultReader`, refer to the following code:
#### TransformStream
A [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) consists of a writable stream and a readable stream. When data is written to the writable stream, the data is available to be read from the readable stream.
Oxygen provides an identity transform stream, which forwards all chunks that were written to the writable stream to the readable stream without changing the chunks.
#### WritableStream
A [WritableStream](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) is the writable property of a `TransformStream`. A `WritableStream` in Oxygen can't be created by using the `WritableStream` constructor.
You'll want to write to a `WritableStream` by piping a `ReadableStream` to it.
For an example of how to pipe a `ReadableStream` to a `WritableStream`, refer to the following code:
To write to a `WritableStream` directly, you need to use its writer:
Refer to the [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter) documentation for more details.
#### WritableStreamDefaultWriter
Use a [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter)
when you want to write directly to a `WritableStream`, rather than piping data to it from a `ReadableStream`.
For an example of how to write directly to a `WritableStream`, refer to the code below:
## Web Crypto API
The [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) provides low-level functions for common cryptographic tasks.
Oxygen fully implements the Web Crypto API, with the exception of differences in the supported algorithms compared to the algorithms used in most browsers.
The Web Crypto API provides significant speed and security benefits compared to JavaScript implementations.
The Oxygen Web Crypto API is implemented through the [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto#Methods)
interface, accessible through the global `crypto.subtle` binding. The Oxygen Web Crypto API implementations works differently from [the Crypto API in Node.js](https://nodejs.org/api/crypto.html). Code that uses the Node.js implementation of the Web Crypto API must be changed to work with the implementation provided by Oxygen.
For common uses of the Web Crypto API, refer to the Cloudflare documentation on [signing requests](https://developers.cloudflare.com/workers/examples/signing-requests/).
For a detailed list of supported cryptographic algorithms, refer to the Cloudflare documentation on [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms).
## JavaScript objects and web APIs
Oxygen runs on the V8 JavaScript engine from Google Chrome. The runtime is frequently updated to match the latest stable release of Google Chrome. This enables you to use the latest JavaScript features without needing to transpile your code.
### Available JavaScript objects
All of the [standard built-in JavaScript objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference) are supported, except for the following APIs, for security reasons:
- `eval()`
- `new Function()`
The `Date.now()` method, and any time-related APIs, doesn't provide access to the exact timestamp. Instead, the current time is frozen and points to the timestamp of the last performed I/O operation.
### Available web APIs
The following APIs are globally available:
#### Base64
- [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob)
- [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa)
#### Timers
The following timers are only available inside of the worker's request handler, and can't be used in the global context:
- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval)
- [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval)
- [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
- [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout)
#### Events
- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
- [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
#### Aborting asynchronous operations
- [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
- [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
#### URLs
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern)