sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.72k stars 1.94k forks source link

Dynamic route parameter with SSR using static-adapter #2907

Closed endigo9740 closed 2 years ago

endigo9740 commented 2 years ago

Describe the bug

Here's the route components I've setup in my app and how I expect the URLs to resolve:

/routes/index.svelte -> / (homepage)
/routes/blog/index.svelte -> /blog
/routes/blog/[slug].svelte -> /blog/post-title-here

When I test this locally via npm run dev (or build + preview) everything works as expected. I can:

  1. I can navigate between paths using UI anchor tags
  2. Browse directly to each path in a new tab/window
  3. I can refresh the routes and remain on the desired page

Unfortunately, when I deploy to my server environment the blog post pages specifically have the following issues:

  1. If I navigate to the posts via a UI anchor tag -> works as expected
  2. If I try to copy/paste the full URL path into a new window/tab -> the homepage component shows instead
  3. If I try to browse to the post via the UI, then refresh -> same as number 2 above

NOTE: the URL I was trying to access remains in the URL bar in the browser, just visually the page shows the wrong content - the homepage instead of the slug/post page.

EDIT FYI the blog list is fetched dynamically from a headless Wordpress API. That means the blog list, the list of posts, will update and change over time. Likewise the blog post pages fetch the content of each page based on an ID provided as part of the route slug. I use SvelteKits module load() method along with a JS fetch call to retrieve the the content from the API, then rendered it on the [slug].svelte component page.

I'm assuming this may not be a SvelteKit-specific problem, but perhaps a bad configuration on my AWS server setup. Here's some details of how I configured the following:

Per Cloudfront, by default you will not be able to access subdirectories, which means SSR-mode cannot be used. However, after some research, I found that if you use the S3 -website- endpoint url instead of the auto-suggested S3 instance, then subdirectories will work as expected. Here's my source if you want to read more:

https://www.mark-gilbert.co.uk/serving-index-pages-from-non-root-locations-with-aws-cloudfront/

At this point everything is working on my site, minus the routing for the blog post pages. Any help or guidance is welcome. I'm happy to address any questions you might have.

Reproduction

As far as I'm aware the issue cannot be replicated using a local build of a SvelteKit app. For now I've linked directly to my site to show the issue in question.

These work. You can browse to each directly and refresh no problem:

These fail. Cannot browse to these URLs. Navigating to these pages from /blog, then refreshing, causes the homepage to show instead of the blog post content:

EDIT: I've reverted to SPA mode on our live site, so the issue will not be present currently.

Logs

No console errors are present when the route fails to resolve to the correct page.

System Info

System:
    OS: macOS 12.0.1
    CPU: (8) arm64 Apple M1
    Memory: 1.25 GB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.18.1 - /opt/homebrew/opt/node@14/bin/node
    npm: 6.14.15 - /opt/homebrew/opt/node@14/bin/npm
  Browsers:
    Chrome: 96.0.4664.55
    Safari: 15.1

Severity

annoyance

Additional Information

Here's what I'm seeing in the /dist directory after I build the app in SSR mode:

NOTE: showing only relevant files here:

index.html
/blog/index.html
dist/_app/pages/blog/_slug_.svelte-{hash}.js
dist/_app/pages/blog/index.svelte-{hash}.js

I'm kind of surprised not to see an /blog/slug.html or equivalent page.

For now I'm planning to work around this issue by using SPA mode. But I'd really love to use SSR if possible!

Conduitry commented 2 years ago

If Kit can't find links to your pages when crawling the site it won't render them, but you can give additional entry points. See https://kit.svelte.dev/docs#configuration-prerender

endigo9740 commented 2 years ago

Thanks for the quick response. I've deployed changes to the live site above using the following svelte.config.js:

import preprocess from 'svelte-preprocess';
import adapter from '@sveltejs/adapter-static';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://github.com/sveltejs/svelte-preprocess
    // for more information about preprocessors
    preprocess: preprocess({
        scss: {
            includePaths: ['./src/styles']
        },
    }),

    kit: {
        target: '#svelte',
        // https://github.com/sveltejs/kit/tree/master/packages/adapter-static
        adapter: adapter({
            pages: 'dist',
            assets: 'dist',
            fallback: null // 'index.html'
        }),
        prerender: {
            crawl: true,
            enabled: true,
            onError: 'fail'
        }
    }
};

export default config;

The documentation you linked is a bit thin, so perhaps I did something wrong here, but I'm not seeing any improvement. The issues described above persist.

I just realized I didn't include this information in the original post, so I'll edit to append this, but the blog list is fetched dynamically from a headless Wordpress API. That means the posts will update and change over time. Likewise the blog post pages fetch the content of each page based on an ID provided as part of the route slug. I use SvelteKits module load() method along with a JS fetch call to retrieve the the content from the API, then rendered it on the [slug].svelte component page.

Perhaps I misunderstand the concept, but doesn't this mean these pages can never be prerendered? As I understand it, prerendering happens at build time? Is that correct? Wouldn't prerendering the blog list/pages leave me with a stale blog that never updates?

ivands commented 2 years ago

I got the same problem. Why doesn't the frontend realize it should render the correct page?

endigo9740 commented 2 years ago

Sorry I should have updated this sooner, but I had the issue addressed via a post on Reddit. Essentially S3 is not equip to handle SSR. You need to use something like EC2 to properly handle this. If your only option is S3, then opt for the SPA approach for now.

https://www.reddit.com/r/aws/comments/rdclh8/comment/ho0icxp/?utm_source=share&utm_medium=web2x&context=3