I'd rather live by the old maxim "be liberal in what you accept, and strict in what you produce". But perhaps I'm overlooking an important use case here, in which I'd happily stand corrected.
I'd rather live by the old maxim "be liberal in what you accept, and strict in what you produce". But perhaps I'm overlooking an important use case here, in which I'd happily stand corrected.
Having this as part of your interface definition, along with generated representations of the possible data in the language your choice[0] and automatic (so you can't forget it) validation that rejects invalid input with HTTP 400 bad request instead of 500 internal server error when the DB constraint fails (or even worse, you persist invalid data and something breaks later) is definitively useful.
That said, OpenAPI fails at this somewhat as the people writing the spec don't consider the (de)serialization and validation libraries. Thus, OpenAPI keeps adding features/constraints that are not supported by the libraries and are thus cannot be automatically validated or even produce nonsense output on generation (e.g. in Java you might end up with raw Map or even Object fields)
Isn't that all just basic data validation and sanitation that we all do anyway? And wouldn't it still have to be done, even if the JSON came with all the schemabloat?
I don't think I'm suddenly going to accept that the data presented to my function is valid and fit for purposes just because it goes through someone's JSON schema library. That's just passing the responsibility on to a third party, which feels lazy and dangerous to me.
Maybe it'll be OK for systems that don't get unusual data. But mine are always ingesting strange things, and the only person who knows what's truly valid and what's not is me, not a third party. That's my responsibility as the programmer of the system.
I looked through the web page, and it's probably great for someone. Somewhere. But I'd like to see that SKU example that was presented fully fleshed out with real-world type data. It seems OK if your data only consists of "productID," "productName," and "productTags." But in real life, SKUs are often incredibly complex, and this just seems to invite disaster by making complex systems even more complex. So much so that they require even more complex tools to manage them.
You trust the memory manager, the SSL library and countless others to do their jobs, data validation is no different.
Whether a particular library is up to scratch, is, ofcourse, debatable.
Any library worth it's salt, like asp.net, allows you to define custon data type, like SKU, with whatever validation your heart desires.
I don't think I'm suddenly going to accept that the data presented to my function is valid and fit for purposes just because it goes through someone's JSON schema library. That's just passing the responsibility on to a third party, which feels lazy and dangerous to me.
Do you also distrust that third parties can implement HTTP, generate SQL, enforce the constraints in your DDL, do arithmetic operations, etc. correctly ?Of course there is still a lot of semantic validation you have to do manually (e.g. for an order: does the referenced product exist, is the seller allowed to sell it, at this time, for this price, for this currency, taxes, do the line prices add up to the order price, contingents, etc., etc.).
Expressing these constraints in your interface definition is just another declarative validation layer like your implementation language's type system, database schema/constraints, generic validation libraries[0].
Also, as the server author you are usually also the author of the interface definition containing all these constraints.
[0] E.g. https://beanvalidation.org . The OpenAPI to Java generator will express constraints using BV annotations. BV also integrates with the Hibernate ORM to validate data before you send it to the DB, or you can manually annotate your DTOs/domain model and programmatically trigger the validation at any time, so you only have to know one set of annotations and rely on a single library for validation in any layer of your application.
> Isn't that all just basic data validation and sanitation that we all do anyway? And wouldn't it still have to be done, even if the JSON came with all the schemabloat?
The point of JSON Schema (and schemas for serialization format in general) is to have a machine readable description of the data constraints that both a writer of the data and a reader of that data can use to automate both validation and documentation rendering.
> I don't think I'm suddenly going to accept that the data presented to my function is valid and fit for purposes just because it goes through someone's JSON schema library. That's just passing the responsibility on to a third party, which feels lazy and dangerous to me.
Do you trust the compiler/runtime of your programming language?
JSON Schema can allow to use a JSON Schema definition for translation into validation code. That may not fit all crazy validation rules, but at least that allow to automate one validation layer from a specification.
The purpose of a schema isn't to ensure that every business rule is satisfied. It's just the first step of data validation. It's step 1 of input triage. It's saying, "I'm not even going to look at your input until you can demonstrate that the request meets the minimum requirements of a theoretically valid request."
If I'm asking for a Student, and I say they have to have a StudentId, a FirstName, a LastName, and a DateOfBirth, then I can put that into a schema and immediately reject any submission that tries to submit Surname or GivenName. I can go further and say that StudentId is up to 25 characters and can't be blank. DateOfBirth has to be present, must be a date in yyyy-MM-dd format, and it must be in the past, etc.
You can reject the data as incomplete or invalid at a glance. You're setting up a basic set of rules that allows you to instantly discard a whole range of invalid input.
I also want to add that consumers with an appropriate framework can automatically generate a response body explaining where in the payload that errors were encountered and what those errors are. This allows API consumers to understand what is going wrong far more easily that a 500 being returned.
ASP.NET for example can do exactly this with validation on incoming payloads.
If the payload is invalid, then either way there will be an error, but it's nice to be able to give more helpful errors.
API must be absolutely strict and unambiguous. It helps everyone in the end. Or you'll end up with parseInt abomination.
And for strict API you need strict validation. Probably better than schema can provide, but schema is a good start with declarative description and proven battle-tested libraries. After request was validated by the schema, you can be sure in some facts, like which properties are not null, which values are numbers and so on. This already helps to eliminate lots of unnecessary code.
Well, I think you just moved the validation code to another place. Instead of putting it in your app, where (in my opinion) it belongs, you are putting it in a hard-to-read and difficult to understand external JSON file. I'm not sure how that's an improvement.
Otherwise, it’s just in your code, where it’s potentially not just “hard-to-read”, but impossible to read for anybody but the authors.
That said, I’m partial to other solutions like protobuf, but what can you do.
Also, I can replicate my validations at many levels down the stack: from the client to API-GW to the database model. All with a single definition.
schemaValidate(input, {
"required": ["price"]
"properties": {
"price": {
"type": "number",
"minimum": 0,
"maximum": 1000
}
}})
vs if (input.hasOwnProperty("price"))
throw Error("Missing price property")
let price = parseInt(data["price"], 10)
if (isNaN(price))
throw Error("Property 'price' is not a number")
if (price < 0)
throw Error("Property 'price' is negative")
if (price > 1000)
throw Error("Property 'price' is too high")
Not claiming the later is particularly difficult, in fact it's very easy to understand, but it is sligthly more verbose and tedious, for bigger validations it can become even worse. Here the schema turns every 2-3 lines of custom validation code into 1, provides automatic and better error messages of the error cause and acts as a contract which consumers of the api can read or even use to auto generate code, GUI, autocompletion. For the later use cases you would of course put the schema in a separate public file. For more complex validation you can of course add them after calling schemaValidate.All that validation takes a lot of effort and makes your code hard to read.
If you verify that the JSON input matches the expected schema at the very beginning, you can skip all the validation and error handling later, since you already know that is valid. This would make it a lot safer to handle external input.
Then the next step would be if language & tooling also supported JSON schema, so that the way you access JSON documents in code also doesn't violate the schema. This would help prevent bugs from programmer errors.
Instead of just failing somewhere deep inside the app (even perhaps deferred to later) with "cannot read property of undefined... stack trace"
XAML, for example, would suck if it didn't have a bunch of XSD mumbo jumbo backing it up.
All arguments for and against schemas in databases translate well to this case.
As a specific example, when I'm writing a snapcraft.yaml, I want to be able to view a schema to see what all I can put in there. What's important is that the schema is actually used by snapcraft itself for validation, otherwise it's no better than the rest of the snapcraft documentation, which is pretty meh. Schemas are also easy to digest for other tooling, so e.g. my editor can automatically highlight when I make a mistake, without having to specifically write a plugin for each tool that accepts JSON/YAML
- Error messages: A validator doesn't just have to tell you that a document is invalid, it can also tell you why it is invalid and what you have to do to fix it.
- Better editors or viewers: If e.g. an IDE knows that your JSON is "really" a record or a graph or a table, it could show some specialized UI and make it easier to view or edit those documents.
- Smarter auto-formatting: A schema-aware pretty-printer could format JSON in a way that's better readable than just putting every field on one line.
- Documentation generation: e.g. Swagger does this well: You pass it a schema file and it generates a polished HTML documentation, complete with examples and interactive web client.
- Code generation: You could auto-generate parsers, serialisers or data structures that are specialized for a particular schema. This would probably be most useful for low-level languages or other languages where working with generic JSON is inconvenient.
- More efficient storage: A data store that "knows" that all documents conform to a particular schema could optomize by storing/querying only the schema's data structures and not a generic JSON DOM. (Though to be fair, it's not clear how large the performance improvements from this would be, especially compared to a DB that learns similar information from the usage patterns and data that is actually stored)
That's a lot of "could"s though. So far, I haven't seen a lot of those use-cases realized. What also frequently baffles me is that use-cases almost seem like an afterthought in schema design. Most specs and blog posts (like this one) read as if a schema doesn't really need any justification and actual uses are just a nice bonus.
Instead of an overengeneered spec, I'd prefer an actual software suite which realizes some of the above use-cases - and then brings a well-designed schema language with it.
I believe Swagger/OpenAPI started this way, which might be how they gained traction at all. But judging from the OP, they seem to be taken over by the astronauts again...
Validation while in production makes no sense, I agree. But automated testing… I bet you can catch some unintentional changes to your JSON output while testing. Not all of them, but some. Maybe even enough to justify keeping the schema current.