question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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

See original GitHub issue

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

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:5
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
bfangercommented, 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.

2reactions
Gin-Quincommented, Dec 14, 2021

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Data Fetching · Get Started with Nuxt
Nuxt provides composables to handle data fetching within your ... <script setup>const { data: count } = await useFetch('/api/count')</script><template> Page ...
Read more >
Streamline your JavaScript code with top-level await
This app will fetch the top news data from a news API and render it locally in ... Top-level await does not work...
Read more >
Data Fetching - Nuxt
In Nuxt we have 2 ways of getting data from an API. We can use the fetch method or the asyncData method.
Read more >
ECMAScript: Top-level await | Saeloun Blog
Top -level await enables modules to act as big async functions. With top-level await, ECMAScript Modules (ESM) can await resources. Other modules ...
Read more >
Suspense | Vue.js
This includes components using <script setup> with top-level await expressions. ... export default { async setup() { const res = await fetch(.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found