Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top-level await in script and / or nuxt-like server data fetching #3043

Closed
Gin-Quin opened this issue Dec 14, 2021 · 6 comments
Closed

Top-level await in script and / or nuxt-like server data fetching #3043

Gin-Quin opened this issue Dec 14, 2021 · 6 comments

Comments

@Gin-Quin
Copy link

Gin-Quin commented Dec 14, 2021

Describe the problem

The actual way SvelteKit loads data from server before loading the page has two defaults:

  1. it's very verbose 🗣️
  2. it's not type-safe ☠️

This is a typical example of server-side rendering with SvelteKit:

<script context="module" lang="ts">
  export async function load() {
    return {
      props: {
        foo: await fetch("api/foo") as string
      }
    }
  }
</script>

<script lang="ts">
export let foo: string
</script> 

So we've written a lot of code just to return a string, and it is absolutely not safe. You have to indicate the type first in the load function and then in the main script section.

Linters won't complain if we write something like this:

<script context="module" lang="ts">
  export async function load() {
    return {
      props: {
        foo: await fetch("api/foo") as number // changed my mind, now it's a number
      }
    }
  }
</script>

<script lang="ts">
export let foo: string // ouch, forgot to change the type here
</script> 

Describe the proposed solution

I won't describe a full proposed solution as I don't know about Svelte and SvelteKit internals, but instead I will share raw ideas that need refinement and further thinking.

With Vue and the script setup syntax, it is actually possible to use await in the main script section.

This wonderful feature brings that kind of syntax:

<script lang="ts">
  const foo = await fetch("api/foo") as number
</script>

Which is extremely concise and type-safe.

I know for sure this is not a small feature, it would need changes even in the Svelte internal parts, not just SvelteKit.

Just making await working will not be a magical solution ; there should be a way to prevent data-fetching first from server and then from client after hydration. To achieve this in a clean way, Nuxt 3 uses the useFetch composable:

<script setup lang="ts">
  const foo = useFetch("api/foo")
</script>

Which is not exactly the same approach as the await but quite close. Maybe SvelteKit should define its own useFetch / useServerLoader - or whatever it is named - utility function to deal with asynchronous server-side loading?

To deal with page parameters and similar stuff, I would use the same syntax as in client-side, ie with $page store. That simplifies things as you share one syntax for server and client.

I think this feature should be thought taking in consideration other issues about SvelteKit loading, as it might resolve them in an elegant way as well:

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

@rogueyoshi
Copy link

Top level await would at least be consistent with the newer versions of node but there might be design choices at play here

@Gin-Quin
Copy link
Author

Top-level await does not need newer versions of node since the code inside '<script>' is put inside a function.

@Gin-Quin Gin-Quin changed the title Make svelte script able to run asynchronous code Top-level await and / or nuxt-like server data fetching Dec 14, 2021
@Gin-Quin Gin-Quin changed the title Top-level await and / or nuxt-like server data fetching Top-level await in script and / or nuxt-like server data fetching Dec 14, 2021
@benmccann
Copy link
Member

Related: #2301

@bfanger
Copy link
Contributor

bfanger commented Jan 14, 2022

I'd love to see top-level await in Svelte, i think it would be a very elegant api to allow a streaming api and a great alternative to React's suspend.

export let params;
$: post = await fetch(`/blog/${params.slug}`)

it also unlocks using {#await} blocks on the server.


But although the usage is simple, the impact on the svelte codebase is non-trivial:

It moves the responsibilities of async behavior outside of the component:

  • who renders a spinner, and how?
  • who renders the error, and how?
  • should delay animations?
  • should delay routing and maybe more

It complicates things because mounting, #if, #each & #key are no longer synchronous.

Hydration is also affected, you don't want to see the server rendered page and go into a pending state on the client and reload everything again. So all top-level awaits should be preresolved in the hydration step and use the values from the server. I think this is doable. if hydratingPhase then return serialized result else evaluate the await expression
It should reject the promise if the resolved value could not be serialized.

Speaking of rejected promises, i'd suggest to only serialize the successful promises:

  • Serializing an error message might expose server details.
  • Show the pending state on server render, and retry the promise expression on the client. (like the current {#await} behavior)

That a lot of work, but it definitely on my wishlist for Svelte, just imagine how much it will improve working with promises.

@Rich-Harris
Copy link
Member

Using await in components for... something is a nice idea, but it's a change that would need to happen in Svelte itself, and would require a detailed RFC. It would at minimum need to be a Svelte 4 feature. Since we now have page endpoints (i.e. foo.js supplies props to foo.svelte, without a load function) the boilerplate issue is solved, and type safety is something that could be improved with #647 (we already have per-route types for params, we just need to do props as well), so I'll close this

@junaga
Copy link

junaga commented Apr 26, 2022

@Rich-Harris

Hi, total Noob here

This is a critically underestimated feature. Javascript Modules are the new default, used by every modern tool. (One of my personal favorites Firebase).

top-level await, together with the export and import statements are the defining characteristics of it.
please add it to your svelte 4 todo <3

cya

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants