cloudflare / workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
https://developers.cloudflare.com/workers/
Apache License 2.0
2.73k stars 718 forks source link

🐛 BUG: Cloudflare pages not deploying functions #1859

Closed jaystacey closed 1 week ago

jaystacey commented 2 years ago

What version of Wrangler are you using?

2

What operating system are you using?

Cloudflare Pages

Describe the Bug

Wrangler is not detecting functions routes, when deploying with Cloudflare Pgaes from github repo.

output below, when '/functions/api/contact.ts' function file exists.

11:19:55.381 🚧 'wrangler pages ' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
11:19:55.389  
11:19:55.490 ✘ [ERROR] No routes found when building Functions directory: /opt/buildhome/repo/functions
11:19:55.491  
11:19:55.491  
11:19:55.491 If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose
11:19:55.502 Warning: Wrangler did not find routes when building functions. Skipping.
GregBrimble commented 2 years ago

What does your functions/api/contact.ts file look like? I suspect you aren't correctly exporting any onRequest* handlers.

jaystacey commented 2 years ago

contact.ts exports a onRequestPost() function, previously been working however after a recent merge (not relating to that file) wrangler no longer detects any functions routes.

I did a test with a 'test.js' function at the base functions directory (/functions/test.js) using example code:

export async function onRequest() { return new Response("Hello, world!"); }

and it doens't detect that as a route either...

also the garbled log output.

image

chri70 commented 2 years ago

Same problem but deployed from Gitlab repo (not really significant I suppose) image

A previous commit works with the same code in /functions directory. If I redeploy this working commit all is fine.

GregBrimble commented 2 years ago

Do you have a link to the repository (or one that is similarly configured) and copy of your root directory/build command/output directory settings?

Note that the functions/ directory should be in the root directory of your repo (usually that's just the top-level folder).

chri70 commented 2 years ago

I will send you the complete directory. If you want I can invite you to the Gitlab repo

chri70 commented 2 years ago

Any news about this problem ? @jaystacey I see in you deploy log "You can now deploy .output/public to any static hosting!", is it a Nuxt 3 project ?

jaystacey commented 2 years ago

yep, using nuxt3 3.0.0-rc.10, coincidence that we are both using nuxt and facing this issue?

chri70 commented 2 years ago

I'm using nuxt3 3.0.0-rc.11. I made a little test by generating with nuxi generate in my local environment, then add .output/public folder to my git repo, I used npm ci instead of nuxt genreate in the build script in CF deployment settings to get dependencies, and guess what ... the scripts of functions folder was correctly compiled and the routes are available.

chri70 commented 2 years ago

I restore the generation on CF side, and I list files of functions folder before and after generation. Before nuxt generate (----ls-before line in log), functions folder is ok, but after generation it does not exist anymore (----ls-after line in log). I think it's not a wrangler problem but a Nuxt or Nitro issue instead

<html>
<body>
<!--StartFragment-->

16:14:24.368 | > cfgenerate
-- | --
16:14:24.368 | > echo ----ls-before && ls functions -l -R && nuxt generate && echo ----ls-after && ls functions -l -R
16:14:24.368 |  
16:14:24.386 | ----ls-before
16:14:24.399 | functions:
16:14:24.399 | total 5
16:14:24.399 | drwxr-xr-x 2 buildbot nogroup 4096 Oct  5 14:13 api
16:14:24.399 | -rw-r--r-- 1 buildbot nogroup  158 Oct  5 14:13 tsconfig.json
16:14:24.399 |  
16:14:24.399 | functions/api:
16:14:24.400 | total 5
16:14:24.400 | -rw-r--r-- 1 buildbot nogroup 3729 Oct  5 14:13 contact.ts
16:14:24.400 | -rw-r--r-- 1 buildbot nogroup  325 Oct  5 14:13 index.ts
16:14:24.499 | Nuxi 3.0.0-rc.11
16:14:24.519 | Nuxt 3.0.0-rc.11 with Nitro 0.5.4
16:14:31.237 | ℹ Client built in 3614ms
16:14:31.239 | ℹ Building server...
16:14:32.888 | ✔ Server built in 1649ms
16:14:32.953 | ✔ Generated public .output/public
16:14:32.954 | ℹ Initializing prerenderer
16:14:34.029 | ℹ Prerendering 3 initial routes with crawler
16:14:34.169 | ├─ / (140ms)
16:14:34.180 | ├─ /mentions-legales (11ms)
16:14:34.183 | ├─ /200.html (2ms)
16:14:34.184 | ├─ /_payload.js (1ms)
16:14:34.186 | ├─ /mentions-legales/_payload.js (2ms)
16:14:34.188 | ✔ You can now deploy .output/public to any static hosting!
16:14:34.209 | ----ls-after
16:14:34.223 | functions:
16:14:34.224 | total 0
16:14:34.237 | Finished
16:14:34.817 | 🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
16:14:34.825 |  
16:14:34.929 | ✘ [ERROR] No routes found when building Functions directory: /opt/buildhome/repo/functions

<!--EndFragment-->
</body>
</html>
chri70 commented 2 years ago

Finally, Nitro seems to detect the deployment environment (see Zero-Config Providers doc), and select the right preset (in this case cloudflare_pages I presume). In the Nitro clouldflare.ts, we can see :

...
output: {
    serverDir: '{{ rootDir }}/functions'
},
...

The server directory /functions folder is emptied before each Nitro prerender. I suppose that Nuxt 3 /server folder should be used as source of functions folder but there's some issues.

I fixed the problem by setting the environment variable NITRO_PRESET=node-server in CF settings.

jaystacey commented 2 years ago

@chri70 great find, can confirm that NITRO_PRESET=node-server resolves issue with no obvious drawbacks.

fago commented 2 years ago

@chri70 great find, can confirm that NITRO_PRESET=node-server resolves issue with no obvious drawbacks.

@jaystacey seems fine, but if that does not work with the preset "cloudflare_pages" I'd consider the bug still unresolved - so let's re-open this?

fago commented 2 years ago

uhm, this should be a nitro bug though

luc122c commented 1 year ago

Hi, sorry to jump on an old issue, but I had this same problem and came across a different solution. I thought it would be worth sharing for anyone else stumbling across this topic.

Rather than setting the envrionment variable, I added the following to my nuxt.config.ts:

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  nitro: {
    preset: "cloudflare_pages",
  },
});

Also, it's important that you run nuxt build rather than nuxt generate. When you choose the Nuxt preset, it chooses npm run generate as the default build command and /dist as the output directory.

After setting the build command to yarn build and the output directory to .output/public and adding the config above, it built successfully and I could see the SSR activity thanks to the new real-time logs.

PizzaConsole commented 1 year ago

@luc122c

Also, it's important that you run nuxt build rather than nuxt generate. When you choose the Nuxt preset, it chooses npm run generate as the default build command and /dist as the output directory.

Unfortunately that does not help those of us that that DO NOT want SSR, but still want the Cloudflare functions. With is preset you should be able to do either a SPA or SSR. nuxt build is for SSR and nuxt generate is for static/SPA. Currently it will not create the Cloudflare functions if you use nuxt generate

PizzaConsole commented 1 year ago

@luc122c You could however use the prerender config to generate the static html and then run nuxt build, works for me because I only have one page... would not be great with 10+

    nitro: {
        preset: 'cloudflare-pages',
        prerender: {
            ignore: ['/404'],
            routes: ['/']
        },
        serveStatic: true
    }

Plus, this bug still needs to get fixed for server routes to work properly... https://github.com/unjs/nitro/issues/497

cjsinnbeck commented 1 year ago

(Edit: Forgot to add that I intended to ask @luc122c about this - thanks for any input!)

I am a bit confused by this issue: Is the intended behaviour that both the native /functions folder from Cloudflare Pages and the /server/api/ folder from Nuxt 3 are supposed to work in parallel. Or will the /server folder from Nuxt be "transformed" into the Cloudflare Pages /functions folder?

By setting the build step to nuxt build && ls functions and setting the Nitro preset to "cloudflare_pages" I have success with putting my API functions in /server/api, and I can see that the functions folder is then created and populated with two files during deployment on Cloudflare Pages:

03:19:44.892 | functions:
03:19:44.892 | total 702
03:19:44.892 | -rw-r--r-- 1 buildbot nogroup 305165 Feb 26 02:19 [[path]].js
03:19:44.893 | -rw-r--r-- 1 buildbot nogroup 412466 Feb 26 02:19 [[path]].js.map

However, I cannot get it to work by simply creating the /functions folder in my Nuxt project and adding a HelloWorld function as described on https://developers.cloudflare.com/pages/platform/functions/get-started/ - when I deploy, I get this:

03:03:00.842 | Found Functions directory at /functions. Uploading.
03:03:01.419 |  
03:03:01.523 | ✘ [ERROR] No routes found when building Functions directory: /opt/buildhome/repo/functions
03:03:01.523 |  
03:03:01.523 |  
03:03:01.524 | If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
03:03:01.535 | Warning: Wrangler did not find routes when building functions. Skipping.

I am mostly curious if I am misunderstanding how this is supposed to work. Thanks in advance!

trapcodeio commented 1 year ago

I am facing this error. Any solution yet?

jaystacey commented 1 year ago

Appears that there is mroe to this issue, reopening. @trapcodeio can you please post nuxt version and reproduction code.

Skmt3P commented 1 year ago

I can do nothing...

 ⛅️ wrangler 3.0.1
------------------
wrangler dev now uses local mode by default, powered by 🔥 Miniflare and 👷 workerd.
To run an edge preview session for your Worker, use wrangler dev --remote
⎔ Starting local server...
service core:user:worker: Uncaught TypeError: Cannot read properties of undefined (reading 'split')
  at ivsvnonyb4d.js:4962:32
✘ [ERROR] MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above.
jadechip commented 1 year ago

(Edit: Forgot to add that I intended to ask @luc122c about this - thanks for any input!)

I am a bit confused by this issue: Is the intended behaviour that both the native /functions folder from Cloudflare Pages and the /server/api/ folder from Nuxt 3 are supposed to work in parallel. Or will the /server folder from Nuxt be "transformed" into the Cloudflare Pages /functions folder?

By setting the build step to nuxt build && ls functions and setting the Nitro preset to "cloudflare_pages" I have success with putting my API functions in /server/api, and I can see that the functions folder is then created and populated with two files during deployment on Cloudflare Pages:

03:19:44.892 | functions:
03:19:44.892 | total 702
03:19:44.892 | -rw-r--r-- 1 buildbot nogroup 305165 Feb 26 02:19 [[path]].js
03:19:44.893 | -rw-r--r-- 1 buildbot nogroup 412466 Feb 26 02:19 [[path]].js.map

However, I cannot get it to work by simply creating the /functions folder in my Nuxt project and adding a HelloWorld function as described on https://developers.cloudflare.com/pages/platform/functions/get-started/ - when I deploy, I get this:

03:03:00.842 | Found Functions directory at /functions. Uploading.
03:03:01.419 |  
03:03:01.523 | ✘ [ERROR] No routes found when building Functions directory: /opt/buildhome/repo/functions
03:03:01.523 |  
03:03:01.523 |  
03:03:01.524 | If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
03:03:01.535 | Warning: Wrangler did not find routes when building functions. Skipping.

I am mostly curious if I am misunderstanding how this is supposed to work. Thanks in advance!

Would really like some clarity regarding this as well.

CarmenPopoviciu commented 6 months ago

hi folks, can anyone confirm whether you're still experiencing this issue with latest wrangler version? And if so, a repro repository would be much appreciated. Thank you <3

BallisticPain commented 2 months ago

I am experiencing this issue. I'm not using Nuxt... I was using Hono for the Functions. I initially had the functions directory in the src/functions directory, but that's the src directory is the "output" directory.

I moved that up to be /functions and it found the functions. However, it did not auto-generate any routes. So when I submit a post to that URL I get a 405 Method not Allowed error.

I couldn't figure out where to put my custom _routes.json file since I was trying to get it to work. I only need Functions to pay attention when going to the /api/* route. Everything else is static. I finally figured out it recognized it when I placed it at src/_routes.json.

I have the code on Gitlab. It is a private repository, but I don't mind providing access to the repository if it would be helpful. It's a fully static site where I'm trying to make a contact form submit data through a Cloudflare Worker to the Mailgun RESTful API. I never get to the Worker code executed due to the 405 mentioned above.

I did see someone mention that the onRequest needs to be exported. I'm exporting the whole of the api variable at the bottom of the file. Maybe I need Hono to do a specific build command?

Here's my one file located at /functions/api/contact.ts

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import {util, z} from 'zod';
import { zValidator } from "@hono/zod-validator";
import objectKeys = util.objectKeys;

type Bindings = {
    MAILGUN_API_KEY: string;
    MAILGUN_BASE_URL: string;
    MAILGUN_SENDER_DOMAIN: string;
    TURNSTILE_SECRET_KEY: string;
}

const api = new Hono<{ Bindings: Bindings }>().basePath('/api');

const formSubmission = z.object({
    name: z.string(),
    email: z.string().email(),
    subject: z.string(),
    body: z.string(),
    "cf-turnstile-response": z.string(),
});

api.use('/*', logger());
api.use('/*', cors());
api.post('/contact', zValidator('form', formSubmission), async (c, next) => {
    const secret = c.env.TURNSTILE_SECRET_KEY;
    const ip = c.req.header('CF-Connecting-IP') || "";
    const formValid = c.req.valid('form')
    const token = formValid['cf-turnstile-response'];

    const errors: {[key: string]: string[]} = {}
    const requiredFields = ['name', 'email', 'subject', 'body'];
    // Validate data from the form
    // Generic validation to make sure required fields have some sort of non-empty value
    // Shouldn't need this as the site is checking for empty values before sending the form data to this service
    for (let field in formValid) {
        if (requiredFields.includes(field)) {
            const formKey = field as keyof typeof formValid;
            if (formValid[formKey] === undefined || formValid[formKey] === null || formValid[formKey] === '') {
                errors[field] = errors[field] || [];
                errors[field].push('This field is required!');
            }
        }
    }

    // We have error(s)
    if (Object.keys(errors).length > 0) {
        // HTTP_400: Bad Request
        return c.json(errors, 400);
    }

    // Validate Turnstile before Sending Email
    if (! await validateToken(ip, token, secret)) {
        // HTTP_400: Bad Request
        return c.text('Invalid humanity check!', 400);
    }

    // console.log('Form Data', formData);
    console.log('IP:', ip);
    console.log('Errors', errors);
    console.log('Form Valid', formValid);

    return c.text('Email sent. Thank you for reaching out to us!');
});

async function validateToken(ip: string, token: string, secret: string) {
    const formData = new FormData();
    formData.append('secret', secret);
    formData.append('response', token);
    formData.append('remoteip', ip);

    const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
    const result = await fetch(url, {
        body: formData,
        method: 'POST',
    });

    const outcome = await result.json();
    return outcome.success;
}

export default api

Here's the output from my most recent build...

2024-09-13T21:00:47.364587Z Cloning repository...
2024-09-13T21:00:49.128534Z warning: redirecting to https://gitlab.com/anchorarm/website.git/
2024-09-13T21:00:49.129144Z From https://gitlab.com/anchorarm/website
2024-09-13T21:00:49.129263Z  * branch            e5c29d5a321f25622c7ce27408b0753bd7b10565 -> FETCH_HEAD
2024-09-13T21:00:49.129422Z 
2024-09-13T21:00:49.188286Z HEAD is now at e5c29d5 Moved routes again (1)
2024-09-13T21:00:49.188841Z 
2024-09-13T21:00:49.275746Z 
2024-09-13T21:00:49.276365Z Using v2 root directory strategy
2024-09-13T21:00:49.300358Z Success: Finished cloning repository files
2024-09-13T21:00:51.036517Z Checking for configuration in a wrangler.toml configuration file (BETA)
2024-09-13T21:00:51.037145Z 
2024-09-13T21:00:51.146803Z No wrangler.toml file found. Continuing.
2024-09-13T21:00:51.227567Z No build command specified. Skipping build step.
2024-09-13T21:00:51.228514Z Found Functions directory at /functions. Uploading.
2024-09-13T21:00:51.236126Z  ⛅️ wrangler 3.60.1
2024-09-13T21:00:51.236367Z -------------------
2024-09-13T21:00:52.128881Z 
2024-09-13T21:00:52.221308Z ✘ [ERROR] No routes found when building Functions directory: /opt/buildhome/repo/functions
2024-09-13T21:00:52.221629Z 
2024-09-13T21:00:52.221772Z 
2024-09-13T21:00:52.225495Z 🪵  Logs were written to "/root/.config/.wrangler/logs/wrangler-2024-09-13_21-00-51_921.log"
2024-09-13T21:00:52.237394Z Warning: Wrangler did not find routes when building functions. Skipping.
2024-09-13T21:00:52.237776Z Found _routes.json in output directory. Uploading.
2024-09-13T21:00:52.250739Z Validating asset output directory
2024-09-13T21:00:54.118569Z Deploying your site to Cloudflare's global network...
2024-09-13T21:00:56.363975Z Uploading... (203/203)
2024-09-13T21:00:56.364728Z ✨ Success! Uploaded 0 files (203 already uploaded) (0.57 sec)
2024-09-13T21:00:56.364846Z 
2024-09-13T21:00:56.620314Z ✨ Upload complete!
2024-09-13T21:00:58.756997Z Success: Assets published!
2024-09-13T21:01:00.097997Z Success: Your site was deployed!
chri70 commented 2 months ago

Hi @BallisticPain, I think that Hono does not use the /functions folder since hono is in charge of route handling. The backend script is managed by generating a _worker.js file (even in Cloudflare Pages environment).
See these topics :

penalosa commented 1 week ago

We haven't heard any updates here in a while so I'm going to close this issue for now. If anyone is still running into problems feel free to open a new issue with more details and we can investigate further.

@BallisticPain I think in your specific case this is an issue with your Hono setup—you should be able to follow the guides that @chri70 linked to setup & deploy your site.