Most idiomatic way to wrap fetch
in an API request utility
#5173
Replies: 3 comments 2 replies
-
I have had an almost similar situation, where the issue was more about logging what happened and to abstract some common situation like getting json, posting formdata etc, but the basic idea would still apply. In my code I have a couple of helper functions like: export function(fetch, url) {
console.log('GET', url);
return fetch(url).then(res => res.json());
}
export function postJson(fetch, url, data) {
console.log('POST', url);
return fetch(url, {
method: 'POST',
body: JSON.stringify(data)
}).then(res => res.json(());
} (note that in our API a POST would always return the inserted data back as a JSON, the logs would also be removed in production) usage can be a bit cumbersome but is fairly straightforward and not that different from using some library like axios: import { getJson } from '$lib/api-utils';
// same approach in regular code and in load functions and actually wherever
data = await getJson(fetch, path); |
Beta Was this translation helpful? Give feedback.
-
I checked your code: <script context="module">
import {Request} from '$lib/services/server/request';
export async function load({fetch, session}) {
const endpointRequest = new Request({fetch});
return {stuff: {endpointRequest}};
}
</script> and this code unfortunately does not work. If I have an address of type: and on this page I have navigation with consecutive page numbers (9, 10, 11, etc.) then the load() function contained in __layout is called only once, at the beginning, so I can't read subsequent data pages this way. The rest of the routing is done on the browser side. Did I misunderstand something? |
Beta Was this translation helpful? Give feedback.
-
The solution posted by @myieye in #9530 might interest you @georgecrawford |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
It's a common requirement that an application might want to make available an API request utility. A few of examples of how this might be used:
Authentication
Intercepting every call to
fetch
and setting anAuthorization: ...
header with a value stored somewhere in the client.Handling JSON
Intercepting every call to
fetch
and settingAccept: application/json
andContent-Type: application/json
headers, and automatically returningres.json()
where appropriate, in order to reduce repeated boilerplate code.GraphQL
Providing a GraphQL
query()
function which executes the correspondingPOST
request against an API, with correctly formed parameters, variables, headers, etc.There are a variety of places in which this utility might be used:
load()
function of ascript context=module
block, during SSRload()
function of ascript context=module
block, when executed in the browserscript
block in a Svelte component, perhaps as a result of a user interaction such as typing, submitting a form, etc.It's clear from the docs that during SSR, we need to use the
fetch
parameter as supplied toload()
to achieve the first of the above list, as it's the only form offetch
which contains information about browser cookies, and correctly constructs a relative URL.My question is how to construct such an API request utility so that it can be made available to all three of the scenarios above: SSR
load()
, browserload()
and during the component lifecycle.My current approach is something along these lines:
/routes/__layout.svelte
You can see that I'm using the
fetch
as supplied toload()
, passing it to myRequest
helper class, and making it available to other route handlers and during the component lifecycle by adding it tostuff
. However, there's a major problem with this. The SvelteKit implementation of fetch is designed to work during SSR, passing through headers from the browser request, and constructing a relative URL. However, by creating an instance of myRequest
class to be used downstream viastuff.endpointRequest
, I've created a closure over theurl
input toload_node()
. This means that if I usestuff.endpointRequest
later in the app's lifecycle, the fetch implementation resolves relative URLs against the original SSR URL, and not the current URL.Many of my routes make use of page endpoints, but some require custom-made
load()
functions. This is why I've added the above boilerplate to/routes/__layout.svelte
, so that I know it'll be executed once on the server and once on the client.As far as I can see it, one or more of the following must be true:
fetch
is only designed to be used in aload()
function, and not in the component lifecycle, and for the latter I should usewindow.fetch
instead (which will resolve relative URLs correctly against the current URL). If this is true, I feel it could be made clearer in the documentation. It'd also be a shame that I can't construct a singleRequest
helper which works in all the scenarios (unless I use something likethis.#fetch = browser ? window.fetch : fetch;
, which is my current hacky workaround).endpointRequest
instance and make it globally available to the three scenariosfetch
implementation resolves usingwindow.location
where it's available (i.e. in the browser)Have other people run into this problem? How do you solve it? Is this something that SvelteKit should fix for us, or perhaps recommend a userland solution? Thanks for any advice!
Beta Was this translation helpful? Give feedback.
All reactions