Skip to content
This repository has been archived by the owner on May 2, 2024. It is now read-only.

Commit

Permalink
feat: implements useValidatedQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmarrec committed Jun 12, 2022
1 parent e6ed0b5 commit c6a07a0
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 19 deletions.
14 changes: 14 additions & 0 deletions src/body.ts
@@ -0,0 +1,14 @@
import type { Static, TSchema } from '@sinclair/typebox'
import { createError, useBody, CompatibilityEvent } from 'h3'
import { useValidator } from './utils'

export async function useValidatedBody<T extends TSchema> (event: CompatibilityEvent, schema: T) {
const body = await useBody(event)
const validate = useValidator().compile(schema)

if (!validate(body)) {
throw createError({ statusCode: 400, statusMessage: `body ${validate.errors[0].message}` })
}

return body as Static<T>
}
22 changes: 3 additions & 19 deletions src/index.ts
@@ -1,20 +1,4 @@
import type { Static, TSchema } from '@sinclair/typebox'
import { createError, useBody, CompatibilityEvent } from 'h3'
import Ajv from 'ajv'

const ajv = new Ajv({
keywords: ['kind', 'modifier']
})

export async function useValidatedBody<T extends TSchema> (event: CompatibilityEvent, schema: T) {
const body = await useBody(event)
const validate = ajv.compile(schema)

if (!validate(body)) {
throw createError({ statusCode: 400, statusMessage: `body ${validate.errors[0].message}` })
}

return body as Static<T>
}

export { Type } from '@sinclair/typebox'

export * from './body'
export * from './query'
14 changes: 14 additions & 0 deletions src/query.ts
@@ -0,0 +1,14 @@
import type { Static, TSchema } from '@sinclair/typebox'
import { createError, CompatibilityEvent, useQuery } from 'h3'
import { useValidator } from './utils'

export function useValidatedQuery<T extends TSchema> (event: CompatibilityEvent, schema: T) {
const query = useQuery(event)
const validate = useValidator().compile(schema)

if (!validate(query)) {
throw createError({ statusCode: 400, statusMessage: `query ${validate.errors[0].message}` })
}

return query as Static<T>
}
1 change: 1 addition & 0 deletions src/utils/index.ts
@@ -0,0 +1 @@
export * from './validator'
13 changes: 13 additions & 0 deletions src/utils/validator.ts
@@ -0,0 +1,13 @@
import Ajv from 'ajv'

let instance: Ajv

export function useValidator () {
if (!instance) {
instance = new Ajv({
keywords: ['kind', 'modifier']
})
}

return instance
}
2 changes: 2 additions & 0 deletions test/index.test.ts → test/body.test.ts
Expand Up @@ -19,13 +19,15 @@ describe('useValidatedBody', () => {

it('returns 200 OK if body matches validation schema', async () => {
app.use('/validate', async req => await useValidatedBody(req, bodySchema))

const res = await request.post('/validate').send({ required: true })

expect(res.status).toEqual(200)
})

it('throws 400 Bad Request if body does not match validation schema', async () => {
app.use('/validate', async req => await useValidatedBody(req, bodySchema))

const res = await request.post('/validate').send({})

expect(res.status).toEqual(400)
Expand Down
39 changes: 39 additions & 0 deletions test/query.test.ts
@@ -0,0 +1,39 @@
import supertest, { SuperTest, Test } from 'supertest'
import { describe, beforeEach, it, expect } from 'vitest'
import { createApp, App } from 'h3'
import { useValidatedQuery, Type } from '../src'

describe('useValidatedQuery', () => {
let app: App
let request: SuperTest<Test>

beforeEach(() => {
app = createApp({ debug: false })
request = supertest(app)
})

const querySchema = Type.Object({
required: Type.String()
})

it('returns 200 OK if query matches validation schema', async () => {
app.use('/validate', req => useValidatedQuery(req, querySchema))

const res = await request.get('/validate?required')

expect(res.status).toEqual(200)
})

it('throws 400 Bad Request if query does not match validation schema', async () => {
app.use('/validate', req => useValidatedQuery(req, querySchema))

const res = await request.get('/validate')

expect(res.status).toEqual(400)
expect(res.body).toEqual(
expect.objectContaining({
statusMessage: "query must have required property 'required'"
})
)
})
})

0 comments on commit c6a07a0

Please sign in to comment.