Is the client always going to use a library to interact, is connecting without any code/schema published by the API provider a core requirement? Are clients going to be in languages with good protobuf/GraphQL support? Some don't have this. Is the code that talks to the API also owned by the API provider? Public vs private. Support lifecycles.
These factors all play into whether the type safety could even be utilised by clients. And it's not like REST doesn't have this – OpenAPI and Swagger can get a lot of the benefit with fairly minimal work. Both are very common.
They do differ fundamentally in that type safety is an afterthought with REST but is built in with some of the alternatives. That can have it's benefits too of course, it's easier to integrate with and more broadly supported in part because it doesn't concern itself with that.
OpenAPI can help but keeping your schema in sync with reality can be a pain depending on what libraries you have available. In the best case it really is minimal work, but if you don't have good tooling for whatever web framework you're using it can be a bit of a pain. In my experience it often requires more manual effort to maintain & more risk of mistakes causing the schema to be inaccurate.
Unless your models are very simple, the best approach is to use three separate layers of model definitions:
* API models for serde and conversion of external requests
* business logic models that carry the actual internal functionality
* (optionally) ORM models to convert the data for persistence to a RDBMSThe thing I’ve noticed when stepping into a codebase where this problem has been allowed to occur is the lack of layers of abstraction. Having those different models built up from the start allows for an application to shift along with the needs of the product. Having a single layer, with the endpoints talking literally directly to the ORM models, almost inevitably leads to calcification, spaghettification, and disastrous performance.
* manual:
* schema is maintained manually, independent of the implementation
* usually an afterthought and used mainly for documentation
* implementation-first:
* schema is autogenerated from the code
* used as a reference, possibly to generate clients, and often to run tests
* probably the most common approach, supported by many frameworks
* schema-first:
* schema is maintained manually, with client and server code generated from it
* very rare, but the most correct approachHow is using, for example, JSON Schema to define your types any better or worse than a "proto" file that requires a compiler, a parser, and a client and server library?
There's many tradeoffs of course, it's not like dealing with proto files is painless either.
JSON has a limited set of types. There is object, array, string, number, boolean, and null. That's it.
JSON Schema adds to that by providing definitions of types that build on those basic JSON types.