Policy for referencing legacy (non jsonapi) entitites and endpoints in links and relationships?

What is the best way to reference entities that are not Json API compliant and that live on endpoints that return a completely different data structure? I’m working on a project where we introduced Json API for newer features but the new features are tightly coupled with a very large existing system that also returns Json data.

For example in this case we are returning the relationship to the “legacyentity” but it sort of implies that it will be available at a json api endpoint.

 {
  "type": "newentity",
  "attributes": {...},
  "relationships": {
    "legacyentity": { 
      "data": {"type": "oldstuff", "id": "1"}
    }
  }
}

So far we are just leaving out the links and only returning the relationship if it is needed to correlate something that we have embedded in included. Any thoughts on the best way to structure relationships and links to legacy data would be appreciated.

1 Like

Hmm, so there are a few different approaches you can take depending on the circumstances…

The simplest approach (but one that may not be feasible) would be to take the URIs for the legacy resources and have those URIs also support returning JSON API data, in addition to what they support now. This would be backwards compatible: on requests to the legacy URIs, you’d inspect the Accept header and return the JSON API format if the JSON API media type is provided, and the legacy JSON otherwise. Again, this may not be feasible, but if it is, it would make everything “just work”.

If this isn’t feasible, then I’d want to know: what information about the related resources is it important to include? Or is the important thing just signaling that the relationship exists? Also, what does your general system architecture look like? (That’d help me get a sense of what might be doable.) I can tell you now, though, that your best bet might end up being "meta".

Thanks for the notes. It probably isn’t feasible to support JSON API on the old endpoints in the near or medium term though I believe the intent is to get there eventually. The existing application is fairly sizable and is life with a bunch of live customers. What we are doing now is adding a new domain and associated features to the existing application with JSON API.

So far when a legacy object is needed along with a new JSON API compliant one we are just embedding the old object in included and creating a relationship with the right ids with no link. However, when a request is made that doesn’t specify the include for the legacy we don’t build the relationship. We’ve gotten by without signaling the relationship so far but this impedes some of the framework development on the front end because they have to handle those objects as a special case.

Overall system architecture is JS (backbone) front end, rest over http to Java. And on the server side the MVC controllers talk to microservices to gather data.

Hmm… the only compliant solution I can think of right now that lets you signal relationships, include the legacy data, and not confuse generic clients would look like this:

{
  "data": [{
    "type": "newentity",
    "id": "...",
    "attributes": { /* ... */ },
    "relationships": {
      "legacyentity": {
        "meta": { "legacy-data": {"type": "oldstuff", "id": "1"} }
      }
    }
  }],

  "meta": {
    "legacy-included": [ /* ... */ ]
  },
  "included": { /* ... */ }
}

Above, "legacy-data" works just like standard "data", except that it points to legacy objects that show up in "legacy-included" rather than standard "included".

The key thing making this compliant is that the "legacyentity" relationship object has a "meta" key, because a relationship object is allowed (strangely) to just have "meta".

The primary advantage this has over the solution you mentioned earlier is that it doesn’t put any legacy entities in "included"—doing that is sure to trip up generic clients which expect "included" to contain proper resource objects.

The downside, I guess, is that all that "meta" looks a little ugly.

Once the profile extension mechanism is finalized, it’ll be possible to solve this more elegantly, as there’ll be a spec-compliant way for you to include new keys in the document (and even define their meaning in a way that other clients can reuse). That is, you’d be able to do something like this:

{
  "data": [{
    "type": "newentity",
    "id": "...",
    "attributes": { /* ... */ },
    "relationships": {
      "legacyentity": {
        "legacy-data": {"type": "oldstuff", "id": "1"},
        // meta would technically still be needed here
        // for compliance, but it can be empty
        "meta": { }
      }
    }
  }],

  "legacy-included": [ /* ... */ ],
  "included": { /* ... */ }
}

If you control all the clients for this API, and you’re willing to do some find and replace modifications later, you could even implement the profile extension version now and then just tweak your implementation once the details of the profile extension mechanism codify.

As I’m currently working on the profile extension topic, I checked if e.g. an JSON-LD extension would be possible (there were some suggeations how this could be done in JSON-API.

In case of JSON-LD there is one important problem and perhaps also in the one above: All keys must meet the rules for the member name format and e.g. the “@” is reserves by the base spec, so such an extension would not posaible without violating or extending the base spec first.

Ideas how to solve this?

@Ziege Can we continue the JSON LD stuff in one of the extensions-related threads? I think JSON LD’s a bit off topic for the question @joe_white101 is asking here. (But I do have some ideas for it.)

Oh, I see. I think I misunderstood “legacy data” and thought it could also contain data with perhaps conflicting key formats…

Ahh, no you’re right then. I wasn’t thinking about that case/missed that connection.

@joe_white101 Does your data have any keys whose names violate http://jsonapi.org/format/#document-member-names? If you, you may be in trouble.

Re JSON LD: my strategy was just going to be to relax the member rules to allow for it in particular (which shouldn’t break old clients, as they should just ignore @-prefixed members), but that doesn’t solve the more general problem of allowing extensions to add data that violates the member rules. Then again, I’m not sure that’s something we should allow…

I believe all of the member names are compliant though I haven’t explicitly reviewed to be sure.

I haven’t had a chance to give your response from yesterday any detailed thought however the profile extension mechanism looks really promising. I think that your suggestion to create a temporary profile extension may work be the cleanest solution for us. Thanks for the responses!

I have a similar issue - I need to link to a third party API that returns HTML. Parsing this into JSON API to return to clients (so that they can render it as HTML) seems like a lot of work for not much benefit.

So to stay compliant with JSON API the approach I’m currently exploring is simply to put the URL of the third party API as a JSON string in the attributes section of the JSON API response.
I then have to rely on out of band information (a Swagger description of the API) so the clients know that this particular string should be treated as a URL that they can follow if they want to (they can’t pull the response from that endpoint through automatically via an include request to the JSON API).
So far this seems to be the “least bad” option, and seems to be working okay.

I’ll post here again if I run into any issues with this approach. But I’d be glad to hear if anyone else has already tried this, and whether it worked out okay or not.