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
fetchand setting anAuthorization: ...header with a value stored somewhere in the client.Handling JSON
Intercepting every call to
fetchand settingAccept: application/jsonandContent-Type: application/jsonheaders, and automatically returningres.json()where appropriate, in order to reduce repeated boilerplate code.GraphQL
Providing a GraphQL
query()function which executes the correspondingPOSTrequest 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=moduleblock, during SSRload()function of ascript context=moduleblock, when executed in the browserscriptblock 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
fetchparameter as supplied toload()to achieve the first of the above list, as it's the only form offetchwhich 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
fetchas supplied toload(), passing it to myRequesthelper 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 myRequestclass to be used downstream viastuff.endpointRequest, I've created a closure over theurlinput toload_node(). This means that if I usestuff.endpointRequestlater 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:
fetchis only designed to be used in aload()function, and not in the component lifecycle, and for the latter I should usewindow.fetchinstead (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 singleRequesthelper which works in all the scenarios (unless I use something likethis.#fetch = browser ? window.fetch : fetch;, which is my current hacky workaround).endpointRequestinstance and make it globally available to the three scenariosfetchimplementation resolves usingwindow.locationwhere 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