# Agents URL: https://developers.cloudflare.com/workers-ai/agents/ import { LinkButton } from "~/components"
Build AI assistants that can perform complex tasks on behalf of your users using Cloudflare Workers AI and Agents.
Where `json_schema` must be a valid [JSON Schema](https://json-schema.org/) declaration.
## JSON Mode example
When using JSON Format, pass the schema as in the example below as part of the request you send to the LLM.
The LLM will follow the schema, and return a response such as below:
As you can see, the model is complying with the JSON schema definition in the request and responding with a validated JSON object.
## Supported Models
This is the list of models that now support JSON Mode:
- [@cf/meta/llama-3.1-8b-instruct-fast](/workers-ai/models/llama-3.1-8b-instruct-fast/)
- [@cf/meta/llama-3.1-70b-instruct](/workers-ai/models/llama-3.1-70b-instruct/)
- [@cf/meta/llama-3.3-70b-instruct-fp8-fast](/workers-ai/models/llama-3.3-70b-instruct-fp8-fast/)
- [@cf/meta/llama-3-8b-instruct](/workers-ai/models/llama-3-8b-instruct/)
- [@cf/meta/llama-3.1-8b-instruct](/workers-ai/models/llama-3.1-8b-instruct/)
- [@cf/meta/llama-3.2-11b-vision-instruct](/workers-ai/models/llama-3.2-11b-vision-instruct/)
- [@hf/nousresearch/hermes-2-pro-mistral-7b](/workers-ai/models/hermes-2-pro-mistral-7b/)
- [@hf/thebloke/deepseek-coder-6.7b-instruct-awq](/workers-ai/models/deepseek-coder-6.7b-instruct-awq/)
- [@cf/deepseek-ai/deepseek-r1-distill-qwen-32b](/workers-ai/models/deepseek-r1-distill-qwen-32b/)
We will continue extending this list to keep up with new, and requested models.
Note that Workers AI can't guarantee that the model responds according to the requested JSON Schema. Depending on the complexity of the task and adequacy of the JSON Schema, the model may not be able to satisfy the request in extreme situations. If that's the case, then an error `JSON Mode couldn't be met` is returned and must be handled.
JSON Mode currently doesn't support streaming.
---
# Features
URL: https://developers.cloudflare.com/workers-ai/features/
import { DirectoryListing } from "~/components";
documents
: results
:
Here's a better example of a chat session using multiple iterations between the user and the assistant.
Note that different LLMs are trained with different templates for different use cases. While Workers AI tries its best to abstract the specifics of each LLM template from the developer through a unified API, you should always refer to the model documentation for details (we provide links in the table above.) For example, instruct models like Codellama are fine-tuned to respond to a user-provided instruction, while chat models expect fragments of dialogs as input.
### Unscoped Prompts
You can use unscoped prompts to send a single question to the model without worrying about providing any context. Workers AI will automatically convert your `prompt` input to a reasonable default scoped prompt internally so that you get the best possible prediction.
You can also use unscoped prompts to construct the model chat template manually. In this case, you can use the raw parameter. Here's an input example of a [Mistral](https://docs.mistral.ai/models/#chat-template) chat template prompt:
---
# Demos and architectures
URL: https://developers.cloudflare.com/workers-ai/guides/demos-architectures/
import {
ExternalResources,
GlossaryTooltip,
ResourcesBySelector,
} from "~/components";
Workers AI can be used to build dynamic and performant services. The following demo applications and reference architectures showcase how to use Workers AI optimally within your architecture.
## Demos
Explore the following
The prompt above adopts several best practices, including:
* Using `Waiting
Waiting
Waiting
`;
async function handleRequest(request) {
const url = new URL(request.url);
let apiUrl = url.searchParams.get("apiurl");
if (apiUrl == null) {
apiUrl = API_URL;
}
// Rewrite request to point to API URL. This also makes the request mutable
// so you can add the correct Origin header to make the API server think
// that this request is not cross-site.
request = new Request(apiUrl, request);
request.headers.set("Origin", new URL(apiUrl).origin);
let response = await fetch(request);
// Recreate the response so you can modify the headers
response = new Response(response.body, response);
// Set CORS headers
response.headers.set("Access-Control-Allow-Origin", url.origin);
// Append to/Add Vary header so browser will cache response correctly
response.headers.append("Vary", "Origin");
return response;
}
async function handleOptions(request) {
if (
request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null
) {
// Handle CORS preflight requests.
return new Response(null, {
headers: {
...corsHeaders,
"Access-Control-Allow-Headers": request.headers.get(
"Access-Control-Request-Headers",
),
},
});
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS",
},
});
}
}
const url = new URL(request.url);
if (url.pathname.startsWith(PROXY_ENDPOINT)) {
if (request.method === "OPTIONS") {
// Handle CORS preflight requests
return handleOptions(request);
} else if (
request.method === "GET" ||
request.method === "HEAD" ||
request.method === "POST"
) {
// Handle requests to the API server
return handleRequest(request);
} else {
return new Response(null, {
status: 405,
statusText: "Method Not Allowed",
});
}
} else {
return rawHtmlResponse(DEMO_PAGE);
}
},
};
```
Waiting
Waiting
Waiting
`;
async function handleRequest(request) {
const url = new URL(request.url);
let apiUrl = url.searchParams.get("apiurl");
if (apiUrl == null) {
apiUrl = API_URL;
}
// Rewrite request to point to API URL. This also makes the request mutable
// so you can add the correct Origin header to make the API server think
// that this request is not cross-site.
request = new Request(apiUrl, request);
request.headers.set("Origin", new URL(apiUrl).origin);
let response = await fetch(request);
// Recreate the response so you can modify the headers
response = new Response(response.body, response);
// Set CORS headers
response.headers.set("Access-Control-Allow-Origin", url.origin);
// Append to/Add Vary header so browser will cache response correctly
response.headers.append("Vary", "Origin");
return response;
}
async function handleOptions(request) {
if (
request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null
) {
// Handle CORS preflight requests.
return new Response(null, {
headers: {
...corsHeaders,
"Access-Control-Allow-Headers": request.headers.get(
"Access-Control-Request-Headers",
),
},
});
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS",
},
});
}
}
const url = new URL(request.url);
if (url.pathname.startsWith(PROXY_ENDPOINT)) {
if (request.method === "OPTIONS") {
// Handle CORS preflight requests
return handleOptions(request);
} else if (
request.method === "GET" ||
request.method === "HEAD" ||
request.method === "POST"
) {
// Handle requests to the API server
return handleRequest(request);
} else {
return new Response(null, {
status: 405,
statusText: "Method Not Allowed",
});
}
} else {
return rawHtmlResponse(DEMO_PAGE);
}
},
} satisfies ExportedHandler;
```
Waiting
Waiting
Waiting
`;
return c.html(DEMO_PAGE);
});
// CORS proxy routes
app.on(["GET", "HEAD", "POST", "OPTIONS"], PROXY_ENDPOINT + "*", async (c) => {
const url = new URL(c.req.url);
// Handle OPTIONS preflight requests
if (c.req.method === "OPTIONS") {
const origin = c.req.header("Origin");
const requestMethod = c.req.header("Access-Control-Request-Method");
const requestHeaders = c.req.header("Access-Control-Request-Headers");
if (origin && requestMethod && requestHeaders) {
// Handle CORS preflight requests
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
"Access-Control-Max-Age": "86400",
"Access-Control-Allow-Headers": requestHeaders,
},
});
} else {
// Handle standard OPTIONS request
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS",
},
});
}
}
// Handle actual requests
let apiUrl = url.searchParams.get("apiurl") || API_URL;
// Rewrite request to point to API URL
const modifiedRequest = new Request(apiUrl, c.req.raw);
modifiedRequest.headers.set("Origin", new URL(apiUrl).origin);
let response = await fetch(modifiedRequest);
// Recreate the response so we can modify the headers
response = new Response(response.body, response);
// Set CORS headers
response.headers.set("Access-Control-Allow-Origin", url.origin);
// Append to/Add Vary header so browser will cache response correctly
response.headers.append("Vary", "Origin");
return response;
});
// Handle method not allowed for proxy endpoint
app.all(PROXY_ENDPOINT + "*", (c) => {
return new Response(null, {
status: 405,
statusText: "Method Not Allowed",
});
});
export default app;
```
Waiting
Waiting
Waiting
'''
async def handle_request(request):
url = URL.new(request.url)
api_url2 = url.searchParams["apiurl"]
if not api_url2:
api_url2 = api_url
request = Request.new(api_url2, request)
request.headers["Origin"] = (URL.new(api_url2)).origin
print(request.headers)
response = await fetch(request)
response = Response.new(response.body, response)
response.headers["Access-Control-Allow-Origin"] = url.origin
response.headers["Vary"] = "Origin"
return response
async def handle_options(request):
if "Origin" in request.headers and "Access-Control-Request-Method" in request.headers and "Access-Control-Request-Headers" in request.headers:
return Response.new(None, headers=to_js({
**cors_headers,
"Access-Control-Allow-Headers": request.headers["Access-Control-Request-Headers"]
}))
return Response.new(None, headers=to_js({"Allow": "GET, HEAD, POST, OPTIONS"}))
url = URL.new(request.url)
if url.pathname.startswith(proxy_endpoint):
if request.method == "OPTIONS":
return handle_options(request)
if request.method in ("GET", "HEAD", "POST"):
return handle_request(request)
return Response.new(None, status=405, statusText="Method Not Allowed")
return raw_html_response(demo_page)
```
Waiting
Waiting
Waiting
"#
);
if req.url()?.path().starts_with(proxy_endpoint) {
match req.method() {
Method::Options => return handle_options(req, &cors_headers),
Method::Get | Method::Head | Method::Post => return handle_request(req, api_url).await,
_ => return Response::error("Method Not Allowed", 405),
}
}
raw_html_response(&demo_page)
}
```
This is a demo using Workers geolocation data.
`; html_content += `You are located at: ${latitude},${longitude}.`; html_content += `Based off sensor data from ${content.data.city.name}:
`; html_content += `The AQI level is: ${content.data.aqi}.
`; html_content += `The N02 level is: ${content.data.iaqi.no2?.v}.
`; html_content += `The O3 level is: ${content.data.iaqi.o3?.v}.
`; html_content += `The temperature is: ${content.data.iaqi.t?.v}°C.
`; let html = `This is a demo using Workers geolocation data.
`; html_content += `You are located at: ${latitude},${longitude}.`; html_content += `Based off sensor data from ${content.data.city.name}:
`; html_content += `The AQI level is: ${content.data.aqi}.
`; html_content += `The N02 level is: ${content.data.iaqi.no2?.v}.
`; html_content += `The O3 level is: ${content.data.iaqi.o3?.v}.
`; html_content += `The temperature is: ${content.data.iaqi.t?.v}°C.
`; let html = `This is a demo using Workers geolocation data.
You are located at: ${latitude},${longitude}.
Based off sensor data from ${content.data.city.name}:
The AQI level is: ${content.data.aqi}.
The N02 level is: ${content.data.iaqi.no2?.v}.
The O3 level is: ${content.data.iaqi.o3?.v}.
The temperature is: ${content.data.iaqi.t?.v}°C.
`; // Complete HTML document const htmlDocument = html`This is a demo using Workers geolocation data.
" html_content += f"You are located at: {latitude},{longitude}." html_content += f"Based off sensor data from {content['data']['city']['name']}:
" html_content += f"The AQI level is: {content['data']['aqi']}.
" html_content += f"The N02 level is: {content['data']['iaqi']['no2']['v']}.
" html_content += f"The O3 level is: {content['data']['iaqi']['o3']['v']}.
" html_content += f"The temperature is: {content['data']['iaqi']['t']['v']}°C.
" html = f"""" + timezone + "
" + timezone + "
${timezone}
Colo: " + request.cf.colo + "
"; html_content += "Country: " + request.cf.country + "
"; html_content += "City: " + request.cf.city + "
"; html_content += "Continent: " + request.cf.continent + "
"; html_content += "Latitude: " + request.cf.latitude + "
"; html_content += "Longitude: " + request.cf.longitude + "
"; html_content += "PostalCode: " + request.cf.postalCode + "
"; html_content += "MetroCode: " + request.cf.metroCode + "
"; html_content += "Region: " + request.cf.region + "
"; html_content += "RegionCode: " + request.cf.regionCode + "
"; html_content += "Timezone: " + request.cf.timezone + "
"; let html = `You now have access to geolocation data about where your user is visiting from.
${html_content} `; return new Response(html, { headers: { "content-type": "text/html;charset=UTF-8", }, }); }, }; ```Colo: " + request.cf.colo + "
"; html_content += "Country: " + request.cf.country + "
"; html_content += "City: " + request.cf.city + "
"; html_content += "Continent: " + request.cf.continent + "
"; html_content += "Latitude: " + request.cf.latitude + "
"; html_content += "Longitude: " + request.cf.longitude + "
"; html_content += "PostalCode: " + request.cf.postalCode + "
"; html_content += "MetroCode: " + request.cf.metroCode + "
"; html_content += "Region: " + request.cf.region + "
"; html_content += "RegionCode: " + request.cf.regionCode + "
"; html_content += "Timezone: " + request.cf.timezone + "
"; let html = `You now have access to geolocation data about where your user is visiting from.
${html_content} `; return new Response(html, { headers: { "content-type": "text/html;charset=UTF-8", }, }); }, } satisfies ExportedHandler; ```Colo: " + request.cf.colo + "
" html_content += "Country: " + request.cf.country + "
" html_content += "City: " + request.cf.city + "
" html_content += "Continent: " + request.cf.continent + "
" html_content += "Latitude: " + request.cf.latitude + "
" html_content += "Longitude: " + request.cf.longitude + "
" html_content += "PostalCode: " + request.cf.postalCode + "
" html_content += "Region: " + request.cf.region + "
" html_content += "RegionCode: " + request.cf.regionCode + "
" html_content += "Timezone: " + request.cf.timezone + "
" html = f"""You now have access to geolocation data about where your user is visiting from.
{html_content} """ headers = {"content-type": "text/html;charset=UTF-8"} return Response(html, headers=headers) ```Colo: ${request.cf.colo}
Country: ${request.cf.country}
City: ${request.cf.city}
Continent: ${request.cf.continent}
Latitude: ${request.cf.latitude}
Longitude: ${request.cf.longitude}
PostalCode: ${request.cf.postalCode}
MetroCode: ${request.cf.metroCode}
Region: ${request.cf.region}
RegionCode: ${request.cf.regionCode}
Timezone: ${request.cf.timezone}
`; // Compose the full HTML const htmlContent = html`You now have access to geolocation data about where your user is visiting from.
${html_content} `; // Return the HTML response return c.html(htmlContent); }); export default app; ```This markup was generated by a Cloudflare Worker.
`; return new Response(html, { headers: { "content-type": "text/html;charset=UTF-8", }, }); }, } satisfies ExportedHandler; ```This markup was generated by a Cloudflare Worker.
""" headers = {"content-type": "text/html;charset=UTF-8"} return Response(html, headers=headers) ```This markup was generated by a Cloudflare Worker.
"#; Response::from_html(html) } ```This markup was generated by a Cloudflare Worker with Hono.
`; return c.html(doc); }); export default app; ```https://
example.com/hello
fetch
request to https://
notexample.com/hello
.") --> B(Is notexample.com
https://
notexample.com/
https://
notexample.com/hello
example.com
zone.)
C -- Yes --> E(Do you own notexample.com
?)
C -- No --> F(Purge https://
notexample.com/hello
example.com
zone.)
E -- Yes --> G(Purge https://
notexample.com/hello
notexample.com
zone.)
E -- No --> H(Sorry, you can not purge the asset. notexample.com
can purge it.)
```
### Purge assets stored with the Cache API
Assets stored in the cache through [Cache API](/workers/runtime-apis/cache/) operations can be purged in a couple of ways:
- Call `cache.delete` within a Worker to invalidate the cache for the asset with a matching request variable.
- Assets purged in this way are only purged locally to the data center the Worker runtime was executed.
- To purge an asset globally, you must use the standard cache purge options. Based on cache API implementation, not all cache purge endpoints function for purging assets stored by the Cache API.
- All assets on a zone can be purged by using the [Purge Everything](/cache/how-to/purge-cache/purge-everything/) cache operation. This purge will remove all assets associated with a Cloudflare zone from cache in all data centers regardless of the method set.
- [Cache Tags](/cache/how-to/purge-cache/purge-by-tags/#add-cache-tag-http-response-headers) can be added to requests dynamically in a Worker by calling `response.headers.append()` and appending `Cache-Tag` values dynamically to that request. Once set, those tags can be used to selectively purge assets from cache without invalidating all cached assets on a zone.
- Currently, it is not possible to purge a URL stored through Cache API that uses a custom cache key set by a Worker. Instead, use a [custom key created via Cache Rules](/cache/how-to/cache-rules/settings/#cache-key). Alternatively, purge your assets using purge everything, purge by tag, purge by host or purge by prefix.
## Edge versus browser caching
The browser cache is controlled through the `Cache-Control` header sent in the response to the client (the `Response` instance return from the handler). Workers can customize browser cache behavior by setting this header on the response.
Other means to control Cloudflare’s cache that are not mentioned in this documentation include: Page Rules and Cloudflare cache settings. Refer to the [How to customize Cloudflare’s cache](/cache/concepts/customize-cache/) if you wish to avoid writing JavaScript with still some granularity of control.
:::note[What should I use: the Cache API or fetch for caching objects on Cloudflare?]
For requests where Workers are behaving as middleware (that is, Workers are sending a subrequest via `fetch`) it is recommended to use `fetch`. This is because preexisting settings are in place that optimize caching while preventing unintended dynamic caching. For projects where there is no backend (that is, the entire project is on Workers as in [Workers Sites](/workers/configuration/sites/start-from-scratch)) the Cache API is the only option to customize caching.
The asset will be cached under the hostname specified within the Worker's subrequest — not the Worker's own hostname. Therefore, in order to purge the cached asset, the purge will have to be performed for the hostname included in the Worker subrequest.
:::
### `fetch`
In the context of Workers, a [`fetch`](/workers/runtime-apis/fetch/) provided by the runtime communicates with the Cloudflare cache. First, `fetch` checks to see if the URL matches a different zone. If it does, it reads through that zone’s cache (or Worker). Otherwise, it reads through its own zone’s cache, even if the URL is for a non-Cloudflare site. Cache settings on `fetch` automatically apply caching rules based on your Cloudflare settings. `fetch` does not allow you to modify or inspect objects before they reach the cache, but does allow you to modify how it will cache.
When a response fills the cache, the response header contains `CF-Cache-Status: HIT`. You can tell an object is attempting to cache if one sees the `CF-Cache-Status` at all.
This [template](/workers/examples/cache-using-fetch/) shows ways to customize Cloudflare cache behavior on a given request using fetch.
### Cache API
The [Cache API](/workers/runtime-apis/cache/) can be thought of as an ephemeral key-value store, whereby the `Request` object (or more specifically, the request URL) is the key, and the `Response` is the value.
There are two types of cache namespaces available to the Cloudflare Cache:
- **`caches.default`** – You can access the default cache (the same cache shared with `fetch` requests) by accessing `caches.default`. This is useful when needing to override content that is already cached, after receiving the response.
- **`caches.open()`** – You can access a namespaced cache (separate from the cache shared with `fetch` requests) using `let cache = await caches.open(CACHE_NAME)`. Note that [`caches.open`](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/open) is an async function, unlike `caches.default`.
When to use the Cache API:
- When you want to programmatically save and/or delete responses from a cache. For example, say an origin is responding with a `Cache-Control: max-age:0` header and cannot be changed. Instead, you can clone the `Response`, adjust the header to the `max-age=3600` value, and then use the Cache API to save the modified `Response` for an hour.
- When you want to programmatically access a Response from a cache without relying on a `fetch` request. For example, you can check to see if you have already cached a `Response` for the `https://example.com/slow-response` endpoint. If so, you can avoid the slow request.
This [template](/workers/examples/cache-api/) shows ways to use the cache API. For limits of the cache API, refer to [Limits](/workers/platform/limits/#cache-api-limits).
:::caution[Tiered caching and the Cache API]
Cache API within Workers does not support tiered caching. Tiered Cache concentrates connections to origin servers so they come from a small number of data centers rather than the full set of network locations. Cache API is local to a data center, this means that `cache.match` does a lookup, `cache.put` stores a response, and `cache.delete` removes a stored response only in the cache of the data center that the Worker handling the request is in. Because these methods apply only to local cache, they will not work with tiered cache.
:::
## Related resources
- [Cache API](/workers/runtime-apis/cache/)
- [Customize cache behavior with Workers](/cache/interaction-cloudflare-products/workers/)
---
# How Workers works
URL: https://developers.cloudflare.com/workers/reference/how-workers-works/
import { Render, NetworkMap, WorkersIsolateDiagram } from "~/components"
Though Cloudflare Workers behave similarly to [JavaScript](https://www.cloudflare.com/learning/serverless/serverless-javascript/) in the browser or in Node.js, there are a few differences in how you have to think about your code. Under the hood, the Workers runtime uses the [V8 engine](https://www.cloudflare.com/learning/serverless/glossary/what-is-chrome-v8/) — the same engine used by Chromium and Node.js. The Workers runtime also implements many of the standard [APIs](/workers/runtime-apis/) available in most modern browsers.
The differences between JavaScript written for the browser or Node.js happen at runtime. Rather than running on an individual's machine (for example, [a browser application or on a centralized server](https://www.cloudflare.com/learning/serverless/glossary/client-side-vs-server-side/)), Workers functions run on [Cloudflare's global network](https://www.cloudflare.com/network) - a growing global network of thousands of machines distributed across hundreds of locations.
event.respondWith(responseResponse|Promise)
: void
* Refer to [`respondWith`](#respondwith).
* event.waitUntil(promisePromise)
: void
* Refer to [`waitUntil`](#waituntil).
* event.passThroughOnException()
: void
* Refer to [`passThroughOnException`](#passthroughonexception).
### `respondWith`
Intercepts the request and allows the Worker to send a custom response.
If a `fetch` event handler does not call `respondWith`, the runtime delivers the event to the next registered `fetch` event handler. In other words, while not recommended, this means it is possible to add multiple `fetch` event handlers within a Worker.
If no `fetch` event handler calls `respondWith`, then the runtime forwards the request to the origin as if the Worker did not. However, if there is no origin – or the Worker itself is your origin server, which is always true for `*.workers.dev` domains – then you must call `respondWith` for a valid response.
```js
// Format: Service Worker
addEventListener('fetch', event => {
let { pathname } = new URL(event.request.url);
// Allow "/ignore/*" URLs to hit origin
if (pathname.startsWith('/ignore/')) return;
// Otherwise, respond with something
event.respondWith(handler(event));
});
```
### `waitUntil`
The `waitUntil` command extends the lifetime of the `"fetch"` event. It accepts a `Promise`-based task which the Workers runtime will execute before the handler terminates but without blocking the response. For example, this is ideal for [caching responses](/workers/runtime-apis/cache/#put) or handling logging.
With the Service Worker format, `waitUntil` is available within the `event` because it is a native `FetchEvent` property.
With the ES modules format, `waitUntil` is moved and available on the `context` parameter object.
```js
// Format: Service Worker
addEventListener('fetch', event => {
event.respondWith(handler(event));
});
async function handler(event) {
// Forward / Proxy original request
let res = await fetch(event.request);
// Add custom header(s)
res = new Response(res.body, res);
res.headers.set('x-foo', 'bar');
// Cache the response
// NOTE: Does NOT block / wait
event.waitUntil(caches.default.put(event.request, res.clone()));
// Done
return res;
}
```
### `passThroughOnException`
The `passThroughOnException` method prevents a runtime error response when the Worker throws an unhandled exception. Instead, the script will [fail open](https://community.microfocus.com/cyberres/b/sws-22/posts/security-fundamentals-part-1-fail-open-vs-fail-closed), which will proxy the request to the origin server as though the Worker was never invoked.
To prevent JavaScript errors from causing entire requests to fail on uncaught exceptions, `passThroughOnException()` causes the Workers runtime to yield control to the origin server.
With the Service Worker format, `passThroughOnException` is added to the `FetchEvent` interface, making it available within the `event`.
With the ES modules format, `passThroughOnException` is available on the `context` parameter object.
```js
// Format: Service Worker
addEventListener('fetch', event => {
// Proxy to origin on unhandled/uncaught exceptions
event.passThroughOnException();
throw new Error('Oops');
});
```
---
# Protocols
URL: https://developers.cloudflare.com/workers/reference/protocols/
Cloudflare Workers support the following protocols and interfaces:
| Protocol | Inbound | Outbound |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **HTTP / HTTPS** | Handle incoming HTTP requests using the [`fetch()` handler](/workers/runtime-apis/handlers/fetch/) | Make HTTP subrequests using the [`fetch()` API](/workers/runtime-apis/fetch/) |
| **Direct TCP sockets** | Support for handling inbound TCP connections is [coming soon](https://blog.cloudflare.com/workers-tcp-socket-api-connect-databases/) | Create outbound TCP connections using the [`connect()` API](/workers/runtime-apis/tcp-sockets/) |
| **WebSockets** | Accept incoming WebSocket connections using the [`WebSocket` API](/workers/runtime-apis/websockets/), or with [MQTT over WebSockets (Pub/Sub)](/pub-sub/learning/websockets-browsers/) | [MQTT over WebSockets (Pub/Sub)](/pub-sub/learning/websockets-browsers/) |
| **MQTT** | Handle incoming messages to an MQTT broker with [Pub Sub](/pub-sub/learning/integrate-workers/) | Support for publishing MQTT messages to an MQTT topic is [coming soon](/pub-sub/learning/integrate-workers/) |
| **HTTP/3 (QUIC)** | Accept inbound requests over [HTTP/3](https://www.cloudflare.com/learning/performance/what-is-http3/) by enabling it on your [zone](/fundamentals/setup/accounts-and-zones/#zones) in **Speed** > **Optimization** > **Protocol Optimization** area of the [Cloudflare dashboard](https://dash.cloudflare.com/). | |
| **SMTP** | Use [Email Workers](/email-routing/email-workers/) to process and forward email, without having to manage TCP connections to SMTP email servers | [Email Workers](/email-routing/email-workers/) |
---
# Security model
URL: https://developers.cloudflare.com/workers/reference/security-model/
import { WorkersArchitectureDiagram } from "~/components"
This article includes an overview of Cloudflare security architecture, and then addresses two frequently asked about issues: V8 bugs and Spectre.
Since the very start of the Workers project, security has been a high priority — there was a concern early on that when hosting a large number of tenants on shared infrastructure, side channels of various kinds would pose a threat. The Cloudflare Workers runtime is carefully designed to defend against side channel attacks.
To this end, Workers is designed to make it impossible for code to measure its own execution time locally. For example, the value returned by `Date.now()` is locked in place while code is executing. No other timers are provided. Moreover, Cloudflare provides no access to concurrency (for example, multi-threading), as it could allow attackers to construct ad hoc timers. These design choices cannot be introduced retroactively into other platforms — such as web browsers — because they remove APIs that existing applications depend on. They were possible in Workers only because of runtime design choices from the start.
While these early design decisions have proven effective, Cloudflare is continuing to add defense-in-depth, including techniques to disrupt attacks by rescheduling Workers to create additional layers of isolation between suspicious Workers and high-value Workers.
The Workers approach is very different from the approach taken by most of the industry. It is resistant to the entire range of [Spectre-style attacks](https://www.cloudflare.com/learning/security/threats/meltdown-spectre/), without requiring special attention paid to each one and without needing to block speculation in general. However, because the Workers approach is different, it requires careful study. Cloudflare is currently working with researchers at Graz University of Technology (TU Graz) to study what has been done. These researchers include some of the people who originally discovered Spectre. Cloudflare will publish the results of this research as they becomes available.
For more details, refer to [this talk](https://www.infoq.com/presentations/cloudflare-v8/) by Kenton Varda, architect of Cloudflare Workers. Spectre is covered near the end.
## Architectural overview
Beginning with a quick overview of the Workers runtime architecture:
Edit src/App.tsx
and save to test HMR
Edit api/index.ts
to change the name
Click on the Vite and React logos to learn more
> ); } export default App; ``` Before deploying again, we need to rebuild our project: ```sh npm run build ``` #### 7. Deploy with Wrangler ```sh npx wrangler deploy ``` Now we can see a new button **Name from API**, and if you click the button, we can see our API response as **Cloudflare**! ## Learn moreput(request, response)
: Promise
* Attempts to add a response to the cache, using the given request as the key. Returns a promise that resolves to `undefined` regardless of whether the cache successfully stored the response.
:::note
The `stale-while-revalidate` and `stale-if-error` directives are not supported when using the `cache.put` or `cache.match` methods.
:::
#### Parameters
* `request` string | Request
* Either a string or a [`Request`](/workers/runtime-apis/request/) object to serve as the key. If a string is passed, it is interpreted as the URL for a new Request object.
* `response` Response
* A [`Response`](/workers/runtime-apis/response/) object to store under the given key.
#### Invalid parameters
`cache.put` will throw an error if:
* The `request` passed is a method other than `GET`.
* The `response` passed has a `status` of [`206 Partial Content`](https://www.webfx.com/web-development/glossary/http-status-codes/what-is-a-206-status-code/).
* The `response` passed contains the header `Vary: *`. The value of the `Vary` header is an asterisk (`*`). Refer to the [Cache API specification](https://w3c.github.io/ServiceWorker/#cache-put) for more information.
#### Errors
`cache.put` returns a `413` error if `Cache-Control` instructs not to cache or if the response is too large.
### `Match`
```js
cache.match(request, options);
```
* match(request, options)
: Promise`delete(request, options)
: Promise`encode(inputUSVString)
: Uint8Array
* Encodes a string input.
***
## TextDecoder
### Background
The `TextDecoder` interface represents a UTF-8 decoder. Decoders take a stream of bytes as input and emit a stream of code points.
[`TextDecoder()`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder) returns a newly constructed `TextDecoder` that generates a code-point stream.
### Constructor
```js
let decoder = new TextDecoder();
```
### Properties
* `decoder.encoding` DOMString read-only
* The name of the decoder that describes the method the `TextDecoder` uses.
* `decoder.fatal` boolean read-only
* Indicates if the error mode is fatal.
* `decoder.ignoreBOM` boolean read-only
* Indicates if the byte-order marker is ignored.
### Methods
* `decode()` : DOMString
* Decodes using the method specified in the `TextDecoder` object. Learn more at [MDN’s `TextDecoder` documentation](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode).
---
# EventSource
URL: https://developers.cloudflare.com/workers/runtime-apis/eventsource/
## Background
The [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) interface is a server-sent event API that allows a server to push events to a client. The `EventSource` object is used to receive server-sent events. It connects to a server over HTTP and receives events in a text-based format.
### Constructor
```js
let eventSource = new EventSource(url, options);
```
* `url` USVString - The URL to which to connect.
* `options` EventSourceInit - An optional dictionary containing any optional settings.
By default, the `EventSource` will use the global `fetch()` function under the
covers to make requests. If you need to use a different fetch implementation as
provided by a Cloudflare Workers binding, you can pass the `fetcher` option:
```js
export default {
async fetch(req, env) {
let eventSource = new EventSource(url, { fetcher: env.MYFETCHER });
// ...
}
};
```
Note that the `fetcher` option is a Cloudflare Workers specific extension.
### Properties
* `eventSource.url` USVString read-only
* The URL of the event source.
* `eventSource.readyState` USVString read-only
* The state of the connection.
* `eventSource.withCredentials` Boolean read-only
* A Boolean indicating whether the `EventSource` object was instantiated with cross-origin (CORS) credentials set (`true`), or not (`false`).
### Methods
* `eventSource.close()`
* Closes the connection.
* `eventSource.onopen`
* An event handler called when a connection is opened.
* `eventSource.onmessage`
* An event handler called when a message is received.
* `eventSource.onerror`
* An event handler called when an error occurs.
### Events
* `message`
* Fired when a message is received.
* `open`
* Fired when the connection is opened.
* `error`
* Fired when an error occurs.
### Class Methods
* EventSource.from(readableStreamReadableStream) : EventSource
* This is a Cloudflare Workers specific extension that creates a new `EventSource` object from an existing `ReadableStream`. Such an instance does not initiate a new connection but instead attaches to the provided stream.
---
# Fetch
URL: https://developers.cloudflare.com/workers/runtime-apis/fetch/
import { TabItem, Tabs } from "~/components"
The [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) provides an interface for asynchronously fetching resources via HTTP requests inside of a Worker.
:::note
Asynchronous tasks such as `fetch` must be executed within a [handler](/workers/runtime-apis/handlers/). If you try to call `fetch()` within [global scope](https://developer.mozilla.org/en-US/docs/Glossary/Global_scope), your Worker will throw an error. Learn more about [the Request context](/workers/runtime-apis/request/#the-request-context).
:::
:::caution[Worker to Worker]
Worker-to-Worker `fetch` requests are possible with [Service bindings](/workers/runtime-apis/bindings/service-bindings/).
:::
## Syntax
fetch(resource, options optional)
: Promise`getAttribute(namestring)
: string | null
- Returns the value for a given attribute name on the element, or `null` if it is not found.
- hasAttribute(namestring)
: boolean
- Returns a boolean indicating whether an attribute exists on the element.
- setAttribute(namestring, valuestring)
: Element
- Sets an attribute to a provided value, creating the attribute if it does not exist.
- removeAttribute(namestring)
: Element
- Removes the attribute.
- before(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content before the element.
after(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content right after the element.
- prepend(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content right after the start tag of the element.
- append(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content right before the end tag of the element.
- replace(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Removes the element and inserts content in place of it.
- setInnerContent(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Replaces content of the element.
- remove()
: Element
- Removes the element with all its content.
- removeAndKeepContent()
: Element
- Removes the start tag and end tag of the element but keeps its inner content intact.
- `onEndTag(handlerFunctionbefore(contentContent, contentOptionsContentOptionsoptional)
:
EndTag
- Inserts content right before the end tag.
- after(contentContent, contentOptionsContentOptionsoptional)
:
EndTag
- Inserts content right after the end tag.
remove()
: EndTag
- Removes the element with all its content.
### Text chunks
Since Cloudflare performs zero-copy streaming parsing, text chunks are not the same thing as text nodes in the lexical tree. A lexical tree text node can be represented by multiple chunks, as they arrive over the wire from the origin.
Consider the following markup: `before(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content before the element.
after(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content right after the element.
- replace(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Removes the element and inserts content in place of it.
- remove()
: Element
- Removes the element with all its content.
### Comments
The `comments` function on an element handler allows developers to query and manipulate HTML comment tags.
```js
class ElementHandler {
comments(comment) {
// An incoming comment element, such as
}
}
```
#### Properties
- `comment.removed` boolean
- Indicates whether the element has been removed or replaced by one of the previous handlers.
- `comment.text` string
- The text of the comment. This property can be assigned different values, to modify comment’s text.
#### Methods
- before(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content before the element.
after(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Inserts content right after the element.
- replace(contentContent, contentOptionsContentOptionsoptional)
:
Element
- Removes the element and inserts content in place of it.
- remove()
: Element
- Removes the element with all its content.
### Doctype
The `doctype` function on a document handler allows developers to query a document’s [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype).
```js
class DocumentHandler {
doctype(doctype) {
// An incoming doctype element, such as
//
}
}
```
#### Properties
- `doctype.name` string | null read-only
- The doctype name.
- `doctype.publicId` string | null read-only
- The quoted string in the doctype after the PUBLIC atom.
- `doctype.systemId` string | null read-only
- The quoted string in the doctype after the SYSTEM atom or immediately after the `publicId`.
### End
The `end` function on a document handler allows developers to append content to the end of a document.
```js
class DocumentHandler {
end(end) {
// The end of the document
}
}
```
#### Methods
- append(contentContent, contentOptionsContentOptionsoptional)
:
DocumentEnd
- Inserts content after the end of the document.