Skip to content

Commit

Permalink
feat: Supporting different content-type responses (fastify#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
WoH committed Dec 22, 2022
1 parent f63e3e5 commit 42bf20a
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 4 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,32 @@ app.inject({
})
```

Different content types responses are supported by `@fastify/response-validation`, `@fastify/swagger` and `@fastify`. Please use `content` for the response otherwise Fastify itself will fail to compile the schema:
```js
{
response: {
200: {
description: 'Description and all status-code based properties are working',
content: {
'application/json': {
schema: {
name: { type: 'string' },
image: { type: 'string' },
address: { type: 'string' }
}
},
'application/vnd.v1+json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'string' }
}
}
}
}
}
}
```

If you want to override the default [ajv](https://www.npmjs.com/package/ajv) configuration, you can do that by using the `ajv` option:
```js
// Default configuration:
Expand Down
29 changes: 25 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,37 @@ function fastifyResponseValidation (fastify, opts, next) {
const statusCodes = {}
for (const statusCode in schema) {
const responseSchema = schema[statusCode]
statusCodes[statusCode] = ajv.compile(
getSchemaAnyway(responseSchema)
)

if (responseSchema.content !== undefined) {
statusCodes[statusCode] = {}
for (const mediaName in responseSchema.content) {
statusCodes[statusCode][mediaName] = ajv.compile(
getSchemaAnyway(responseSchema.content[mediaName].schema)
)
}
} else {
statusCodes[statusCode] = ajv.compile(
getSchemaAnyway(responseSchema)
)
}
}

return preSerialization

function preSerialization (req, reply, payload, next) {
const validate = statusCodes[reply.statusCode] || statusCodes[(reply.statusCode + '')[0] + 'xx']
let validate = statusCodes[reply.statusCode] || statusCodes[(reply.statusCode + '')[0] + 'xx']

if (validate !== undefined) {
// Per media type validation
if (validate.constructor === Object) {
const mediaName = reply.getHeader('content-type').split(';')[0]
if (validate[mediaName] == null) {
next(new Error(`No schema defined for media type ${mediaName}`))
return
}
validate = validate[mediaName]
}

const valid = validate(payload)
if (!valid) {
const err = new Error(schemaErrorsText(validate.errors))
Expand Down
88 changes: 88 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,94 @@ test('Should check only the assigned status code', async t => {
t.strictSame(JSON.parse(response.payload), { answer: '42' })
})

test('Should check media types', async t => {
const fastify = Fastify()
await fastify.register(plugin)

fastify.route({
method: 'GET',
url: '/',
schema: {
response: {
'2xx': {
content: {
'application/geo+json': {
schema: {
type: 'object',
properties: {
answer: { type: 'number' }
}
}
}
}
}
}
},
handler: async (req, reply) => {
reply.header('Content-Type', 'application/not+json')
return { answer: 42 }
}
})

const response = await fastify.inject({
method: 'GET',
url: '/'
})

t.equal(response.statusCode, 500)
t.strictSame(JSON.parse(response.payload), {
statusCode: 500,
error: 'Internal Server Error',
message: 'No schema defined for media type application/not+json'
})
})

test('Should support media types', async t => {
const fastify = Fastify()
await fastify.register(plugin)

fastify.route({
method: 'GET',
url: '/',
schema: {
response: {
'2xx': {
content: {
'application/a+json': {
schema: {
type: 'object',
properties: {
answer: { type: 'boolean' }
}
}
},
'application/b+json': {
schema: {
type: 'object',
properties: {
answer: { type: 'number' }
}
}
}
}
}
}
},
handler: async (req, reply) => {
reply.header('Content-Type', 'application/b+json')
return { answer: 42 }
}
})

const response = await fastify.inject({
method: 'GET',
url: '/'
})

t.equal(response.statusCode, 200)
t.strictSame(JSON.parse(response.payload), { answer: 42 })
})

test('Should check anyOf Schema', async t => {
const fastify = Fastify()
await fastify.register(plugin)
Expand Down

0 comments on commit 42bf20a

Please sign in to comment.