Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,32 @@ Specify the `type` property when decorating response headers to prevent schema m
}
```

Response headers can also be declared using standard JSON Schema or TypeBox object structures containing `properties`:

```js
{
response: {
200: {
type: 'object',
properties: {
id: { type: 'string' }
},
headers: {
type: 'object',
properties: {
'X-Total-Count': {
type: 'integer',
description: 'Total number of items'
}
}
}
}
}
}
```

In OpenAPI 3.0/3.1 specifications, response `headers` are mapped as key-value maps of Header Objects (with `schema` and optional `description`). When `headers` is passed as a top-level schema object with `properties`, `@fastify/swagger` unwraps the nested properties to populate the response headers map.

<a name="route.response.empty_body"></a>
##### Different content types responses
> ℹ️ Note: Supported [only by OpenAPI v3](https://swagger.io/docs/specification/v3_0/describing-responses/), not Swagger (OpenAPI v2).
Expand Down
12 changes: 8 additions & 4 deletions lib/spec/openapi/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,17 @@ function resolveResponse (opts, fastifyResponseJson, produces, ref) {
// add headers when there are any.
if (rawJsonSchema.headers) {
response.headers = {}
Object.keys(rawJsonSchema.headers).forEach(function (key) {
const rawHeaders = (rawJsonSchema.headers.properties && typeof rawJsonSchema.headers.properties === 'object')
? rawJsonSchema.headers.properties
: rawJsonSchema.headers

Object.keys(rawHeaders).forEach(function (key) {
const header = {
schema: { ...rawJsonSchema.headers[key] }
schema: { ...rawHeaders[key] }
}

if (rawJsonSchema.headers[key].description) {
header.description = rawJsonSchema.headers[key].description
if (rawHeaders[key].description) {
header.description = rawHeaders[key].description
// remove invalid field
delete header.schema.description
}
Expand Down
4 changes: 3 additions & 1 deletion lib/spec/swagger/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ function resolveResponse (fastifyResponseJson, ref) {

// add headers when there are any.
if (rawJsonSchema.headers) {
response.headers = rawJsonSchema.headers
response.headers = (rawJsonSchema.headers.properties && typeof rawJsonSchema.headers.properties === 'object')
? rawJsonSchema.headers.properties
: rawJsonSchema.headers
// remove invalid field
delete resolved.headers
}
Expand Down
54 changes: 54 additions & 0 deletions test/spec/openapi/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,60 @@ test('support response headers', async t => {
t.assert.strictEqual(definedPath.responses['200'].content['application/json'].schema.headers, undefined)
})

test('support response headers defined as JSON schema object with properties', async t => {
const opt = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
},
headers: {
type: 'object',
properties: {
'X-WORLD': {
type: 'string'
},
'X-DESCRIPTION': {
description: 'Foo',
type: 'string'
}
}
}
}
}
}
}

const fastify = Fastify()
await fastify.register(fastifySwagger, {
openapi: true
})
fastify.get('/', opt, () => {})

await fastify.ready()

const swaggerObject = fastify.swagger()
const api = await Swagger.validate(swaggerObject)

const definedPath = api.paths['/'].get
t.assert.deepStrictEqual(definedPath.responses['200'].headers['X-WORLD'], {
schema: {
type: 'string'
}
})
t.assert.deepStrictEqual(definedPath.responses['200'].headers['X-DESCRIPTION'], {
description: 'Foo',
schema: {
type: 'string'
}
})
t.assert.strictEqual(definedPath.responses['200'].content['application/json'].schema.headers, undefined)
})

test('response: description and x-response-description', async () => {
const description = 'description - always that of response body, sometimes also that of response as a whole'
const responseDescription = 'description only for the response as a whole'
Expand Down
38 changes: 38 additions & 0 deletions test/spec/swagger/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,44 @@ test('support response headers', async t => {
t.assert.strictEqual(definedPath.responses['200'].schema.headers, undefined)
})

test('support response headers defined as JSON schema object with properties', async t => {
const opt = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
},
headers: {
type: 'object',
properties: {
'X-WORLD': {
type: 'string'
}
}
}
}
}
}
}

const fastify = Fastify()
await fastify.register(fastifySwagger)
fastify.get('/', opt, () => {})

await fastify.ready()

const swaggerObject = fastify.swagger()
const api = await Swagger.validate(swaggerObject)

const definedPath = api.paths['/'].get
t.assert.deepStrictEqual(definedPath.responses['200'].headers, opt.schema.response['200'].headers.properties)
t.assert.strictEqual(definedPath.responses['200'].schema.headers, undefined)
})

test('response: description and x-response-description', async () => {
const description = 'description - always that of response body, sometimes also that of response as a whole'
const responseDescription = 'description only for the response as a whole'
Expand Down