Adding non-required property is considered a breaking change
Issue #15
closed
Given the following API specs:
openapi-old.yaml:
openapi: 3.0.1
info:
title: Old
version: v1
description: Old spec
paths:
/test:
summary: Test
description: Description
post:
operationId: testOperation
requestBody:
content:
application/json:
schema:
type: object
properties:
token:
description: Token supplied by user to authorize the request
type: string
required:
- token
responses:
'201':
description: If the request has been registered successfully
openapi-new.yaml:
openapi: 3.0.1
info:
title: New
version: v1
description: New spec
paths:
/test:
summary: Test
description: Description
post:
operationId: testOperation
requestBody:
content:
application/json:
schema:
type: object
properties:
token:
description: Token supplied by user to authorize the request
type: string
newProperty:
type: string
required:
- token
responses:
'201':
description: If the request has been registered successfully
Running openapi-diff openapi-old.yaml openapi-new.yaml
Yields the following output:
Breaking changes found between the two specifications:
{
"breakingDifferences": [
{
"type": "breaking",
"action": "remove",
"code": "request.body.scope.remove",
"destinationSpecEntityDetails": [
{
"location": "paths./test.post.requestBody.content.application/json.schema",
"value": {
"type": "object",
"properties": {
"token": {
"description": "Token supplied by user to authorize the request",
"type": "string"
},
"newProperty": {
"type": "string"
}
},
"required": [
"token"
]
}
}
],
"entity": "request.body.scope",
"source": "json-schema-diff",
"sourceSpecEntityDetails": [
{
"location": "paths./test.post.requestBody.content.application/json.schema",
"value": {
"type": "object",
"properties": {
"token": {
"description": "Token supplied by user to authorize the request",
"type": "string"
}
},
"required": [
"token"
]
}
}
],
"details": {
"differenceSchema": {
"type": "object",
"properties": {
"newProperty": {
"type": [
"array",
"boolean",
"integer",
"null",
"number",
"object"
]
},
"token": {
"type": "string"
}
},
"required": [
"newProperty",
"token"
]
}
}
}
],
"breakingDifferencesFound": true,
"nonBreakingDifferences": [],
"unclassifiedDifferences": []
}
Adding a non-required property shouldn’t be considered a breaking change.
Comments (3)
-
-
Account Deactivated reporter I wasn’t aware of that, thanks for the explanation!
-
- changed status to closed
- Log in to comment
I’ll explain the breaking change by way of example. In the original schema this request was accepted:
This is because by default JSON Schema allows all values in all properties, and by adding keywords you are placing constraints on the allowed values. So since the original schema did not place any constraints on the property
newProperty
, it was allowed to be any value.However, the new schema does not accept this request because it applies a constraint to this property, only allowing it to be of type
string
. This means that this property can no longer be of typenumber
. It is also not allowed to bearray
,boolean
,integer
,null
orobject
. You can see this in thedifferenceSchema
returned by the tool. Any client sending a request withnewProperty
with any of these now unsupported types will have their requests considered invalid if you make this change, and so therefore we consider this a breaking change.If you are confident no client is sending a property with one of these now unsupported types you can safely make the change - the tool has done it’s job of helping you make informed decisions about what impact a change to your spec will have.
You could also consider using the
additionalProperties
keyword in order to prevent undefined properties from being accepted. This would allow you to add properties into the properties list of the schema and not have them be flagged as breaking changes. For example, consider this old request body schema:Compared to this new request body schema:
This would not be flagged as a breaking change because the original schema did not allow the property
newProperty
to be any value.Just be aware that if you use this approach it will mean that when you do want to add new properties to your API you will be forced to make a change to the spec first before the property will be allowed, you won’t be able to start with the client. You will have to consider if this is a tradeoff you are willing to make.
Hope that clears things up, let me know if you have any further questions.