Skip to content

Commit 21ad04f

Browse files
Merge commit '9ac8fe7905bbb8e9890c394aeca7bc57e94be51c'
2 parents a82ed02 + 9ac8fe7 commit 21ad04f

File tree

14 files changed

+630
-0
lines changed

14 files changed

+630
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
type: overview
3+
pcx-content-type: navigation
4+
title: Functions examples
5+
weight: 8
6+
layout: list
7+
---
8+
9+
# Functions examples
10+
11+
12+
{{<directory-listing>}}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
type: example
3+
summary: Set up an A/B test by controlling what page is served based on
4+
cookies. This version supports passing the request through to test and control
5+
on the origin.
6+
tags:
7+
- Originless
8+
pcx-content-type: configuration
9+
title: A/B testing with middleware
10+
weight: 1001
11+
layout: example
12+
---
13+
14+
```js
15+
---
16+
filename: /functions/_middleware.js
17+
---
18+
const cookieName = "ab-test-cookie"
19+
const newHomepagePathName = "/test"
20+
21+
const abTest = async ({ request, next, env }) => {
22+
const url = new URL(request.url)
23+
// if homepage
24+
if (url.pathname === "/") {
25+
// if cookie ab-test-cookie=new then change the request to go to /test
26+
// if no cookie set, pass x% of traffic and set a cookie value to "current" or "new"
27+
28+
let cookie = request.headers.get("cookie")
29+
// is cookie set?
30+
if (cookie && cookie.includes(`${cookieName}=new`)) {
31+
// pass the request to /test
32+
url.pathname = newHomepagePathName
33+
return env.ASSETS.fetch(url)
34+
} else {
35+
const percentage = Math.floor(Math.random() * 100)
36+
let version = "current" // default version
37+
// change pathname and version name for 50% of traffic
38+
if (percentage < 50) {
39+
url.pathname = newHomepagePathName
40+
version = "new"
41+
}
42+
// get the static file from ASSETS, and attach a cookie
43+
const asset = await env.ASSETS.fetch(url)
44+
let response = new Response(asset.body, asset)
45+
response.headers.append("Set-Cookie", `${cookieName}=${version}; path=/`)
46+
return response
47+
}
48+
}
49+
return next()
50+
};
51+
52+
export const onRequest = [abTest];
53+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
type: example
3+
summary: A Pages Functions for appending CORS headers.
4+
tags:
5+
- CORS
6+
pcx-content-type: configuration
7+
title: Adding CORS headers
8+
weight: 1002
9+
layout: example
10+
---
11+
12+
This example is a snippet from our Cloudflare Pages Template repo.
13+
14+
```js
15+
---
16+
filename: /functions/_middleware.js
17+
---
18+
19+
// Respond to OPTIONS method
20+
export const onRequestOptions: PagesFunction = async () => {
21+
return new Response(null, {
22+
status: 204,
23+
headers: {
24+
'Access-Control-Allow-Origin': '*',
25+
'Access-Control-Allow-Headers': '*',
26+
'Access-Control-Allow-Methods': 'GET, OPTIONS',
27+
'Access-Control-Max-Age': '86400',
28+
},
29+
});
30+
};
31+
32+
// Set CORS to all /api responses
33+
export const onRequest: PagesFunction = async ({ next }) => {
34+
const response = await next();
35+
response.headers.set('Access-Control-Allow-Origin', '*');
36+
response.headers.set('Access-Control-Max-Age', '86400');
37+
return response;
38+
};
39+
40+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
pcx-content-type: concept
3+
title: Functions (beta)
4+
layout: single
5+
---
6+
7+
# Functions (beta)
8+
9+
{{<Aside type="note" header="Functions is currently in beta">}}
10+
11+
You can track current issues that the Pages team is fixing in [Known issues](/pages/platform/known-issues/). Let us know any unreported issues by posting in the [Cloudflare Developers Discord](https://discord.com/invite/cloudflaredev).
12+
13+
{{</Aside>}}
14+
15+
With Pages, you can now build full-stack applications by executing code on the Cloudflare network with help from [Cloudflare Workers](https://workers.cloudflare.com/). Functions enable you to run server-side code to enable dynamic functionality without running a dedicated server. With Functions, you can introduce application aspects such as authenticating, querying databases, handling form submissions, or working with middleware.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
pcx-content-type: how-to
3+
title: Advanced Mode
4+
weight: 9
5+
---
6+
7+
# Advanced mode
8+
9+
In some cases, the built-in routing and middleware system is not desirable for existing applications. You may already have a Worker that is fairly complex and/or would be tedious to splice it up into Pages' file-based routing system. For these cases, Pages offers developers the ability to define a `_worker.js` file in the output directory of your Pages project.
10+
11+
When using a `_worker.js` file, the entire `/functions` directory is ignored – this includes its routing and middleware characteristics. Instead, the `_worker.js` file is deployed **as is** and **must be** written using the [Module Worker syntax](/workers/runtime-apis/fetch-event/#syntax-module-worker).
12+
13+
If you have never used Module syntax, refer to the [JavaScript modules blog post to learn more](https://blog.cloudflare.com/workers-javascript-modules/). Using Module Workers enables JavaScript frameworks to generate a Worker as part of the Pages output directory contents.
14+
15+
Your custom Module Worker will assume full control of all incoming HTTP requests to your domain. Because of this, your custom Worker is required to make and/or forward requests to your project's static assets.
16+
17+
```js
18+
---
19+
filename: _worker.js
20+
---
21+
export default {
22+
async fetch(request, env) {
23+
const url = new URL(request.url);
24+
if (url.pathname.startsWith('/api/')) {
25+
// TODO: Add your custom /api/* logic here.
26+
return new Response('Ok');
27+
}
28+
// Otherwise, serve the static assets.
29+
// Without this, the Worker will error and no assets will be served.
30+
return env.ASSETS.fetch(request);
31+
},
32+
};
33+
```
34+
35+
The `env.ASSETS.fetch()` function will allow you to send the user to a modified path which is defined through the `url` parameter. `env` is the object that contains your environment variables and bindings. `ASSETS` is a default Function binding that allows communication between your Function and Pages' asset serving resource. `fetch()` calls to Pages' asset-serving resource and serves the requested asset.
36+
37+
{{<Aside type="warning">}}
38+
39+
Your custom Module Worker is required to forward requests to static assets. Failure to do so will result in broken and/or unwanted behavior because your website's contents will not be served if you do not serve it.
40+
41+
{{</Aside>}}
42+
43+
Then after placing your `_worker.js` file in your output directory, deploy your project normally through your git integration.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
pcx-content-type: how-to
3+
title: API Reference
4+
weight: 3
5+
---
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
pcx-content-type: how-to
3+
title: Bindings
4+
weight: 5
5+
---
6+
7+
# Bindings
8+
9+
## Adding bindings
10+
11+
A binding is how your Function (Worker) interacts with external resources. You can add KV, Durable Object, and plain-text bindings to your project. A binding is a runtime variable that the Workers runtime provides to your code. You can also use these bindings in development with [Wrangler](/pages/platform/functions/#develop-and-preview-locally).
12+
13+
### KV namespace
14+
15+
Workers KV is Cloudflare's globally replicated key-value storage solution. Within Pages, you can choose from the list of KV namespaces that you created from the dashboard by going to **Account Home** > **Pages** > **your Pages project** > **Settings** > **Functions** > **KV namespace bindings**. Select **Add binding** and input a **Variable name** and select a _KV namespace_ from the list of your existing Workers KV namespaces. You will need to repeat this for both the **Production** and **Preview** environments.
16+
17+
![Editing a KV namespace Binding and adding a Variable name](/pages/platform/media/KV-functions.png)
18+
19+
### KV namespace locally
20+
21+
While developing locally, you can interact with your KV namespace by add `-k, --kv [Namespace binding]` to your run command. For example, if your namespace is bound to `TodoList`, you can access the KV namespace in your local dev by running `npx wrangler pages dev dist --kv TodoList`. The data from this namespace can be accessed using `context.env`.
22+
23+
```js
24+
export async function onRequest({ env }) {
25+
const task = await env.TodoList.get("Task:123");
26+
return new Response(task);
27+
}
28+
```
29+
30+
### Durable Object namespace
31+
32+
Durable Objects are Cloudflare's strongly consistent coordination primitive that power capabilities such as connecting WebSockets, handling state, and building applications. As with Workers KV, you first have to [create the Durable Object](/workers/learning/using-durable-objects/#uploading-a-durable-object-worker). You can then configure it as a binding to your Pages project.
33+
34+
Go to **Account Home** > **Pages** > **your Pages project** > **Settings** > **Functions** > **Durable Object bindings**. Select **Add binding** and input a **Variable name** and select a _Durable Object namespace_ from the list of your existing Durable Objects. You will need to repeat this for both the **Production** and **Preview** environments.
35+
36+
![Editing a Durable Object namespace Binding and adding a Variable name](/pages/platform/media/DO-functions.png)
37+
38+
### Durable Objects locally
39+
40+
Just as you can access kv with `-k` or `--kv` you can access durable objects in your local builds with `-o`, `--do` followed by your Durable object name and class.
41+
42+
### Environment variable
43+
44+
An [environment variable](/workers/platform/environment-variables/) is an injected value that can be accessed by your Functions. It is stored as plaintext. You can set your environment variables directly within the Pages interface for both your production and preview environments at run-time and build-time.
45+
46+
To add environment variables, go to **Account Home** > **Pages** > **your Pages project** > **Settings** > **Environment variables**.
47+
48+
![Editing an environment variable by adding a variable name and value](/pages/platform/media/ENV-functions.png)
49+
50+
### Adding environment variables locally
51+
52+
When developing locally, you can access environment variables by adding a binding to your Wrangler commands like `npx wrangler pages dev dist --binding ENV_NAME="ENV_VALUE"`. This allows you to then access the environment value in your component by using `env.ENV_NAME`.
53+
54+
For example, you can run `npx wrangler pages dev dist --binding COLOR="BLUE"` and then:
55+
56+
```js
57+
export async function onRequest({ env }) {
58+
return new Response(env.COLOR);
59+
}
60+
```
61+
62+
Here is a real-world example of using environment variables inside a middleware function. To connect [Sentry](https://www.sentry.io/) to a Cloudflare Worker, you can use [Toucan js](https://github.com/robertcepa/toucan-js) and access your Sentry Data Source Name (DSN) in your function.
63+
64+
```js
65+
const SentryMiddleware = async ({ request, next, env, waitUntil }) => {
66+
const sentry = new Toucan({
67+
dsn: env.SENTRY_DSN,
68+
context: { waitUntil, request },
69+
});
70+
try {
71+
return await next();
72+
} catch (thrown) {
73+
sentry.captureException(thrown);
74+
return new Response(`Error ${thrown}`, {
75+
status: 500,
76+
});
77+
}
78+
};
79+
export const onRequest = [SentryMiddleware];
80+
```
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
pcx-content-type: tutorial
3+
title: Writing your first Function
4+
weight: 2
5+
---
6+
7+
# Writing your first Function
8+
9+
When writing request handlers within your Pages application, each `/functions` file must `export` a function to handle the incoming request. Each function will receive a singular `context` object, which contains all the information for the request:
10+
11+
```js
12+
export async function onRequest(context) {
13+
// Contents of context object
14+
const {
15+
request, // same as existing Worker API
16+
env, // same as existing Worker API
17+
params, // if filename includes [id] or [[path]]
18+
waitUntil, // same as ctx.waitUntil in existing Worker API
19+
next, // used for middleware or to fetch assets
20+
data, // arbitrary space for passing data between middlewares
21+
} = context;
22+
23+
return new Response("Hello, world!");
24+
}
25+
```
26+
27+
When migrating from a [Module Worker](/workers/runtime-apis/fetch-event/#syntax-module-worker), this signature combines the traditional `fetch` handler's arguments into a single object along with additional, Pages-specific keys.
28+
29+
In the previous example, an `onRequest` function was exported. This is a generic name because it generically handles all HTTP requests. However, to react to specific HTTP request methods, you may use the method name as a suffix to the exported function. For example, a handler that should only receive `GET` requests should be named `onRequestGet`. The following other handlers are supported:
30+
31+
- `onRequestPost`
32+
- `onRequestPut`
33+
- `onRequestPatch`
34+
- `onRequestDelete`
35+
- `onRequestHead`
36+
- `onRequestOptions`
37+
38+
These are the requests you export to write your first function. For example, you can write a function to output `"Hello World"` when it hits a `/functions/hello-world.js` file:
39+
40+
```js
41+
---
42+
filename: functions/hello-world.js
43+
---
44+
// Reacts to POST /hello-world
45+
export async function onRequestPost(request) {
46+
// ...
47+
return new Response(`Hello world`);
48+
}
49+
```
50+
51+
Another helpful example for handling single path segments can be querying an API for data, for example, [Rick and Morty API](https://rickandmortyapi.com/documentation/#rest) for information on the show characters. You can write a function to show each character on request using the ID to identify them:
52+
53+
```js
54+
---
55+
filename:function/character/[id].js
56+
---
57+
export async function onRequestGet({ params }) {
58+
const res = await fetch(`https://rickandmortyapi.com/api/character/${params.id}`);
59+
const data = await res.json();
60+
const info = JSON.stringify(data, null, 2);
61+
return new Response(info);
62+
}
63+
```
64+
65+
The above will return each character at `/character/{id}` ID being associated with the character.
66+
67+
### Handling multiple requests in a single function
68+
69+
You can define multiple HTTP handlers in a single file by defining multiple exports within the same file. For example, this file will handle `POST` and `PUT` requests with the same handler code:
70+
71+
```js
72+
export async function onRequestPost(context) {
73+
// ...
74+
}
75+
76+
export const onRequestPut = onRequestPost;
77+
```
78+
79+
Additionally, an exported handler may be an array of function handlers. This allows you to easily compose Functions as a group, which may include a mix of shared and/or one-off behaviors:
80+
81+
```js
82+
import { extraLogging } from "middlewares.ts";
83+
84+
export const onRequest = [
85+
extraLogging,
86+
87+
async ({ request }) => {
88+
// ...
89+
},
90+
];
91+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
pcx-content-type: get-started
3+
title: Get started
4+
weight: 1
5+
---
6+
7+
# Get Started
8+
9+
## Built with Cloudflare Workers
10+
11+
Cloudflare Workers provides a serverless [execution environment](https://www.cloudflare.com/en-gb/learning/serverless/what-is-serverless/) that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure.
12+
13+
Previously, you could only add dynamic functionality to your Pages site by manually deploying a Worker using Wrangler, which meant that your application is written across both Pages and Workers.
14+
15+
Functions allow you to leverage the Workers platform directly from within a Pages project by utilizing a project's filesystem convention. In addition, Functions enable you to deploy your entire site – static and dynamic content – when you `git push`.
16+
17+
{{<Aside type="note" header="Functions is currently in beta">}}
18+
You can track current issues that the Pages team is fixing in Known issues. Let us know any unreported issues by posting in the Cloudflare Developers Discord.
19+
{{</Aside>}}
20+
21+
## Setup
22+
23+
To get started, create a `/functions` directory at the root of your project. Writing your Functions files in this directory automatically generates a Worker with custom functionality at the predesignated routes.
24+
25+
Now that you have your `/functions` directory setup, get started [writing your first function](/pages/platform/functions/first-function/)
26+
27+
## Demo
28+
29+
To get started with your first Pages project with Functions, refer to the [demo blog post on how to build an image sharing application](http://blog.cloudflare.com/building-full-stack-with-pages). In this demo, you will build a JSON API with Functions (storing data on KV and Durable Objects), integrate with [Cloudflare Images](/images/) and [Cloudflare Access](/cloudflare-one/), and use React for your front end.

0 commit comments

Comments
 (0)