Skip to content

Conversation

@xandris
Copy link
Contributor

@xandris xandris commented Oct 18, 2025

I completely messed up my commit history somehow. Take two...

single mode generates these files:

.
├── endpoints.context.ts
├── endpoints.handlers.ts
├── endpoints.ts
├── endpoints.validator.ts
└── endpoints.zod.ts

But the contexts file has this import:

import { ListPetsParams,
CreatePetsParams,
CreatePetsBody } from './endpoints.schemas';

When in reality the TypeScript types live in endpoints.ts.

tags mode generates these files:

.
├── endpoints.schemas.ts
├── endpoints.validator.ts
├── health.context.ts
├── health.handlers.ts
├── health.ts
├── pets.context.ts
├── pets.handlers.ts
├── pets.ts
└── pets.zod.ts

But strangely, an absolute import:

import {
  ListPetsParams,
  CreatePetsParams,
  CreatePetsBody
} from 'endpoints.schemas';

Refactor the hono client to prevent these sorts of issues.

First, introduce a helper that generates import paths. Prefer computing the relative path between absolute paths. Callers must know the importer and importee paths but otherwise shouldn't perform path manipulation even if the relationship between the two is supposedly known.

With path handling abstracted, this lets us remove some duplicated code. Much of the duplication relates to mode handling, with the difference being the import paths in the code templates. Split generateHandlers into generateHandlerFiles and generateHandlerFile. generateHandlerFiles decides how to group verbs into files and defers code generation to generateHandlerFile. split generateContext and generateHandlers similarly.

Remove some duplicate path computation. generateZvalidator reports its output path, feed that to generateHandlerFiles. Compute the path to the schemas in generateExtraFiles because its location doesn't vary per tag split.

Externalize the zValidator implemention. It's static and outlining it makes the code easier to read.

Use type imports in the orval code to satisfy the TypeScript compiler.

Generate type imports where we can; it's a good practice.

Use a Set to avoid a quadratic unique algorithm. As a side effect, generate sorted imports.

I notice there are a lot of uses .reduce that mutate the accumulator and .flatMap that .push into an array; these are usually clearer as a traditional for-loop.

`single` mode generates these files:

```text
.
├── endpoints.context.ts
├── endpoints.handlers.ts
├── endpoints.ts
├── endpoints.validator.ts
└── endpoints.zod.ts
```

But the contexts file has this import:

```typescript
import { ListPetsParams,
CreatePetsParams,
CreatePetsBody } from './endpoints.schemas';
```

When in reality the TypeScript types live in `endpoints.ts`.

`tags` mode generates these files:

```text
.
├── endpoints.schemas.ts
├── endpoints.validator.ts
├── health.context.ts
├── health.handlers.ts
├── health.ts
├── pets.context.ts
├── pets.handlers.ts
├── pets.ts
└── pets.zod.ts
```

But strangely, an absolute import:

```typescript
import {
  ListPetsParams,
  CreatePetsParams,
  CreatePetsBody
} from 'endpoints.schemas';
```

Refactor the hono client to prevent these sorts of issues.

First, introduce a helper that generates import paths. Prefer computing the relative path between
absolute paths. Callers must know the importer and importee paths but otherwise shouldn't perform
path manipulation even if the relationship between the two is supposedly known.

With path handling abstracted, this lets us remove some duplicated code. Much of the duplication
relates to mode handling, with the difference being the import paths in the code templates.
Split `generateHandlers` into `generateHandlerFiles` and `generateHandlerFile`.
`generateHandlerFiles` decides how to group verbs into files and defers code generation to
`generateHandlerFile`. split `generateContext` and `generateHandlers` similarly.

Remove some duplicate path computation. `generateZvalidator` reports its output path, feed that to
`generateHandlerFiles`. Compute the path to the schemas in `generateExtraFiles` because its location
doesn't vary per tag split.

Externalize the zValidator implemention. It's static and outlining it makes the code easier to read.

Use type imports in the orval code to satisfy the TypeScript compiler.

Generate type imports where we can; it's a good practice.

Use a Set to avoid a quadratic unique algorithm. As a side effect, generate sorted imports.

I notice there are a lot of uses `.reduce` that mutate the accumulator and `.flatMap` that `.push`
into an array; these are usually clearer as a traditional for-loop.

Signed-off-by: Alexandra Parker <alexandra.i.parker.civ@us.navy.mil>
@melloware melloware added the hono Hono related issue label Oct 18, 2025
@melloware melloware merged commit 3e1a6ba into orval-labs:master Oct 18, 2025
2 checks passed
@melloware melloware added this to the 7.14.0 milestone Oct 18, 2025
@melloware
Copy link
Collaborator

7.14.0 has been released if you want to verify all your changes!

@xandris
Copy link
Contributor Author

xandris commented Oct 20, 2025

thank you! i'll try it with our project at work tomorrow :)

@xandris
Copy link
Contributor Author

xandris commented Oct 21, 2025

@melloware looks good! i just ran my first two api calls through it and already found some spots where we don't conform to the API spec

one oddity i noticed at runtime is if a response body fails validation the server still returns 400 bad request. imo response validation failure should be 500 what do you think

@melloware
Copy link
Collaborator

Hmmm 500 is usually only for unexpected errors. 4xx is usually for validation errors I typically use 422

@xandris
Copy link
Contributor Author

xandris commented Oct 22, 2025

@melloware I wasn't familiar with 422 so I looked it up

https://www.rfc-editor.org/rfc/rfc9110#name-422-unprocessable-content

The 422 (Unprocessable Content) status code indicates that the server understands the content type of the request content (hence a 415 (Unsupported Media Type) status code is inappropriate), and the syntax of the request content is correct, but it was unable to process the contained instructions. For example, this status code can be sent if an XML request content contains well-formed (i.e., syntactically correct), but semantically erroneous XML instructions.

This makes sense for schema validation–it's valid JSON syntax but it doesn't conform to the schema semantics. If response validation fails, though, the client didn't do anything wrong. There may be no request body at all, and modifying the request is unlikely to fix the issue. The server's response generation is wrong and it requires the server to be reconfigured or updated.

In general, the 4XX family should be for client errors, but responses are something the server generates so it doesn't make sense to me to return a 4XX in that case.

@melloware
Copy link
Collaborator

Yep when you said validation I thought you meant like they submitted but "last name greater than 50 characters" etc is when I throw 422

If it's bad content like malformed JSON I would throw a 400 bad request.

I only throw 500 for unhandled database errors or disk space is full something a client can't do anything about or recover from. Truly an "internal server error"

At least that is what I have always done

zhu-hong pushed a commit to zhu-hong/orval that referenced this pull request Oct 29, 2025
`single` mode generates these files:

```text
.
├── endpoints.context.ts
├── endpoints.handlers.ts
├── endpoints.ts
├── endpoints.validator.ts
└── endpoints.zod.ts
```

But the contexts file has this import:

```typescript
import { ListPetsParams,
CreatePetsParams,
CreatePetsBody } from './endpoints.schemas';
```

When in reality the TypeScript types live in `endpoints.ts`.

`tags` mode generates these files:

```text
.
├── endpoints.schemas.ts
├── endpoints.validator.ts
├── health.context.ts
├── health.handlers.ts
├── health.ts
├── pets.context.ts
├── pets.handlers.ts
├── pets.ts
└── pets.zod.ts
```

But strangely, an absolute import:

```typescript
import {
  ListPetsParams,
  CreatePetsParams,
  CreatePetsBody
} from 'endpoints.schemas';
```

Refactor the hono client to prevent these sorts of issues.

First, introduce a helper that generates import paths. Prefer computing the relative path between
absolute paths. Callers must know the importer and importee paths but otherwise shouldn't perform
path manipulation even if the relationship between the two is supposedly known.

With path handling abstracted, this lets us remove some duplicated code. Much of the duplication
relates to mode handling, with the difference being the import paths in the code templates.
Split `generateHandlers` into `generateHandlerFiles` and `generateHandlerFile`.
`generateHandlerFiles` decides how to group verbs into files and defers code generation to
`generateHandlerFile`. split `generateContext` and `generateHandlers` similarly.

Remove some duplicate path computation. `generateZvalidator` reports its output path, feed that to
`generateHandlerFiles`. Compute the path to the schemas in `generateExtraFiles` because its location
doesn't vary per tag split.

Externalize the zValidator implemention. It's static and outlining it makes the code easier to read.

Use type imports in the orval code to satisfy the TypeScript compiler.

Generate type imports where we can; it's a good practice.

Use a Set to avoid a quadratic unique algorithm. As a side effect, generate sorted imports.

I notice there are a lot of uses `.reduce` that mutate the accumulator and `.flatMap` that `.push`
into an array; these are usually clearer as a traditional for-loop.

Signed-off-by: Alexandra Parker <alexandra.i.parker.civ@us.navy.mil>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hono Hono related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants