Better typed `fetch`/`$fetch` and routes
Context, work, and explanation by @danielroe: https://github.com/unjs/nitro/pull/1532
We are thinking of improving (auto-generated) typed fetch. This requires adopting a new standard/reusable type utility for fetch, which @danielroe is working on. By using the new layer, we can improve performance and consistency and adopt it in several places instead of directly being in nitro core (benefiting nuxt, and in the next steps ofetch and h3 typed fetch support.)
Update: unjs/fetchdts is WIP project for better basis replacement.
Relevant linked issues: (some might be doable in next steps)
- #3507
- #3355
- #2689
- #2583
- #1937
- #1138
- #938
- #841
- #470
- https://github.com/nuxt/nuxt/issues/19077#issuecomment-2887519760
Improving fetch types would be an immense QOL improvement. I for one am very excited about this and am looking forward to more info as y'all push forward with it.
Will this also type readBody to match the json serialization similiar to https://github.com/unjs/nitro/pull/1002?
And will this support alternative serializer to maintain type safety similiar to https://nuxt.com/docs/getting-started/data-fetching#using-an-alternative-serializer?
Is this still planned? To my knowledge, it's one of the things gating Nuxt 4
Hi,
to type route files ([..].method.ts) should we use declare module '../relapth/of/the/file' {...} or there is a better way to apply types to a perticular file ?
@flapili I don't believe it's possible without writing a typescript plugin
@flapili I don't believe it's possible without writing a typescript plugin
Hi @danielroe , is it possible without any plugin in typescript you can, as example do in a referenced d.ts file
declare module "path/to/nuxt.config" {
function foo(a: 'a'): number
}
in the nuxt.config.ts
const x = foo('a')
Of course that will not handle errors at runtime, here foo not declared
@flapili that's great news - and must be new! the last time I tried it it did not work.
At least it work in nuxt (maybe something in the tsconfig make it work/ not a default feature ?)
hi, concretely what is possible / done / todo ?
Shouldn't we rewrite this issue first comment to make it a proper tracker ? or does github project page could be used to track (I don't know well this github feature) ?
currently it's hard to know this current state of each items cc @pi0 @danielroe
@flapili I'm actively working on this at the moment and will be publishing a new (type-only) fetch-dts library to handle this in a generic way that can be used by nitro/ofetch/nuxt under the hood. pooya rightly pinged me about it as it's been a while.
I am returning a branded type from my backend, something like this
type Role = number & { readonly __role: unique symbol };
export default defineEventHandler(() => {
return {role: 1 as Role};
})
in Nuxt it shows as
type Response = {
role: {
toString: {};
toFixed: {};
toExponential: {};
toPrecision: {};
valueOf: {};
toLocaleString: {};
readonly __role: unique symbol;
};
}
Hello, can't wait for this (really hoping to replace tRPC) !
Is there a current plan for body/queryParams validation ? Something like https://github.com/kevinmarrec/h3-typebox
Also, having to pass the response type at first in defineEventHandler prevents from using type inference, is there an alternative?
Maybe less important, but is there a plan for a "better" openapi swagger generation (https://nitro.build/config#openapi) ? With body/params and response types, would be awesome!
Thanks a lot for the great work :)
Is there a current plan for body/queryParams validation
There already is https://h3.unjs.io/examples/validate-data
It's been a few months, and I'm wondering what's the status on this? To be frank, I am having a difficult time with TypeScript performance on the front-end side as my project grows, and I'm not sure what are the best practices anymore if working exclusively with Nuxt / Nitro.
I assume my problems are related to this issue, as I experience TypeScript errors 2589 (Type instantiation is excessively deep and possibly infinite) and 2321 (Excessive stack depth comparing types) frequently, and I use the Nitro types FetchResult and SerializeObject (with Prisma's generated types, which adds more load, I'm sure) pretty extensively throughout my client code to try and match the expected inputs / outputs of my server code when defining refs and such.
I've considered using tRPC like mentioned above, but my project has reached a point where it would be a lot of refactoring, when I'm not even sure if it would fix the performance issues I'm having.
@Tenrys I was having issues with typescript performance a lot as well. It is still slow, but if you by any chance are using the vscode extension "Pretty Type Errors" try turning that off.
As I understand it, this issue is dependent on a bunch of upstream stuff, so I think it will eventually get there.
It's been a few months, and I'm wondering what's the status on this? To be frank, I am having a difficult time with TypeScript performance on the front-end side as my project grows, and I'm not sure what are the best practices anymore if working exclusively with Nuxt / Nitro.
I assume my problems are related to this issue, as I experience TypeScript errors 2589 (
Type instantiation is excessively deep and possibly infinite) and 2321 (Excessive stack depth comparing types) frequently, and I use the Nitro typesFetchResultandSerializeObject(with Prisma's generated types, which adds more load, I'm sure) pretty extensively throughout my client code to try and match the expected inputs / outputs of my server code when defining refs and such.I've considered using tRPC like mentioned above, but my project has reached a point where it would be a lot of refactoring, when I'm not even sure if it would fix the performance issues I'm having.
On my Nitro project, I started using shared types (ts and arktype) that are in shared package. Thus it's known what the API should return, but you have to change it when you want to change the response, which is fine in my opinion.
How about using something like this:
// server/api/user/[id].patch.ts
import { z } from "zod"; // zod used as an example
export default defineEventHandler({
validatedRouteParams: z.object({ id: z.coerce.number() }),
// validatedQueryParams: null,
validatedBody: z.object({ name: z.string(), age: z.number() }),
// response: z.object({ name: z.string(), age: z.number() }), // inferred?
// status: 200 // sucess status code of the response, default to 200
}, async ({ event, body, routeParams, queryParams }) => {
return body
})
This would make it possible for $fetch to typecheck body, route params and query params.
I think this could also be used to generate openapi swagger files/doc.
It would also be nice to have an option to use superjson for payload/reponse.
Regarding route params, we could also have them accessed like this:
$fetch("/user/[id]", {
routeParams: { id: 123 },
})
This would make passing urls to the $fetch function typesafe as well. (Inspired by openapi-typescript / nuxt-open-fetch)
Inspirations:
- https://trpc.io/
- https://www.zodios.org/ (https://www.zodios.org/docs/api/openapi)
- https://ez.robintail.cz/v23.4.0/quick-start
- https://github.com/Foundry376/express-zod-openapi-autogen
Edit: Another interesting project : https://orpc.unnoq.com/ (typesafe, openapi compatible - can be used to generate a swagger, validation, integrates with tanstack query, supports SSE and Streaming)
One small input from me.
When a project contains a file with "TS2321: Excessive stack depth comparing types" error, then nuxi typecheck also finishes with an error like this:
Found 2 errors in the same file, starting at: app/composables/useFetchPage.ts:9
ERROR Process exited with non-zero status (2)
at R._waitForOutput (/C:/Users/user/ws/project/node_modules/tinyexec/dist/main.js:530:13)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Object.run (/C:/Users/user/ws/project/node_modules/@nuxt/cli/dist/chunks/typecheck.mjs:70:9)
at async runCommand (/C:/Users/user/ws/project/node_modules/citty/dist/index.mjs:316:16)
at async runCommand (/C:/Users/user/ws/project/node_modules/citty/dist/index.mjs:307:11)
at async runMain (/C:/Users/user/ws/project/node_modules/citty/dist/index.mjs:445:7)
ERROR Process exited with non-zero status (2)
nuxi typecheck still correctly reports some other unrelated errors in other files, but I'm not sure if it reports all errors in the project or only those errors that are happened during the check before "Excessive stack depth" error occured and as soon as it occured, nuxi typecheck just terminates its execution (skipping checking of other files in the project). In other words, I'm not sure if there is no TS errors in my project because of this and how to check it without opening/checking files one-by-one.
I've had a similar issue like @xak2000, type errors only occur when I run a typecheck script, not within VS Code itself. Running bun --bun typecheck resolved this for me.
As mentionned in #470, we encountered this in the back using the latest nitro. We have like 25 api routes. I saw NitroFetchRequest displaying all our routes
Is this subject still WIP ?
The issues related to this are significantly impacting the development experience in Nuxt in larger projects. One of Nuxt's most valuable features is the inferred types for server routes, but issues like this cause tsserver to become extremely slow (which is already a concern even under normal conditions). For reference, see https://github.com/nuxt/nuxt/issues/33838
The only workaround that has been effective for us is removing the generated server route types. However, this isn't an ideal solution, as the types need to be removed again each time the server restarts or anything triggers type regeneration. It would be helpful to have an option to disable type generation for server routes (if such an option already exists, I haven't been able to locate it).
If there are any suggestions to make the development experience more manageable, I would greatly appreciate them.