Skip to content
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

feat: add generation of route input types #892

Closed
wants to merge 1 commit into from

Conversation

Blechlawine
Copy link

πŸ”— Linked issue

#841

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

Add generated types for inputs to routes. A more detailed explanation is in the issue #841.

Resolves #841

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Copy link
Collaborator

@danielroe danielroe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using a generic for defineEventHandler? We could have:

export default defineEventHandler<{ id: string }>(event => {
  const data = await readBody(event)
  // data is typed
})

This would need to be implemented in https://github.com/unjs/h3 (with backwards-compatible generic implementation), and then this PR could be (very slightly) updated to use the generic instead of exported Input type.

@Blechlawine
Copy link
Author

What about using a generic for defineEventHandler? We could have:

export default defineEventHandler<{ id: string }>(event => {
  const data = await readBody(event)
  // data is typed
})

This would be ideal, but how would we access the generic type through the type of the exported eventHandler?

@danielroe
Copy link
Collaborator

We can use a query along the lines of T extends EventHandler<infer Input> (pseudo-code, I'd have to check the name of the type).

@anuragkumar19
Copy link

anuragkumar19 commented Jan 30, 2023

@danielroe @Blechlawine

Hello,
I think, for type-safety we cannot trust that the data sent by the user is the same type we are inferring.

My thought is that it would be nice if we would expose a function eg-parseBody / validateBody which returns the parsed body. The function's return type will be inferred as the type of body

export const parseBody = async (event) => {
  const data = await readBody(event) // type of data is unknown

  const parsedData = parse(data) // do validation, use joi, yup, zod or manual

  // now parsed data has a type. Let's say : Input - It will be inferred
  return parsedData
}

export default defineEventHandler(event => {
  const parsedData = await readBody(event)
  // data is typed:`Input` and parsed
})

From this there will be one more benefit: we can assign type to the useFetch composable in nuxt to automatically infer type of input data as we do with response.

parseQuery would be easy to extend once one is implemented.

@Blechlawine
Copy link
Author

Blechlawine commented Jan 30, 2023

@anuragkumar19

Hello, I think, for type-safety we cannot trust that the data sent by the user is the same type we are inferring.

My thought is that it would be nice if we would expose a util eg-parseBody / validateBody which takes in a function that returns the parsed body. The function's return type will be inferred as the type of body

From this there will be one more benefit: we can assign type to the useFetch composable in nuxt to automatically infer type of input data as we do with response.

This was exactly my thinking, but I wanted to leave that up to the developer to decide if they want to validate the request body. A helper could easily be built around my original idea:

// defineZodRoute.ts (a custom file which should probably not be included in nitro)
import { z } from "zod";

function defineZodRoute<TInput, TOutput, TResponse>(
    input: z.ZodType<TOutput, z.ZodTypeDef, TInput>,
    resolver: (input: TOutput) => TResponse
) {
    return defineEventHandler(async (event) => {
        const body = await readBody(event);
        const parsedInput = input.safeParse(body);
        if (parsedInput.success) {
            return resolver(parsedInput.data);
        } else {
            return parsedInput.error;
        }
    });
}

export default defineZodRoute;
// routes/index.ts
import { z } from "zod";
import defineZodRoute from "../lib/defineZodRoute";

const validator = z.object({ greeting: z.string() });

export default defineZodRoute(validator, (input) => {
    return {
        hello: input.greeting,
    };
});

export type Input = z.input<typeof validator>;

@anuragkumar19
Copy link

anuragkumar19 commented Jan 30, 2023

@Blechlawine
Yeah. Right. Sorry, I missed some details in last comment. I was just trying to avoid explicit mention of types and any make it e2e type-safe with zod so developer will not shoot himself in foot. I wanted nuxt to automatically infer types as it infer response types.

@Hebilicious Hebilicious added upstream enhancement New feature or request labels Jun 30, 2023 — with Volta.net
@Hebilicious Hebilicious self-assigned this Jun 30, 2023
@Hebilicious Hebilicious self-requested a review June 30, 2023 19:29
@Hebilicious Hebilicious added the types label Jul 1, 2023 — with Volta.net
@pi0
Copy link
Member

pi0 commented Aug 6, 2023

Hi, dear @Blechlawine. Thanks for the nice write-up in #841 and this PR.

New typed events handlers in unjs/h3#417 will help to implement auto-type generation. First step is #1532

I am closing this one for maintenance but thanks again ❀️

@pi0 pi0 closed this Aug 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request types upstream
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Types for route inputs
5 participants