Skip to content

Instantly share code, notes, and snippets.

@adamgarcia4
Created December 19, 2022 00:45
Show Gist options
  • Save adamgarcia4/7b5644bb6bb5d3daabf5f8377c5b66b4 to your computer and use it in GitHub Desktop.
Save adamgarcia4/7b5644bb6bb5d3daabf5f8377c5b66b4 to your computer and use it in GitHub Desktop.
const swaggerValidationMiddleware =
(routeConfig: ISwaggerParams) => {
const AdditionalResponses: { [statusCode: string]: ResponseConfig } = {
422: {
description: 'Invalid input',
content: {
'application/json': {
schema: z.object({
code: z.string(),
message: z.string(),
expected: z.string(),
received: z.string(),
path: z.array(z.string()),
}),
},
},
},
500: {
description: 'Internal Server Error',
},
}
Object.assign(routeConfig.responses, AdditionalResponses)
registerRoute(routeConfig) // This calls registry.registerPath.
return (req: Request, res: Response, next: NextFunction) => {
if (!routeConfig.request) {
return next()
}
const send = res.send
const { headers, params, query, body } = routeConfig.request
try {
if (headers) {
for (const header of (headers)) {
try {
Object.assign(req.headers, header.parse(req.headers))
} catch (error) {
throw new Error(JSON.stringify({ type: 'headers', error } as ErrorStructure))
}
}
}
if (params) {
const requestSchema = params
try {
Object.assign(req.params, requestSchema.parse(req.params))
} catch (error) {
throw new Error(JSON.stringify({ type: 'params', error } as ErrorStructure))
}
}
if (query) {
const requestSchema = query
try {
Object.assign(req.query, requestSchema.parse(req.query))
} catch (error) {
throw new Error(JSON.stringify({ type: 'query', error } as ErrorStructure))
}
}
if (body) {
const requestSchema = body
let isSuccess = false
let error: z.ZodError | null = null
for (const content of Object.values(requestSchema.content || {})) {
const { schema } = content
if ('safeParse' in schema) {
const test = schema.safeParse(req.body)
if (test.success) {
isSuccess = true
break
}
error = test.error
}
}
if (!isSuccess) {
throw new Error(JSON.stringify({ type: 'body', error } as ErrorStructure))
}
}
res.send = function (body) {
// Override once!
res.send = send
if (!routeConfig.responses?.[res.statusCode]) {
console.log('No response schema for status code: ', res.statusCode)
return res.send('No response schema for status code: ' + res.statusCode)
}
const responseSchema = routeConfig.responses[res.statusCode]
let isSuccess = false
let error: ZodError | null = null
for (const content of Object.values(responseSchema.content || {})) {
const { schema } = content
if ('safeParse' in schema) {
const test = schema.safeParse(body) as any
if (test.success) {
return res.send(body)
}
error = test.error
}
}
if (!isSuccess) {
console.log('No response schema for status code: ', res.statusCode)
return res.status(422).send({
response: error?.issues ?? []
})
}
return res.send(body)
}
next()
} catch (error) {
console.log('error!')
const errorObj = JSON.parse(error.message) as ErrorStructure
return res.send({
[errorObj.type]: errorObj.error.issues
})
}
}
}
// Usage
router.get(
'/pathName',
swaggerMiddleware({
method: 'get',
path: '/pathName',
description: 'Description',
summary: 'Summary...',
request: {
query: z.object({queryParam: z.string()}),
},
responses: {
200: {
description: 'This is the happy path',
content: {
'application/json': {
schema: z.object({data: z.string()}),
},
},
},
},
}),
(req, res) => {...}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment