These templates are simple building blocks for developing Workers scripts.
Skeletons that are useful when starting a project. Requires installation of Wrangler.
Simple Hello World in JS
wrangler generate my-app
https://github.com/cloudflare/worker-template
Simple Hello World in Rust
wrangler generate my-app
https://github.com/cloudflare/rustwasm-worker-template
Selects the logic based on the request method and URL. Use with REST APIs or apps that require routing logic.
wrangler generate my-app
https://github.com/cloudflare/worker-template-router
Simple Hello World in TypeScript
wrangler generate my-app
https://github.com/EverlastingBugstopper/worker-typescript-template
Copy pasteable code that can be used in a new or existing project.
Sends two GET request to two urls and aggregates the responses into one response.
async function handleRequest(request) {
const init = {
headers: {
'content-type': type,
},
}
const responses = await Promise.all([fetch(url1, init), fetch(url2, init)])
const results = await Promise.all([gatherResponse(responses[0]), gatherResponse(responses[1])])
return new Response(results, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
/**
* gatherResponse awaits and returns a response body as a string.
* Use await gatherResponse(..) in an async function to get the response body
* @param {Response} response
*/
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get('content-type')
if (contentType.includes('application/json')) {
return await response.json()
} else if (contentType.includes('application/text')) {
return await response.text()
} else if (contentType.includes('text/html')) {
return await response.text()
} else {
return await response.text()
}
}
/**
* Example someHost is set up to return JSON responses
* Replace url1 and url2 with the hosts you wish to
* send requests to
* @param {string} url the URL to send the request to
*/
const someHost = 'https://workers-tooling.cf/demos'
const url1 = someHost + '/requests/json'
const url2 = someHost + '/requests/json'
const type = 'application/json;charset=UTF-8'
Redirects requests to certain URLs based a mapped object to the request’s URL.
async function handleRequest(request) {
let requestURL = new URL(request.url)
let path = requestURL.pathname.split('/redirect')[1]
let location = redirectMap.get(path)
if (location) {
return Response.redirect(location, 301)
}
// If in map, return the original request
return fetch(request)
}
addEventListener('fetch', async event => {
event.respondWith(handleRequest(event.request))
})
const externalHostname = 'workers-tooling.cf'
const redirectMap = new Map([
['/bulk1', 'https://' + externalHostname + '/redirect2'],
['/bulk2', 'https://' + externalHostname + '/redirect3'],
['/bulk3', 'https://' + externalHostname + '/redirect4'],
['/bulk4', 'https://google.com'],
])
Cache using Cloudflare’s Cache API. This example can cache POST requests as well as change what hostname to store a response in cache. Note the previewer is not avalible for using Cache API.
async function handleRequest(event) {
let request = event.request
let cacheUrl = new URL(request.url)
// hostname for a different zone
cacheUrl.hostname = someOtherHostname
let cacheKey = new Request(cacheUrl, request)
let cache = caches.default
// Get this request from this zone's cache
let response = await cache.match(cacheKey)
if (!response) {
//if not in cache, grab it from the origin
response = await fetch(request)
// must use Response constructor to inherit all of response's fields
response = new Response(response.body, response)
// Cache API respects Cache-Control headers, so by setting max-age to 10
// the response will only live in cache for max of 10 seconds
response.headers.append('Cache-Control', 'max-age=10')
// store the fetched response as cacheKey
// use waitUntil so computational expensive tasks don't delay the response
event.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
async function handlePostRequest(event) {
let request = event.request
let body = await request.clone().text()
let hash = await sha256(body)
let cacheUrl = new URL(request.url)
// get/store the URL in cache by prepending the body's hash
cacheUrl.pathname = '/posts' + cacheUrl.pathname + hash
// Convert to a GET to be able to cache
let cacheKey = new Request(cacheUrl, {
headers: request.headers,
method: 'GET',
})
let cache = caches.default
//try to find the cache key in the cache
let response = await cache.match(cacheKey)
// otherwise, fetch response to POST request from origin
if (!response) {
response = await fetch(request)
event.waitUntil(cache.put(cacheKey, response))
}
return response
}
addEventListener('fetch', event => {
try {
let request = event.request
if (request.method.toUpperCase() === 'POST')
return event.respondWith(handlePostRequest(event))
return event.respondWith(handleRequest(event))
} catch (e) {
return event.respondWith(new Response('Error thrown ' + e.message))
}
})
async function sha256(message) {
// encode as UTF-8
const msgBuffer = new TextEncoder().encode(message)
// hash the message
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)
// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer))
// convert bytes to hex string
const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')
return hashHex
}
const someOtherHostname = 'my.herokuapp.com'
Determine how/if Cloudflare’s Classic CDN and Browsers will cache a resource by setting TTLs, cache keys, and cache settings.
async function handleRequest(request) {
let url = new URL(request.url)
// Only use the path for the cache key, removing query strings
// and always storing HTTPS e.g. https://www.example.com/file-uri-here
let someCustomKey = `https://${url.hostname}${url.pathname}`
let response = await fetch(request, {
cf: {
// Tell Cloudflare's Global CDN to always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5, // sets TTL to 5 and cacheEverything setting
//Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
})
// must use Response constructor to inherit all of response's fields
response = new Response(response.body, response)
//Set cache control headers to cache on browser for 25 minutes
response.headers.set('Cache-Control', 'max-age=1500')
return response
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
Add necessary CORS headers to a third party API response
async function handleRequest(request) {
const url = new URL(request.url)
const apiurl = url.searchParams.get('apiurl')
// Rewrite request to point to API url. This also makes the request mutable
// so we can add the correct Origin header to make the API server think
// that this request isn't cross-site.
request = new Request(apiurl, request)
request.headers.set('Origin', new URL(apiurl).origin)
let response = await fetch(request)
// 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
}
function handleOptions(request) {
// Make sure the necesssary headers are present
// for this to be a valid pre-flight 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 pre-flight request.
// If you want to check the requested method + headers
// you can do that here.
return new Response(null, {
headers: corsHeaders,
})
} else {
// Handle standard OPTIONS request.
// If you want to allow other HTTP Methods, you can do that here.
return new Response(null, {
headers: {
Allow: 'GET, HEAD, POST, OPTIONS',
},
})
}
}
addEventListener('fetch', event => {
const request = event.request
const url = new URL(request.url)
if (url.pathname.startsWith(proxyEndpoint)) {
if (request.method === 'OPTIONS') {
// Handle CORS preflight requests
event.respondWith(handleOptions(request))
} else if (
request.method === 'GET' ||
request.method === 'HEAD' ||
request.method === 'POST'
) {
// Handle requests to the API server
event.respondWith(handleRequest(request))
} else {
event.respondWith(async () => {
return new Response(null, {
status: 405,
statusText: 'Method Not Allowed',
})
})
}
} else {
// Serve demo page
event.respondWith(rawHtmlResponse(demoPage))
}
})
// We support the GET, POST, HEAD, and OPTIONS methods from any origin,
// and accept the Content-Type header on requests. These headers must be
// present on all responses to all CORS requests. In practice, this means
// all responses to OPTIONS requests.
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
}
// The URL for the remote third party API you want to fetch from
// but does not implement CORS
const apiurl = 'https://workers-tooling.cf/demos/demoapi'
// The endpoint you want the CORS reverse proxy to be on
const proxyEndpoint = '/corsproxy/'
// The rest of this snippet for the demo page
async function rawHtmlResponse(html) {
return new Response(html, {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
})
}
const demoPage = `
<!DOCTYPE html>
<html>
<body>
<h1>API GET without CORS Proxy</h1>
<a target='_blank' href='https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful'>Shows TypeError: Failed to fetch since CORS is misconfigured</a>
<p id='noproxy-status'/>
<code id='noproxy'>Waiting</code>
<h1>API GET with CORS Proxy</h1>
<p id='proxy-status'/>
<code id='proxy'>Waiting</code>
<h1>API POST with CORS Proxy + Preflight</h1>
<p id='proxypreflight-status'/>
<code id='proxypreflight'>Waiting</code>
<script>
let reqs = {};
reqs.noproxy = async () => {
let response = await fetch('${apiurl}')
return await response.json()
}
reqs.proxy = async () => {
let response = await fetch(window.location.origin + '${proxyEndpoint}?apiurl=${apiurl}')
return await response.json()
}
reqs.proxypreflight = async () => {
const reqBody = {
msg: "Hello world!"
}
let response = await fetch(window.location.origin + '${proxyEndpoint}?apiurl=${apiurl}', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(reqBody),
})
return await response.json()
}
(async () => {
for (const [reqName, req] of Object.entries(reqs)) {
try {
let data = await req()
document.getElementById(reqName).innerHTML = JSON.stringify(data)
} catch (e) {
document.getElementById(reqName).innerHTML = e
}
}
})()
</script>
</body>
</html>`
Sends a request to a remote server, reads HTML from the response, then serves that HTML.
async function handleRequest(request) {
const init = {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
}
const response = await fetch(url, init)
const results = await gatherResponse(response)
return new Response(results, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
/**
* gatherResponse awaits and returns a response body as a string.
* Use await gatherResponse(..) in an async function to get the response body
* @param {Response} response
*/
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get('content-type')
if (contentType.includes('application/json')) {
return await response.json()
} else if (contentType.includes('application/text')) {
return await response.text()
} else if (contentType.includes('text/html')) {
return await response.text()
} else {
return await response.text()
}
}
/**
* Example someHost at url is set up to respond with HTML
* Replace url with the host you wish to send requests to
* */
const someHost = 'https://workers-tooling.cf/demos'
const url = someHost + '/static/html'
Sends a GET request and reads in JSON from the response.
async function handleRequest(request) {
const init = {
headers: {
'content-type': type,
},
}
const responses = await Promise.all([fetch(url1, init), fetch(url2, init)])
const results = await Promise.all([gatherResponse(responses[0]), gatherResponse(responses[1])])
return new Response(results, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
/**
* gatherResponse awaits and returns a response body as a string.
* Use await gatherResponse(..) in an async function to get the response body
* @param {Response} response
*/
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get('content-type')
if (contentType.includes('application/json')) {
return await response.json()
} else if (contentType.includes('application/text')) {
return await response.text()
} else if (contentType.includes('text/html')) {
return await response.text()
} else {
return await response.text()
}
}
/**
* Example someHost is set up to return JSON responses
* Replace url1 and url2 with the hosts you wish to
* send requests to
* @param {string} url the URL to send the request to
*/
const someHost = 'https://workers-tooling.cf/demos'
const url1 = someHost + '/requests/json'
const url2 = someHost + '/requests/json'
const type = 'application/json;charset=UTF-8'
Serve an HTML form, then read POSTs from that form data.
async function handlePostRequest(request) {
let reqBody = await readRequestBody(request)
let retBody = `The request body sent in was ${reqBody}`
return new Response(retBody)
}
async function handleRequest(request) {
let retBody = `The request was a GET `
return new Response(retBody)
}
addEventListener('fetch', event => {
const { request } = event
const { url } = request
if (url.includes('form')) {
return event.respondWith(rawHtmlResponse(someForm))
}
if (request.method === 'POST') {
return event.respondWith(handlePostRequest(request))
} else if (request.method === 'GET') {
return event.respondWith(handleRequest(request))
}
})
/**
* rawHtmlResponse delievers a response with HTML inputted directly
* into the worker script
* @param {string} html
*/
async function rawHtmlResponse(html) {
const init = {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
}
return new Response(html, init)
}
/**
* readRequestBody reads in the incoming request body
* Use await readRequestBody(..) in an async function to get the string
* @param {Request} request the incoming request to read from
*/
async function readRequestBody(request) {
const { headers } = request
const contentType = headers.get('content-type')
if (contentType.includes('application/json')) {
const body = await request.json()
return JSON.stringify(body)
} else if (contentType.includes('application/text')) {
const body = await request.text()
return body
} else if (contentType.includes('text/html')) {
const body = await request.text()
return body
} else if (contentType.includes('form')) {
const formData = await request.formData()
let body = {}
for (let entry of formData.entries()) {
body[entry[0]] = entry[1]
}
return JSON.stringify(body)
} else {
let myBlob = await request.blob()
var objectURL = URL.createObjectURL(myBlob)
return objectURL
}
}
const someForm = `
<!DOCTYPE html>
<html>
<body>
<h1>Hello World</h1>
<p>This is all generated using a Worker</p>
<form action="/demos/requests" method="post">
<div>
<label for="say">What do you want to say?</label>
<input name="say" id="say" value="Hi">
</div>
<div>
<label for="to">To who?</label>
<input name="to" id="to" value="Mom">
</div>
<div>
<button>Send my greetings</button>
</div>
</form>
</body>
</html>
Recommended practice for forming a request based off the incoming request. First, takes in the incoming request then modifies specific properties like POST body, redirect, and the Cloudflare specific property cf and runs the fetch.
async function handleRequest(request) {
/**
* Best practice is to only assign new properties on the request
* object (i.e. RequestInit props) through either a method or the constructor
*/
let newRequestInit = {
// Change method
method: 'POST',
// Change body
body: JSON.stringify({ bar: 'foo' }),
// Change the redirect mode.
redirect: 'follow',
//Change headers, note this method will erase existing headers
headers: {
'Content-Type': 'application/json',
},
// Change a Cloudflare feature on the outbound response
cf: { apps: false },
}
// Change URL
let url = someUrl
// Change just the host
url = new URL(url)
url.hostname = someHost
// Best practice is to always use the original request to construct the new request
// thereby cloning all the attributes, applying the URL also requires a constructor
// since once a Request has been constructed, its URL is immutable.
const newRequest = new Request(url, new Request(request, newRequestInit))
// Set headers using method
newRequest.headers.set('X-Example', 'bar')
newRequest.headers.set('Content-Type', 'application/json')
try {
return await fetch(newRequest)
} catch (e) {
return new Response(JSON.stringify({ error: e.message }), { status: 500 })
}
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Example someHost is set up to return raw JSON
* @param {string} someUrl the URL to send the request to, since we are setting hostname too only path is applied
* @param {string} someHost the host the request will resolve too
*/
const someHost = 'example.com'
const someUrl = 'https://foo.example.com/api.js'
Recommended practice for mutating a fetched response. First, fetches a request then modifies specific properties which are immutable: status, statusText, headers and body.
async function handleRequest(request) {
/**
* Best practice is to only assign properties on the response
* object (i.e. ResponseInit props) through either a method or the constructor
* since properties are immutable
*/
let originalResponse = await fetch(request)
let originalBody = await originalResponse.json()
// Change status and statusText
// Make sure to pass in originalResponse to preserving all parts
// of the original response except the part we want to update.
let response = new Response(originalResponse, { status: 500, statusText: 'some message' })
// Change response body by adding the foo prop
let body = JSON.stringify({ foo: 'bar', ...originalBody })
response = new Response(body, response)
// Add a header using set method
response.headers.set('foo', 'bar')
// Set destination header to the value of the source header
if (response.headers.has(headerNameSrc)) {
response.headers.set(headerNameDst, response.headers.get(headerNameSrc))
console.log(
`Response header "${headerNameDst}" was set to "${response.headers.get(headerNameDst)}"`,
)
}
return response
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* @param {string} headerNameSrc the header to get the new value from
* @param {string} headerNameDst the header to set based off of value in src
*/
const headerNameSrc = 'foo'//'Orig-Header'
const headerNameDst = 'Last-Modified'
Sends a POST request with JSON data from the Workers script.
async function handleRequest(request) {
const init = {
body: JSON.stringify(body),
method: 'POST',
headers: {
'content-type': 'application/json;charset=UTF-8',
},
}
const response = await fetch(url, init)
const results = await gatherResponse(response)
return new Response(results, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
/**
* gatherResponse awaits and returns a response body as a string.
* Use await gatherResponse(..) in an async function to get the response body
* @param {Response} response
*/
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get('content-type')
if (contentType.includes('application/json')) {
return await response.json()
} else if (contentType.includes('application/text')) {
return await response.text()
} else if (contentType.includes('text/html')) {
return await response.text()
} else {
return await response.text()
}
}
/**
* Example someHost is set up to take in a JSON request
* Replace url with the host you wish to send requests to
* @param {string} url the URL to send the request to
* @param {BodyInit} body the JSON data to send in the request
*/
const someHost = 'https://workers-tooling.cf/demos'
const url = someHost + '/requests/json'
const body = {
results: ['default data to send'],
errors: null,
msg: 'I sent this to the fetch',
}
Redirect a request by sending a 301 or 302 HTTP response
async function handleRequest(request) {
return Response.redirect(someURLToRedirectTo, code)
}
addEventListener('fetch', async event => {
event.respondWith(handleRequest(event.request))
})
/**
* Example Input
* @param {Request} url where to redirect the response
* @param {number?=301|302} type permanent or temporary redirect
*/
const someURLToRedirectTo = 'https://www.google.com'
const code = 301
Delivers an HTML page from HTML directly in the Worker script.
async function handleRequest(request) {
const init = {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
}
return new Response(someHTML, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
const someHTML = `<!DOCTYPE html>
<html>
<body>
<h1>Hello World</h1>
<p>This is all generated using a Worker</p>
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
</body>
</html>
`
Renders a response of type application/json to the client
async function handleRequest(request) {
const init = {
headers: {
'content-type': 'application/json;charset=UTF-8',
},
}
return new Response(JSON.stringify(someJSON), init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
const someJSON = {
result: ['some', 'results'],
errors: null,
msg: 'this is some random json',
}
More boilerplate projects. Requires installation of Wrangler.
The gallery is actively growing. The template creator allows you to share templates. Host a public repo, and then run wrangler generate https://github.com/<your-repo>.
For archived recipes see the old docs.
Serve BinAST via a Cloudflare Worker
wrangler generate my-app
https://github.com/xtuc/binast-cf-worker-template
Image Resizer in C compiled to Wasm with Emscripten
wrangler generate my-app
https://github.com/cloudflare/worker-emscripten-template
🔥Lightning-fast, globally distributed Apollo GraphQL server, deployed at the edge using Cloudflare Workers. Read the tutorial to learn more!
wrangler generate my-app
https://github.com/signalnerve/workers-graphql-server
Retrieve the dominant color of a PNG or JPEG image
wrangler generate my-app
https://github.com/xtuc/img-color-worker
Log exceptions and errors in your Workers application to Sentry.io, an error tracking tool
wrangler generate my-app
https://github.com/bustle/cf-sentry
Worker for measuring download / upload connection speed from the client side, using the Performance Timing API
wrangler generate my-app
https://github.com/cloudflare/worker-speedtest-template