Skip to content

Replace this.fetch with this.get #3

Closed
@Rich-Harris

Description

@Rich-Harris

Stumbled into a bit of a rabbit hole while adding prerendering to adapter-node — because this.fetch is expected to work exactly the same as fetch, we have to account for the possibility that someone will this.fetch('/some-static-file.json), which means that the prerenderer needs to know where the static files could live, which muddies the API somewhat.

It's also just a bit of an awkward API:

  • Sapper doesn't currently handle URLs relative to the requested page (though this could easily change, and has in fact changed in the current app-utils)
  • The whole res = await this.fetch(...); data = await res.json() dance is a bit of a pain, honestly
  • It's easy to forget that you need credentials: 'include' if you're using session
  • fetch supports non-GET methods, which don't make sense in the context of preload (or load, as the case may be)

I'd like to replace it with this.get:

<script context="module">
  export async function load({ params }) {
    return await this.get(`./${params.slug}.json`);
  }
</script>
  • Only works with server routes, not static files or external resources. Simpler
  • Accepts a URL (easiest way to identify server routes, which will of course continue to work with plain fetch), which can be relative to the requested page
  • Always includes credentials
  • Returns a promise for the body of the response. If the Content-Type is application/json, is JSON.parsed. If it's text/*, is returned as text. Otherwise is returned as an array buffer
  • Non-200 responses throw an error (augmented with status code), which is equivalent to calling this.error(status, error), except that you can catch and handle if necessary. For common cases, this eliminates the need to add error handling around the response returned from this.fetch

We could also expose get alongside the other app functions (goto et al) for use outside preload/load functions. (It would probably just throw in Node.) That's not necessary for this.get to have value though.

Of course there are still times when it's useful to be able to use fetch in preload/load — hn.svelte.dev fetches data from https://api.hnpwa.com, for example, and it's good to be able to do this isomorphically (rather than forcing the app to always connect to the app's own server, like ahem some frameworks). But if it's only to be used for external URLs, then there's no need to use this.fetch, we can just use regular fetch. We can polyfill it in Node with @rollup/plugin-inject and node-fetch (which already powers this.fetch). With this approach, many apps wouldn't need node-fetch at all, whereas currently it has to be included because of this.fetch.

This seems like all upside to me, except for the breakingness of the change. But if there was ever a time for breaking changes this is it. Thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions