#Don't build non-existent route

23 messages · Page 1 of 1 (latest)

lost gulch
#

@ornate vale I'm using Storyblok for CMS and it does something peculiar. Where I want files (or pages/stories) organized and so put them in corresponding folder. What Storyblok does is, aparently still send a "root index" on to Astro as long as there are children pages inside folder despite my not having defined or created any page as root of the folder.

As a sample, I have a directory called "legal" in Storyblok. And then inside this directory, I have pages that outputs as "/legal/terms", "/legal/privacy", etc. In some cases, I'd created a page, say /legal and assign it as "root of the folder" as per Storyblok definition. In that case, /legal becomes an active route and is available.

But in this case, I have no such root file, but Storyblok still apparently sends it off to Astro so getStaticPaths includes a /legal as an existing route, which then 404's since there's no corresponding/matching route or page content.

That make sense?

So, how do I either prevent Storyblok from doing this across board, as in, not output a path at the root of a folder except a page is explicitly defined for such. Or, prevent Astro for building routes for which no page exists?

ornate valeBOT
#

Hi @lost gulch. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

I understand your question about preventing Astro from building routes for which no page exists, specifically in the context of using Storyblok as a CMS. This is indeed a tricky situation, but let's explore some possible solutions based on the information available in the Astro documentation.

  1. Excluding pages from being built:

Astro provides a way to exclude pages from being built by prefixing their names with an underscore (_). However, this solution is more applicable to local files rather than CMS-generated content. Excluding pages (https://docs.astro.build/en/guides/routing/#excluding-pages)

  1. Using dynamic routes with server-side rendering (SSR):

If you're using SSR mode in Astro, you can create a dynamic route that checks if the content exists before rendering. This way, you can handle non-existent routes more gracefully. Here's an example of how you might structure this:

---
// src/pages/legal/[...slug].astro
import { useStoryblokApi } from '@storyblok/astro'

const storyblokApi = useStoryblokApi();
const slug = Astro.params.slug;
let content;

try {
  const { data } = await storyblokApi.get(`cdn/stories/legal/${slug}`, {
    version: import.meta.env.DEV ? "draft" : "published",
  });
  content = data.story.content;
} catch (error) {
  return Astro.redirect('/404');
}
---

<html lang="en">
  <head>
    <title>Legal Page</title>
  </head>
  <body>
    <!-- Render your content here -->
  </body>
</html>

This approach will redirect to a 404 page if the content doesn't exist. On-demand rendering (https://docs.astro.build/en/guides/cms/storyblok/#on-demand-rendering)

  1. Custom handling in getStaticPaths:

If you're using static site generation, you can modify your getStaticPaths function to filter out routes that don't have corresponding content. Here's a conceptual example:

#
---
export async function getStaticPaths() {
  const storyblokApi = useStoryblokApi();
  const { data } = await storyblokApi.get('cdn/stories', {
    content_type: 'legal',
    version: import.meta.env.DEV ? "draft" : "published",
  });

  return data.stories
    .filter(story => story.content) // Only include stories with content
    .map(story => ({
      params: { slug: story.slug },
      props: { content: story.content },
    }));
}

const { content } = Astro.props;
---

<!-- Render your content here -->

This approach ensures that only pages with actual content are generated. Generating pages (https://docs.astro.build/en/guides/cms/storyblok/#generating-pages)

  1. Configuring Storyblok:

While not directly related to Astro, you might want to explore if there are any settings in Storyblok that can prevent it from sending empty folder roots. This would require consulting Storyblok's documentation or reaching out to their support.

In conclusion, the most robust solution would likely involve a combination of careful configuration in Storyblok and custom handling in Astro, either through SSR with dynamic routes or by filtering in getStaticPaths for static generation. The exact implementation would depend on your specific setup and requirements.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@past carbon Kapa.ai is still learning and improving, please let me know how I did by reacting below.

lost gulch
#

@ornate vale Apt, Kapa. Thanks

ornate valeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You're welcome! I'm glad I could help provide some useful information about handling routes in Astro when using Storyblok as a CMS. If you have any more questions about Astro, feel free to ask.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@past carbon Kapa.ai is still learning and improving, please let me know how I did by reacting below.

lost gulch
#

@ornate vale how would I modify my present filter to work with your direction

        .filter((link) => !link.is_folder)
        .map((link) => {
            return {
                params: { slug: link.slug === 'home' ? undefined : link.slug },
            }
        })```
ornate valeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To modify your current filter based on the suggestions I provided earlier, we can adapt it to ensure that only pages with actual content are generated, while also handling the special case for the 'home' slug. Here's how you could modify your code:

return links
  .filter((link) => !link.is_folder && link.content) // Only include non-folder links with content
  .map((link) => {
    return {
      params: { slug: link.slug === 'home' ? undefined : link.slug },
      props: { content: link.content } // Pass the content as props
    }
  });

This modification does the following:

  1. It keeps the filter for non-folder items (!link.is_folder).
  2. It adds an additional condition to check if the link has content (link.content). This assumes that Storyblok provides a content property for each link. If the property has a different name, adjust accordingly.
  3. In the map function, it now includes the content as part of the props. This allows you to access the content in your Astro component.

This approach is based on the suggestion for custom handling in getStaticPaths (https://docs.astro.build/en/guides/cms/storyblok/#generating-pages) from the Storyblok integration guide for Astro.

Remember that this is a conceptual adaptation. You might need to adjust it further based on the exact structure of the data you receive from Storyblok and your specific requirements. Also, ensure that you're handling the 'home' case appropriately in your Astro component, since its slug is set to undefined.

If you need to make any further adjustments or if you have any questions about implementing this in your Astro project, please feel free to ask!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@past carbon Kapa.ai is still learning and improving, please let me know how I did by reacting below.

lost gulch
#

@ornate vale can you look at the entire gsp and update, I seem to be getting 404s

export async function getStaticPaths() {
    const storyblokApi = useStoryblokApi()

    let links = await storyblokApi.getAll('cdn/links', {
        version: 'published',
    })
    links = Object.values(links).filter(
        (link) =>
            !(link.slug === 'settings') &&
            !(link.slug === 'legal') &&
            !link.slug.startsWith('global') &&
            !link.slug.startsWith('awards') &&
            !link.slug.startsWith('designations') &&
            !link.slug.startsWith('profiles') &&
            !link.slug.startsWith('category') &&
            !link.slug.startsWith('successes') &&
            !link.slug.startsWith('offices') &&
            !link.slug.startsWith('regions'),
    )

    return links
        .filter((link) => !link.is_folder && link.content)
        .map((link) => {
            return {
                params: { slug: link.slug === 'home' ? undefined : link.slug },
                props: { content: link.content },
            }
        })
}
ornate valeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Thank you for providing the full getStaticPaths function. I can see why you might be getting 404 errors. Let's analyze and modify the code to address potential issues:

export async function getStaticPaths() {
    const storyblokApi = useStoryblokApi()

    let links = await storyblokApi.getAll('cdn/links', {
        version: 'published',
    })

    links = Object.values(links).filter(
        (link) =>
            !(link.slug === 'settings') &&
            !(link.slug === 'legal') &&
            !link.slug.startsWith('global') &&
            !link.slug.startsWith('awards') &&
            !link.slug.startsWith('designations') &&
            !link.slug.startsWith('profiles') &&
            !link.slug.startsWith('category') &&
            !link.slug.startsWith('successes') &&
            !link.slug.startsWith('offices') &&
            !link.slug.startsWith('regions'),
    )

    const stories = await Promise.all(
        links.map(async (link) => {
            if (link.is_folder) return null;
            try {
                const { data } = await storyblokApi.get(`cdn/stories/${link.slug}`, {
                    version: 'published',
                });
                return data.story;
            } catch (error) {
                console.error(`Error fetching story for slug: ${link.slug}`, error);
                return null;
            }
        })
    );

    return stories
        .filter(Boolean)
        .map((story) => ({
            params: { slug: story.slug === 'home' ? undefined : story.slug },
            props: { story },
        }));
}

Here's what I've changed and why:

#
  1. We're now fetching the full story content for each link. This is because the links endpoint in Storyblok doesn't provide the full content, only metadata about the story Storyblok API (https://docs.astro.build/en/guides/cms/storyblok/#generating-pages).

  2. We use Promise.all to fetch all stories concurrently, which should be more efficient.

  3. We're catching and logging any errors that occur when fetching individual stories. This will help you identify any problematic slugs.

  4. We filter out any null results (which could be from folders or failed fetches) using .filter(Boolean).

  5. Instead of passing just the content, we're passing the entire story object as props. This gives you access to more data in your Astro component if needed.

This approach should resolve the 404 errors by ensuring that:

  • We're only generating pages for actual stories, not just links.
  • We're handling potential errors when fetching stories.
  • We're passing the full story data to your Astro component.

If you're still experiencing 404 errors after this change, it might be worth logging the stories array before the final return statement to see exactly what data you're working with. Also, ensure that your Astro component is correctly using the story prop that's being passed to it.

Remember to adjust your Astro component to use Astro.props.story instead of Astro.props.content if you were previously using the latter.

Storyblok & Astro Integration Guide (https://docs.astro.build/en/guides/cms/storyblok/)

Let me know if you need any further clarification or if you encounter any other issues!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@past carbon Kapa.ai is still learning and improving, please let me know how I did by reacting below.

lost gulch
#

@ornate vale I still 404 everywhere

ornate valeBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I apologize that you're still experiencing 404 errors everywhere. Let's try to troubleshoot this issue further. Based on the Astro documentation and our previous conversation, here are a few things we can check:

  1. Verify Storyblok API calls:
    First, let's make sure we're actually getting data from Storyblok. Add some console.log statements in your getStaticPaths function to see what's being returned:
export async function getStaticPaths() {
    const storyblokApi = useStoryblokApi()

    let links = await storyblokApi.getAll('cdn/links', {
        version: 'published',
    })
    console.log('Links from Storyblok:', links);

    // ... (rest of your filtering logic)

    const stories = await Promise.all(
        links.map(async (link) => {
            if (link.is_folder) return null;
            try {
                const { data } = await storyblokApi.get(`cdn/stories/${link.slug}`, {
                    version: 'published',
                });
                return data.story;
            } catch (error) {
                console.error(`Error fetching story for slug: ${link.slug}`, error);
                return null;
            }
        })
    );
    console.log('Fetched stories:', stories);

    // ... (rest of your function)
}
  1. Check your Astro configuration:
    Make sure your Astro configuration is correctly set up for Storyblok. In your astro.config.mjs file, you should have something like this:
import { defineConfig } from 'astro/config';
import storyblok from '@storyblok/astro';

export default defineConfig({
  integrations: [
    storyblok({
      accessToken: 'your-access-token',
      components: {
        // your component mappings
      },
    }),
  ],
});
#
  1. Verify your dynamic route file:
    Ensure that your dynamic route file (probably [...slug].astro) is in the correct location and is set up to handle the props you're passing:
---
const { story } = Astro.props;
---
<html>
  <head>
    <title>{story.name}</title>
  </head>
  <body>
    <!-- Render your story content here -->
  </body>
</html>
  1. Check for SSR vs SSG:
    If you're using server-side rendering (SSR), you might need to adjust your approach. The Storyblok Astro guide (https://docs.astro.build/en/guides/cms/storyblok/#on-demand-rendering) provides an example for SSR:
---
import { useStoryblokApi } from '@storyblok/astro'

const storyblokApi = useStoryblokApi();
const slug = Astro.params.slug;
let content;

try {
  const { data } = await storyblokApi.get(`cdn/stories/${slug}`, {
    version: import.meta.env.DEV ? "draft" : "published",
  });
  content = data.story.content;
} catch (error) {
  return Astro.redirect('/404');
}
---
<!-- Your template here -->
  1. Debug mode:
    Try running Astro in debug mode to get more information:
astro build --debug

This will provide more detailed logs which might help identify the issue.

If none of these steps resolve the issue, it would be helpful to see the exact error messages you're getting and any console output from the debugging steps above. The problem could be related to how Storyblok is set up, how Astro is configured, or how the routes are being generated.

Astro Troubleshooting Guide (https://docs.astro.build/en/guides/troubleshooting/)

#

Remember, 404 errors usually mean that Astro can't find the pages it's trying to render, so the key is to ensure that the pages you expect to be generated are actually being created during the build process.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: