1

I'm building an REST API that exposes a callback method for a 3rd-party service.

Let's imagine that there are two similar (but not identical) 3rd-party services. They both call back my API (POST /api/callback), but have different request formats (JSON):

Provider 1

{"sender": "Provider1", "field1": "value1", "fieldN": "valueN"}

Provider 2

{"sender": "Provider2", "key1": "value1", "keyN": "valueN"}

So they have only one common parameter (sender) and others might differ.

Next, I have two corresponding DTOs that JSON should be mapped to

Pseudo-code:

class Provider1DTO {
    string sender;
    string field1;
    string fieldN;
}

class Provider2DTO {
    string sender;
    string key1;
    string keyN;
}

Provider1DTO dto1 = mapper.readJSON(json, Provider1DTO.class)
Provider2DTO dto2 = mapper.readJSON(json, Provider2DTO.class)

Question:

What is correct approach to achieve per-provider customization?

1) Introduce separate endpoints

/api/callback/provider1
/api/callback/provider2

2) Use single endpoint and analyze sender field of request body to map JSON to the relevant DTO

3) Use single endpoint and analyze X-Sender-ID (example) of request headers to map JSON to the relevant DTO

Approach #2 seems best to me, since I can't force providers to use custom request headers (as per #3). As for #1: there might be multiple services that provide identical request format, so there's no need to have separate endpoint

What are your thoughts on this? Thanks in advance

7
  • Are the data formats different, because the version of your API has changed? Are the "clients" 3rd party services that provide documentation on how to integrate with their applications? Sep 4, 2018 at 12:01
  • Are the fields that differ based on configuration options or an Entity-Attribute-Value table in a database? Sep 4, 2018 at 12:02
  • @GregBurghardt formats are different because some "clients" have own established API that they don't want to change. But the others are not that rigid and are ready to follow my design. Difference is only on DTO level, database/entity structure is the same Sep 4, 2018 at 12:36
  • Is the "established API" something they send to other endpoints as well, and not just yours? Sep 4, 2018 at 12:52
  • @GregBurghardt exactly Sep 4, 2018 at 12:54

2 Answers 2

1

Your server's job is to represent a resource, identified by a URI. That resource can have multiple representations (formats/serialisations). You have one resource, two request formats (both JSON schemas) you can accept, and two response formats you can return (also both JSON schemas).

Whilst I suggest that all clients which are able to adapt would themselves be better off adapting to the more widespread JSON schema used (thus able to talk to other servers in future that already use this format), if that cannot be done, I recommend negotiating with the HTTP Accept header. Have your clients send the schema they are requesting with in a Content-Type header, and the schema they want back in an Accept header. You may need to create some new media types for this. You can create private ones or you can register them with IANA if you think they should become public.

Both clients should send requests to a single URL that is not called e.g. "/api/do-something", but more like "/type-of-thing/thing" — URIs should be nouns that say what the thing is you get when you do a GET request. Putting "api" in the URL is a fallacy: all websites are APIs, regardless of whether client software is machine- or human-driven, and regardless of whether they are serving PDF files or XHTML or cat images or JSON.

1

Is there a reason your 3rd party must send their data in that format? Public APIs require cooperation between supplier and consumer, and it is common practice for the agreement to be "I will supply you an API which does x, and I require requests to be in format y".

Approach #2 seems best to me, since I can't force providers to use custom request headers

They are already being "forced" to send the request to a custom endpoint, with a body containing presumably custom and specific values.

If the API consumer changing their requests is out of the question (if this is being done without their knowledge / consent) then approach #2 / #3 would work. I would recommend mapping the request body to a single consumer-agnostic class as close to the endpoint as possible.

1
  • The format is established, apparently they use same format it for other callback implementations and don't want to adapt it for my API. But other "clients" are able to adapt Sep 4, 2018 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.