Deploying Pages Functions with wrangler cli

Hello!

Apologies if this question has an answer in the documentation somewhere. I could be bad a googling :slight_smile:

I’m having trouble using wrangler2 cli to deploy Pages Functions alongside the page itself.
I have a working Direct Upload Pages deployment working using Bitbucket Pipelines. Also the Functions themselves work when running with wrangler pages dev dist/ command on a local machine.

The only documentation I could find is at the bottom of Direct Uploads page stating the following:

Drag-and-drop deployments made from the Cloudflare dashboard do not currently support compiling a functions folder of Pages Functions. In order to deploy a functions folder, you must use Wrangler.

What am I doing wrong and how to get functions to deploy using wrangler publish?

Thanking in advance!

1 Like

As I cannot include links in my posts (yet?), this can hopefully help…
developers [dot] Cloudflare [dot] com [f-slash] pages [f-slash] platform [f-slash] direct-upload [f-slash] #using-functions

Can you share your project structure or a minimal reproduction?

The functions directory should exist in the folder wherever you run the wrangler pages dev command.

It’s a barebones Nuxt.js starter page created using npx create-nuxt-app <project-name>.

.
├── bitbucket-pipelines.yml
├── components
│   ├── NuxtLogo.vue
│   └── Tutorial.vue
├── functions
│   └── api
│       └── email.js
├── nuxt.config.js
├── package.json
├── package-lock.json
├── pages
│   └── index.vue
├── README.md
├── static
│   └── favicon.ico
└── store
    └── README.md

That looks good, thanks! Can you share what your email.js function looks like?

email.js

export async function onRequestPost(context) {
	const {request} = context;
	const requestBody = await request.json()
	
	const res = await fetch(`https://emailoctopus.com/api/1.6/lists/${env.EMAILOCTOPUS_LIST_ID}/contacts`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({
			"api_key": env.EMAILOCTOPUS_API_TOKEN,
			"email_address": requestBody.email_address,
			"fields": {
				"FirstName": requestBody.name,
				"Locale": requestBody.locale
			},
			"tags": [],
			"status": "SUBSCRIBED"
		})
	});

	await res.json();

	return new Response(null, {
		status: res.status
	});
}

Under the “Functions” tab within the Cloudflare Pages dashboard for your deployment, do you see it listed there? What’s the full command you’re using to publish?

The Functions tab shows just the instructions to use Functions.
When I opened the Functions tab, I got an error message saying You are not entitled to use r2. (Code: 10042), which has not happened before.

This is the command I use to deploy
wrangler pages publish out/ --project-name=<project-name> --commit-hash=$BITBUCKET_COMMIT --branch=development

Also, I the “Assets uploaded” tab does not show the functions folder.

I’m sure I tried a deployment that included the functions directory in the deployable dist directory, but I remember it failing.

Thank you for the help for now! I have to run some errands and will be back after a while. If you have ideas I will take a look when I’m back and report here.

I’ll also try manually deploying the dist folder with manually moving functions/ folder in there.

Right, so I deployed the dist/ folder with functions/ copied into it. The deployment’s “Assets uploaded” tab shows the email.js file among other files.

I created a new project with wrangler pages project create test and deployed with wrangler pages publish dist --project-name=test

An image of the Functions tab as well.

Does anyone happen to have any suggestions?

1 Like

@Kaur I just setup new Vite project and experience the same thing, however despite Functions not being displayed in Cloudflare’s UI they work as expected. I put the following dumb function inside /functions/api/example.js:

/**
 * GET /api/example
 */
 export const onRequestGet = async function onRequestGet(context) {
   return new Response("Hello", {
     status: 200,
     headers: {
       'Content-Type': 'application/json;charset=utf-8',
     },
   });
}

After publishing dist folder of the app, going to the following URL returns proper response: e7a2ffd6.test-app-dv4.pages[dot]dev/api/example

I looked at the source code of the wrangler CLI and by publishing the page they are indeed converting entire /functions folder into a single _worker.js file and upload it together with the directory: github[dot]com/Cloudflare/wrangler2/blob/main/packages/wrangler/src/pages/publish.tsx#L263

Difficult to tell though why these functions aren’t displayed in the UI. The generated worker file seems fine at first glance. Thankfully they work regardless of the UI state in CF :sweat_smile:

So yes, a couple of things:

  • there does appear to be a UI bug with Functions not appearing on the dash (but indeed being added), I believe the team is aware :slightly_smiling_face:
  • @Kaur /functions needs to be in the root of your project, not the build output directory. So your setup would look like the following (notice how /functions is beside /out and not inside of it):
|- /out
|---- 200.html
|---- ...etc, all the other files
|- /functions
|---- /api
|-------- email.js

Thank you @msajnog93 for testing it out and getting back to me! Also, thank you @mcfadyeni for confirming the UI bug and also the location of the functions directory!

I did test the setup last night, and got it working. I moved the email.js file up one dir, so it now sits at functions/email.js, just in case my api/ subdirectory was interfering with it (the resulting route was not that important). I also changed the function declaration to look like in @msajnog93’s example, so the final function looks like this:

export const onRequestPost = async function onRequestPost({request, env}) {
	const requestBody = await request.json()
	
	const res = await fetch(`https://emailoctopus.com/api/1.6/lists/${env.EMAILOCTOPUS_LIST_ID}/contacts`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({
			"api_key": env.EMAILOCTOPUS_API_TOKEN,
			"email_address": requestBody.email_address,
			"fields": {
				"FirstName": requestBody.name,
				"Locale": requestBody.locale
			},
			"tags": [],
			"status": "SUBSCRIBED"
		})
	});

	await res.json();

	return new Response(null, {
		status: res.status
	});
}

Thank you again, both of you, for getting back to me!

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.