Is there a published schema for the JSON returned by theAPI?

I have been perusing the API documentation and can find nothing published that describes the JSON that is expected to be posted for the CRUD operations nor anything on the JSON that is returned by the various endpoints.

I have been looking through the groovy source code and even that is hit & miss when I compare the toJSON() of the Product class to the JSON data that the endpoint is actually sending back.

For example the Product.groovy class in the org.pih.warehoust.product package

Map toJson() {
[
id : id,
productCode : productCode,
name : name,
description : description,
category : category,
unitOfMeasure : unitOfMeasure,
pricePerUnit : pricePerUnit,
dateCreated : dateCreated,
lastUpdated : lastUpdated,
color : color,
handlingIcons : handlingIcons,
lotAndExpiryControl : lotAndExpiryControl,
]

The output of the toJSON does not correspond one-for-one with the JSON that the /api/products endpoint returns:

{“active”:true,“category”:“Pain”,“color”:null,“dateCreated”:“2023-01-25T17:59:24Z”,“description”:null,“handlingIcons”:[{“color”:“#db1919”,“icon”:“fa-exclamation-circle”,“label”:“Controlled substance”}],“id”:“8ab0841585e9ebee0185ea15a1490095”,“lastUpdated”:“Jan 25, 2023”,“lotAndExpiryControl”:true,“name”:“Morphine 10mg immediate release tablet”,“pricePerUnit”:0.25,“productCode”:“QX039”,“unitOfMeasure”:“Each”,“updatedBy”:“Miss Administrator”}

Of note is that the “active” property, and the “updatedBy” property are not present in the toJson() method of the Product class. Therefore, there must be something, that is adding in those properties ( this decorator also seems to be reformatting the date properties to various formats) Where and how the decorating and reformatting is occurring is opaque to me, which is why I am asking about a schema.

For JSON responses we use two conventions:

Solution 1: Using hacky toJson()

In some cases, we use a convenience method (i.e. toJson()) to return a map of properties from the object, removing non-essential properties.

    render [(object: object.toJson()) as JSON]

The toJson() method is not a standard in any way. We should have probably implemented our own Jsonnable interface on all of the domain classes that needed to be rendered as JSON. This would have allowed us to generate docs using the toJson() to provide map of properties and some javadoc comments to annotate those properties. But I thought that would be overkill and didn’t want to add something that would be refactored out once we moved to a version of Grails that provided better or different support for JSON marshallers / schemas.

Aside: We have started work on Swagger/OpenAPI documentation for the API that should have a bit more information related to the schema of each object. However, the docs were generated off a branch of the code (feature/upgrade-to-grails-3.3.10) that is under development (Grails 3 migration) and is based on an older version of the system (0.8.17) so it may be outdated.

Solution 2: Using Grails JSON marshaller registry

At some point over the last few years, I realized that the toJson() wasn’t necessary. We just needed to use Grails default JSON marshaller registry to control the response for specific classes.

So when we render the object in the standard way (see below), Grails will try to locate a suitable JSON marshaller in its registry

render [(object: object) as JSON]

We have defined all of those marshallers in Bootstrap.groovy. In some cases we register the marshaller using a closure that just returns the map in the toJson() method

JSON.registerObjectMarshaller(User) { User user ->
    return user.toJson()
}

In others, we ignore the toJson and define the property map when registering the marshaller in the Bootstrap.groovy

Next Steps

In Grails 3, we have a decision to make whether we want to continue using the default JSON renderer or use JSON views Grails Views. Or maybe something else. This obviously needs more investigation.

One of the issues with the default JSON marshaller and renderer approach is that we don’t have a way of rendering different representations of the same object unless we create separate DTO classes for an object.

In other words, in order to respond with summary vs detailed representation of a product, I’d have to create two separate classes to wrap around Product i.e. ProductDetails, ProductSummary. Each DTO would have a separate JSON representation.

This became important when building out the API for the mobile application because we needed different representations of domain objects all over the place and in some cases, we needed to conserve bandwidth so we didn’t always want to send a detailed copy of the object across the wire. For example, if I was editing the product or viewing the inventory for a single product I potentially needed two different detailed representations. And when a product was included in a picklist, I needed a third summary representation.

Some of this can be dealt with using REST subresources but sometimes you may want to send a request for the same API (/api/products/:id) and use a request header (e.g. Content-Type) in order to respond with a different representation.

Here’s a discussion of content negotiation using request headers.

And here’s a discussion of how you can do this programmatically using Gson.

Anyway, we haven’t made any definitive decisions on any of this, so we’re open to suggestions. I’m guessing we’ll be striving to implement a solution that is both flexible and more standardized (at least in the Grails/Spring/Java world).

To answer your specific question, the OpenAPI / Swagger docs should (will) be the source of truth for JSON schema in the future.