Skip to content

Commit

Permalink
feat: Fixed the conformValidator to return an error response when a v…
Browse files Browse the repository at this point in the history
…alidation error occurs
  • Loading branch information
uttk committed Jul 25, 2024
1 parent df322f5 commit 07dc751
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 96 deletions.
61 changes: 29 additions & 32 deletions packages/conform-validator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,9 @@ app.post(
conformValidator((formData) => parseWithZod(formData, { schema })),
(c) => {
const submission = c.req.valid('form')
const data = submission.value

if (submission.status === 'success') {
const data = submission.value

return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
}

const res = c.json({ success: false, message: `Bad Request` }, 400)
throw HTTPException(400, { res })
return c.json({ success: true, message: `${data.name} is ${data.age}` })
}
)
```
Expand All @@ -56,18 +47,8 @@ app.post(
conformValidator((formData) => parseWithYup(formData, { schema })),
(c) => {
const submission = c.req.valid('form')

if (submission.status === 'success') {
const data = submission.value

return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
}

const res = c.json({ success: false, message: `Bad Request` }, 400)
throw HTTPException(400, { res })
const data = submission.value
return c.json({ success: true, message: `${data.name} is ${data.age}` })
}
)
```
Expand All @@ -90,22 +71,38 @@ app.post(
conformValidator((formData) => parseWithYup(formData, { schema })),
(c) => {
const submission = c.req.valid('form')
const data = submission.value
return c.json({ success: true, message: `${data.name} is ${data.age}` })
}
)
```

if (submission.status === 'success') {
const data = submission.value
## Custom Hook Option

return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
}
By default, `conformValidator()` returns a [`SubmissionResult`](https://github.com/edmundhung/conform/blob/6b98c077d757edd4846321678dfb6de283c177b1/packages/conform-dom/submission.ts#L40-L47) when a validation error occurs. If you wish to change this behavior, or if you wish to perform common processing, you can modify the response by passing a function as the second argument.

const res = c.json({ success: false, message: `Bad Request` }, 400)
throw HTTPException(400, { res })
```ts
app.post(
'/author',
conformValidator(
(formData) => parseWithYup(formData, { schema })
(submission, c) => {
if(submission.status !== 'success') {
return c.json({ success: false, message: 'Bad Request' }, 400)
}
}
),
(c) => {
const submission = c.req.valid('form')
const data = submission.value
return c.json({ success: true, message: `${data.name} is ${data.age}` })
}
)
```

> [!NOTE]
> if a response is returned by the Hook function, subsequent middleware or handler functions will not be executed. [see more](https://hono.dev/docs/concepts/middleware).
## Author

uttk <https://github.com/uttk>
Expand Down
8 changes: 7 additions & 1 deletion packages/conform-validator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type GetInput<T extends ParseFn> = T extends (_: any) => infer S
: never
: never

type GetSuccessSubmission<S> = S extends { status: 'success' } ? S : never

type ParseFn = (formData: FormData) => Submission<unknown> | Promise<Submission<unknown>>

type Hook<F extends ParseFn, E extends Env, P extends string> = (
Expand All @@ -29,7 +31,7 @@ export const conformValidator = <
in: {
form: { [K in keyof In]: FormTargetValue }
}
out: { form: Out }
out: { form: GetSuccessSubmission<Out> }
}
>(
parse: F,
Expand All @@ -46,6 +48,10 @@ export const conformValidator = <
}
}

if (submission.status !== 'success') {
return c.json(submission.reply(), 400)
}

c.req.addValidatedData('form', submission)

await next()
Expand Down
15 changes: 6 additions & 9 deletions packages/conform-validator/test/hook.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as z from 'zod'
import { Hono } from 'hono'
import { hc } from 'hono/client'
import { HTTPException } from 'hono/http-exception'
import { parseWithZod } from '@conform-to/zod'
import { conformValidator } from '../src'
import { vi } from 'vitest'
Expand All @@ -16,10 +15,6 @@ describe('Validate requests using a Valibot schema', () => {
})
const handlerMockFn = vi.fn((c) => {
const submission = c.req.valid('form')
if (submission.status !== 'success') {
throw new HTTPException()
}

const value = submission.value
return c.json({ success: true, message: `name is ${value.name}` })
})
Expand Down Expand Up @@ -47,19 +42,21 @@ describe('Validate requests using a Valibot schema', () => {
describe('When the hook return Response', () => {
it('Should return response that the hook returned', async () => {
const req = new Request('http://localhost/author', { body: new FormData(), method: 'POST' })
const res = await app.request(req)
const hookRes = hookMockFn.mock.results[0].value
const res = (await app.request(req)).clone()
const hookRes = hookMockFn.mock.results[0].value.clone()
expect(hookMockFn).toHaveReturnedWith(expect.any(Response))
expect(res.status).toBe(hookRes.status)
expect(await res.json()).toStrictEqual(await hookRes.json())
})
})

describe('When the hook not return Response', () => {
it('Should return response that the handler function returned', async () => {
const res = await client.author.$post({ form: { name: 'Space Cat' } })
const handlerRes = handlerMockFn.mock.results[0].value
const res = (await client.author.$post({ form: { name: 'Space Cat' } })).clone()
const handlerRes = handlerMockFn.mock.results[0].value.clone()
expect(hookMockFn).not.toHaveReturnedWith(expect.any(Response))
expect(res.status).toBe(handlerRes.status)
expect(await res.json()).toStrictEqual(await handlerRes.json())
})
})
})
32 changes: 14 additions & 18 deletions packages/conform-validator/test/valibot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status'
import * as v from 'valibot'
import { Hono } from 'hono'
import { hc } from 'hono/client'
import { HTTPException } from 'hono/http-exception'
import { parseWithValibot } from 'conform-to-valibot'
import { conformValidator } from '../src'

Expand All @@ -26,20 +25,14 @@ describe('Validate requests using a Valibot schema', () => {
conformValidator((formData) => parseWithValibot(formData, { schema })),
(c) => {
const submission = c.req.valid('form')

if (submission.status === 'success') {
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :<'
}`,
})
}

const res = c.json({ success: false, message: 'Bad Request' })
throw new HTTPException(400, { res })
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :3'
}`,
})
}
)

Expand Down Expand Up @@ -107,9 +100,12 @@ describe('Validate requests using a Valibot schema', () => {
expect(res.status).toBe(400)

const json = await res.json()
expect(json).toEqual({
success: false,
message: 'Bad Request',
expect(json).toMatchObject({
status: 'error',
error: {
name: ['Invalid type: Expected string but received undefined'],
age: ['Invalid type: Expected string but received undefined'],
},
})
})
})
32 changes: 14 additions & 18 deletions packages/conform-validator/test/yup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status'
import * as y from 'yup'
import { Hono } from 'hono'
import { hc } from 'hono/client'
import { HTTPException } from 'hono/http-exception'
import { parseWithYup } from '@conform-to/yup'
import { conformValidator } from '../src'

Expand All @@ -22,20 +21,14 @@ describe('Validate requests using a Valibot schema', () => {
conformValidator((formData) => parseWithYup(formData, { schema })),
(c) => {
const submission = c.req.valid('form')

if (submission.status === 'success') {
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :3'
}`,
})
}

const res = c.json({ success: false, message: 'Bad Request' })
throw new HTTPException(400, { res })
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :3'
}`,
})
}
)

Expand Down Expand Up @@ -103,9 +96,12 @@ describe('Validate requests using a Valibot schema', () => {
expect(res.status).toBe(400)

const json = await res.json()
expect(json).toEqual({
success: false,
message: 'Bad Request',
expect(json).toMatchObject({
status: 'error',
error: {
name: ['name is a required field'],
age: ['age is a required field'],
},
})
})
})
32 changes: 14 additions & 18 deletions packages/conform-validator/test/zod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status'
import * as z from 'zod'
import { Hono } from 'hono'
import { hc } from 'hono/client'
import { HTTPException } from 'hono/http-exception'
import { parseWithZod } from '@conform-to/zod'
import { conformValidator } from '../src'

Expand All @@ -22,20 +21,14 @@ describe('Validate requests using a Valibot schema', () => {
conformValidator((formData) => parseWithZod(formData, { schema })),
(c) => {
const submission = c.req.valid('form')

if (submission.status === 'success') {
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :<'
}`,
})
}

const res = c.json({ success: false, message: 'Bad Request' })
throw new HTTPException(400, { res })
const value = submission.value

return c.json({
success: true,
message: `${value.name} is ${value.age}, nickname is ${
value?.nickname || 'nothing yet :3'
}`,
})
}
)

Expand Down Expand Up @@ -103,9 +96,12 @@ describe('Validate requests using a Valibot schema', () => {
expect(res.status).toBe(400)

const json = await res.json()
expect(json).toEqual({
success: false,
message: 'Bad Request',
expect(json).toMatchObject({
status: 'error',
error: {
name: ['Required'],
age: ['Required'],
},
})
})
})

0 comments on commit 07dc751

Please sign in to comment.