nuxt / content

The file-based CMS for your Nuxt application, powered by Markdown and Vue components.
https://content.nuxt.com
MIT License
3.07k stars 623 forks source link

Content API giving 404 on generate build #2062

Open Tragio opened 1 year ago

Tragio commented 1 year ago

Environment

Reproduction

https://github.com/Tragio/nuxt-content-repro

yarn generate && yarn preview first selects works, and subsequent selects return 404.

yarn dev / yarn build works just fine.

Describe the bug

Nuxt Content with filter works just fine on dev, but as soon I generate the build it stops working properly.

CleanShot 2023-05-10 at 12 41 17

Already tried all solutions provided here: https://github.com/nuxt/content/issues/1798

Additional context

No response

Logs

No response

CC: @farnabaz

farnabaz commented 1 year ago

You are generating a static website, which means that there will be no APIs. When you change the select input content will execute query and requests server to get the right data, but instead of data it receives 404.

You should enable the experimental clientdb feature to allow the content module to run queries inside the browser

export default defineNuxtConfig({
  content: {
    experimental: {
      clientDb: true
    }
  }
})
jeffreyvdhondel commented 1 year ago

You are generating a static website, which means that there will be no APIs. When you change the select input content will execute query and requests server to get the right data, but instead of data it receives 404.

You should enable the experimental clientdb feature to allow the content module to run queries inside the browser

export default defineNuxtConfig({
  content: {
    experimental: {
      clientDb: true
    }
  }
})

Having the same problem as @Tragio this experimental clientdb setting doesn't fix my problem still getting an 404.

Tragio commented 1 year ago

@farnabaz thank you very much for your reply 🙏 yes I have tried that before creating this issue.

By putting that in my code reproduction has exactly the same outcome, like @jeffreyvdhondel also has.

farnabaz commented 1 year ago

I see, Sorry for my partial explanation Currently module does not support hybrid mode, which means that you can only use it on full SSG or without SSR so if you want to use client-side querying, you need to disable SSR

export default defineNuxtConfig({
  ssr: false,
  content: {
    experimental: {
      clientDb: true
    }
  }
})

I can imagine this is not exactly what you want to do, But the hybrid feature is yet to be implemented, thats why clientDB also is in experimental mode

jeffreyvdhondel commented 1 year ago

But for what i understand we both don't want to use hybrid mode but just full static. But that doesn't pre-render component logic?

farnabaz commented 1 year ago

Content module creates a list out of all queries that executed in generation phase and store them as static files. But as you can see in this example, queries for Team A, Team B and Team C won't execute in generation. They will execute only if user select a value from select input. In situations like this, there is no way for module to guess the query.

jeffreyvdhondel commented 1 year ago

But if you look into to cached json file the data is there.

farnabaz commented 1 year ago

Indeed, cached json is just a list of all contents. In production (inside server API) it is used to find the corresponding files based on query. But in SSG there is no server API to find the contents. So all the queries should store as separate file in generation phase.

Tragio commented 1 year ago

@farnabaz so we can't use nuxi generate in any way, right? At the moment my requirement was to have a static server.

I tried nuxi generate with the following and the result was the same.

export default defineNuxtConfig({
  ssr: false,
  content: {
    experimental: {
      clientDb: true
    }
  }
})
tomhuntley commented 1 year ago

@Tragio it's working for me using these settings (note the casing on clientDB)

export default defineNuxtConfig({
  ssr: false,
  content: {
    experimental: {
      clientDB: true
    }
  }
})
jeffreyvdhondel commented 1 year ago

Yeah that was the issue :)

Tragio commented 1 year ago

Ahh it was a copy-paste issue 😋 for me it is also working. @farnabaz I was about to create a PR, but not sure if nitro.preset: 'service-worker' does anything for the static deployment. If it should be replaced for what is there or added 🤔 maybe would be better for you to update the docs 🙏

defineNuxtConfig({
  ssr: false,
  nitro: {
    preset: 'service-worker'
  }
})

by:

defineNuxtConfig({
  ssr: false,
  content: {
    experimental: {
      clientDB: true
    }
  }
})
semiaddict commented 1 year ago

I am facing the same issue with a search page that accepts user input via a text field to perform a search on the contents. The experimental clientDB fixes the issue, but degrades SEO as SSR has to be fully disabled.

It would be great to be able to force using the clientDB per query while still using SSR. I'll try to get this done for the project I'm working on.

philkaracho commented 1 year ago

I'm at the same stage as @semiaddict - I'm building a company website with mostly static content. Don't want to disable SSR globally but need to implement a search page with user input.

Would be great if someone came up with a solution.

semiaddict commented 1 year ago

@philkaracho, after looking at the code responsible for using the clientDB, it seems too big a trouble to get it working for my use case as the client-db code is not exported and can't be used outside the content module, and the corresponding composables are also not auto-imported.

Since I only needed this in the search page, I opted for adding the necessary data from the content in the search index. FYI, I am using MiniSearch, which allows adding extra data to the indexed content.

philkaracho commented 1 year ago

@semiaddict Thanks for getting back on this!

I went the same route: fetching all the content once and performing the search locally. MiniSearch looks promising, thanks for sharing that!

wilsonpinto commented 1 year ago

For someone interested, we fixed this issue in this way:

  1. create dynamic slug page [...slug].vue (https://content.nuxtjs.org/examples/navigation/use-route)

    <script setup>
    const { path } = useRoute();
    
    const { data } = await useAsyncData(`content-${path}`, () => {
    return queryContent().where({ _path: path }).findOne()
    })
    </script>
    
    <template>
    <main class="prose text-left">
      <ContentRenderer v-if="data" :value="data" />
    </main>
    </template>
  2. Create a module that scan the 'content' directory and dynamically incorporates its paths into the prerendering routes

import { defineNuxtModule } from '@nuxt/kit';
import fs from 'fs/promises';
import path from 'path';

async function listFilesInDirectory(directory: string, prefix = '') {
  const entries = await fs.readdir(directory, { withFileTypes: true });

  // Use map() to perform the following actions for each entry in the directory
  const files: any = await Promise.all(entries.map(async (entry) => {
    const fullPath = path.join(directory, entry.name);
    const relativePath = path.join(prefix, entry.name);

    if (entry.isDirectory()) {
      // If the entry is a directory, recursively list the files in it
      return listFilesInDirectory(fullPath, relativePath);
    } else {
      // If the entry is a file, remove the extension, prepend a slash, and remove number and dot prefix
      const ext = path.extname(entry.name);
      const nameWithoutExtension = path.basename(entry.name, ext);
      const nameWithoutNumberPrefix = nameWithoutExtension.replace(/^\d+\.\s/, '');

      // Check if the name ends with 'index', if so, replace it with '/'
      if (nameWithoutNumberPrefix === 'index') {
        return path.join('/', prefix, '/');
      }

      return '/' + path.join(prefix, nameWithoutNumberPrefix);
    }
  }));

  // Flatten the array and return it
  return Array.prototype.concat(...files);
}

export default defineNuxtModule({
  async setup(_moduleOptions, nuxt) {
    const paths = await listFilesInDirectory('./content');
    nuxt.hook('nitro:config', (nitroConfig) => {
      nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];

      nitroConfig.prerender.routes?.push(...paths);
    });
  }
});

I hope this helps, however I don't think it solves the initial reported problem ...

I'm sure the nuxt team is already working on a definitive solution

parsajiravand commented 1 year ago

If you are facing issues with generating a static HTML file and receiving a 404 error, you need to add one of your routes to the static generation in nuxt.config.ts. After doing this, all files will be generated correctly.

  export default defineNuxtConfig({
    generate: {
      routes: ["/test-route/test"], // one of routes getting 404
    },
   })
mrniamster commented 1 year ago

@parsajiravand how to make the content & pages render ? especially the slug.vue cases? for static hosting,.

mathijsfr commented 1 year ago

I have the following route rules:

routeRules: {
        '/**': { prerender: true },
        '/dashboard/**': { ssr: false }
    },

Everything works fine. However, when opening a blog, the blog doesn't load. It does load after a refresh. How can I fix this issue?

TeddyHuang-00 commented 1 year ago

I'm having similar issues where something like:

[nitro 17:41:40]   ├─ /api/_content/query/2w8OxZpsdg.1692308491351.json (47ms) (Error: [404] Document not found!)

showed up when running pnpm generate. And disabling SSR along with enabling clientDB solves the problem. But this also break the nuxt-image module and I'm not seeing any images or pictures. Any update or ideas on this problem?

davestewart commented 1 year ago

But this also break the nuxt-image module

This is pretty much my day-to-day experience with Nuxt. Something always seems to break.

It's really quite depressing when you've put so much work in to craft a website.

davestewart commented 1 year ago

FWIW on Netlify what worked for me was:

And FWIW Lighthouse performance shot up from mid 60s to 96 (98 on mobile!):

image

Site link here.

TeddyHuang-00 commented 1 year ago

I somehow get this problem solved, but with different ways in different versions of Nuxt.

In the latest Nuxt 3.7.3, the 404 error should point to a specific file that is missing from your content. Say, for me I have a content/blog directory for holding all the blog posts, and set up a pages/blog/[slug].vue for rendering the blog page and a pages/blog/index.vue to query content in that folder and render a index page. This should work fine in the Nuxt 3.7.3, but for some reason, in earlier versions of Nuxt, you might need want to try one of the two workarounds:

  1. Add a content/blog/index.md as a placeholder and manage to filter that out in pages/blog/index.vue
  2. Move pages/blog/index.vue to pages/blog.vue and also add a placeholder content/blog.md. You only need to make sure that the query path in your pages/blog.vue is set to /blog/ instead of /blog. (Note that this breaks at least in Nuxt 3.7.3 as it will make the child pages not accessible, but works in some earlier verions)
darioackermann commented 9 months ago

I'm having similar issues where something like:

[nitro 17:41:40]   ├─ /api/_content/query/2w8OxZpsdg.1692308491351.json (47ms) (Error: [404] Document not found!)

showed up when running pnpm generate. And disabling SSR along with enabling clientDB solves the problem. But this also break the nuxt-image module and I'm not seeing any images or pictures. Any update or ideas on this problem?

Have you tried this? https://image.nuxt.com/advanced/static-images

TeddyHuang-00 commented 9 months ago

I'm having similar issues where something like:

[nitro 17:41:40]   ├─ /api/_content/query/2w8OxZpsdg.1692308491351.json (47ms) (Error: [404] Document not found!)

showed up when running pnpm generate. And disabling SSR along with enabling clientDB solves the problem. But this also break the nuxt-image module and I'm not seeing any images or pictures. Any update or ideas on this problem?

Have you tried this? https://image.nuxt.com/advanced/static-images

Thanks for your suggestion. I didn't see that page at the time. But now I got the problem solved with static site generating so I would rather stick to it and won't bother pre-rendering the images. Thanks again anyway.

acrolink commented 7 months ago

FWIW on Netlify what worked for me was:

  • setting ssr to true
  • changing the Netlify build command to npm run build (vs generate)
  • removing the Netlify NITRO_PREFIX=netlify-edge environment variable

And FWIW Lighthouse performance shot up from mid 60s to 96 (98 on mobile!):

image

Site link here.

@davestewart And serving it using node .output/server/index.mjs ? Not as static HTML/JS ?