-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Infer types when calling endpoints through fetch wrapper inside load functions #9732
Comments
I think it's hard and tricky to implement as typescript need to know where each endpoint type at compile time, so it's not possible to resolve types just from the provided url, but we would probably need to generate a type map something like: interface EndpointMap {
"/api/books": typeof import("routes/api/books/+server"),
} And then we could patch the fetch function type in load to retrieve types for the corresponding url(and method). This works great for simple api urls but does not handle dynamic urls. Also we should note that the Response interface used by fetch and svelte endpoints are not strictly typed but we could easily patch it: interface TypedResponse<T = any> extends Response {
json(): Promise<T>;
} So in your endpoints you will use TypedResponse instead of Response function typed_json<T>(x: T) {
return json(x) as TypedResponse<T>;
}
type Book = { title: string; author: string };
export async function GET() {
const data: Book[] = ...;
return typed_json(data);
} To extract types from TypedResponse you could use this helper types: // TypedResponse<T> -> T
type GetResponseType<R> = Awaited<R> extends TypedResponse<infer T> ? T : never;
// infer type of endpoint function
type GetEndpointType<S extends RequestHandler> = GetResponseType<ReturnType<S>> So now to get inferred types in load function you will simply cast the response of fetch: export async function load({ fetch }: PageLoadEvent) {
const books = (fetch('/api/books') as ReturnType<typeof import('../api/books/+server').GET>)
.then((res) => res.json())
.catch(() => {
throw error(500, 'Failed to fetch books');
});
return { books };
} |
To expand on @mhmd-22 ideas, here is how SvelteKit do typing for load functions: Current implementation:
|
Closed as duplicate of #647 |
@qurafi I came up with virtually the exact same idea independently, let me know what you think of my idea |
Describe the problem
It would increase ergonomics if the return type of fetch calls to custom endpoints (
+server.js
) was inferred inside load functions when using the providedfetch
function.I'm creating an application that needs to have a available APIs for integration purposes. I also have a database that can only be queried server side. Currently I can use my endpoints (
+server.js
) in myload
functions using the providedfetch
wrapper, and SvelteKit will automatically figure out if it should use HTTP to talk to the API, or just call the+server.js
directly (which is awesome!):However, as you can see, I have to type out the result of the fetch to my endpoint (
+server.js
). This is obviously not a huge deal, but it can get quite tedious and error prone when the application size grows.Describe the proposed solution
Ideally I would like the provided fetch wrapper inside load function (in
+page[.server].js
) to infer the type when querying internal endpoints.If I have this
+server.js
file insidesrc/routes/api/books/
:Then I would have these results in a
+page[.server].js
file:This raises some questions however. What will happen if you do
fetch("/api/books").then(res => res.formData())
? Will it error? And what if you want to do more fancy things inside your endpoint. For example provide JSON response only if some specific headers are set?Alternatives considered
It is possible to turn all
+page.js
files that uses the API into+page.server.js
files, and query the database directly. However, the downside of that is that it's quite easy for your APIs and your internal usage of the database to shift. By dogfooding the API, it's easier to maintain it over time.Another approach (which is how we are doing it currently) is to use tRPC as an abstraction layer:
A downside to this approach is that you get yet another abstraction layer, and you have to set up tRPC.
Importance
would make my life easier
Additional Information
No response
The text was updated successfully, but these errors were encountered: