Swagger 2.0 스펙은 바로 못 먹인다. OpenAPI 3으로 바꾼 다음 zod 클라이언트를 만든다.

{
  "scripts": {
    "convert": "swagger2openapi ./spec.json -o ./spec.yaml",
    "zod": "openapi-zod-client -a \"./spec.yaml\" -o \"./spec.ts\""
  }
}
#277

zodios는 ZodErrorZodiosError.cause에 담아 던진다 — instanceof z.ZodError로는 안 잡힌다.

if (err instanceof ZodiosError && err.cause instanceof ZodError) {
  console.log(fromZodError(err.cause).toString())
}
#278
import z from 'zod'

const envSchema = z.object({
  REACT_APP_FEATURE_VAC_ASK: z.string(),
  REACT_APP_FEATURE_RECORDS: z.string(),
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
})

const windowSchema = z.object({
   SOMETHING_COOL: z.string()
})

export const ENV = envSchema.parse(process.env)
export const WINDOW = windowSchema.parse(window)

#280

vitest에서 사용자 지정 assertion을 추가하여 Zod 스키마와 Response 객체를 비교하기

import { expect } from 'vitest'
import type { ZodTypeAny } from 'zod'

expect.extend({
  /**
   * @param received 테스트할 Response 객체
   * @param schema 검증할 Zod 스키마
   */
  async toMatchSchema(received: Response, schema: ZodTypeAny) {
    const response = await received.json()
    const result = await schema.safeParseAsync(response)

    return {
      message: () => '',
      pass: result.success,
    } satisfies ExpectationResult
  },
})

vitest.d.ts 파일에서 CustomMatchers 인터페이스를 확장하여 TypeScript와의 통합성을 유지.

import type { ZodTypeAny } from 'zod'

interface CustomMatchers<R = unknown> {
  toMatchSchema(schema: ZodTypeAny): Promise<R>
}

todoResponse의 응답 데이터가 todoSchema에 정의된 Zod 스키마와 일치하는지 확인한다.

test('todo', async () => {
  expect(todoResponse.ok).toBeTruthy()
  expect(todoResponse).toMatchSchema(todoSchema)
})

#317

Merge search params with Zod in Remix

#367