1

So imagine, I have an endpoint where subscription payment to a magazine can be made.

There could be different flavors of subscription. eg, weekly, fortnight, yearly, 5 years etc.

For each subscription, the JSON request to the subscription endpoint varies in slightly different ways. For example, perhaps with the 5 years subscription, you might want to ask for the residential address, but for the weekly subscription, perhaps you do not care. Thus validation (and also business logic) differs slightly based on the subscription type.

The question is, which is the preferred way to model the endpoints:

  1. Have one single endpoint: /subscription in the JSON that is sent, you have a discriminator property. That is: { name: "Joe", Age: 66, subscription_type: "weekly | monthly | fortnight | yearly | etc" } So that in the implementation of /subscription you have code that performs different validation to the JSON request and executes different business logic depending on the value of the subscription_type
  2. Have separate endpoints for the different subscription types. For example: /subscription/weekly, subscription/monthly etc and have the implementation of each of these endpoints to only care about the validation and business logic specific to their subscription type.

Any other option possible apart from these two I mentioned? Is there any best case practice for dealing with this kind of scenario?

2
  • 1
    This has nothing to do with REST. What your question seems to be referring to are the requirements of a valid URI. That is addressed by RFC 3986 Jan 26, 2018 at 17:44
  • So if your periodical comes out monthly, but the user wants to pay weekly, what happens? In other words, isn't there validation that ensures the requested payment scheme is valid for the magazine in question? Jan 29, 2018 at 20:56

4 Answers 4

3

The resource is a subscription for some customer. The duration, start date, etc. is information about the subscription.

If the resource address is, e.g. customer/{id}/subscription/monthly. How will you GET the subscription type given the customer id?

It seems cleaner to be able to GET {id}/subscription to get information about a customer's subscription, PUT {id}/subscription to create a new subscription and POST {id}/subscription to amend an existing subscription.

1
  • 3
    If you're going to make "subscription" necessarily follow from "customer" then why would "customer" not necessarily follow as a "party role" and then from there move up to a "party."If the customer isn't important to the interface,then the design shouldn't be customer-centric.As a case in point:I firmly believe that financial systems should be designed directly around credits and debits.The "who" details,the "where" details,and the "why" details are entirely irrelevant within this core context and only "what" and "when" are pertinent.Owe money;pay money;on time.Owe money;no money;past due.etc Jan 29, 2018 at 18:38
3

Your request is orthogonal to the RESTful architectural style.

REST is just an architectural style. One of its assumptions is that you must not define your own protocol semantics inside another protocol and obfuscate the details of your custom conventions by referring to the parent scheme.

As an example, you can't use "URI"s for identifying your resources and violate the requirements of RFC 3986, which is the authoritative source for what constitutes a well formed URI. You cannot assert you are using "HTTP" as your protocol and submit non-idempotent requests with a "GET" request; such a thing makes no sense inside the requirements of HTTP.

Within that framework, the major contribution that the RESTful architectural style makes is the notion that available application state transitions are required to be communicated FROM the server TO the client.


It appears as if you're trying to force a distinction without a difference between your subscriptions and propagate this distinction up through your interface.

If the resource interface is the same for all "Subscriptions", then you produce nothing of value to the expressiveness of that resource by formally splitting it by one of its attributes. The only thing accomplished by doing this within your resource identifiers is to leak your internal server implementation up through your service abstraction.

From the information you have provided, you appear to have a single entity: "Subscription"; therefore, you should strive to design a single interface across the different types of "Subscription".

1

Is there any best case practice for dealing with this kind of scenario?

I have found that asking "how would you do this on a web site?" is a very powerful tool for evaluating your designs.

For each subscription, the JSON request to the subscription endpoint varies in slightly different ways. For example, perhaps with the 5 years subscription, you might want to ask for the residential address, but for the weekly subscription, perhaps you do not care.

So this sounds like having multiple forms to collect data from the customer. There might a weekly form, that collects the information that you need in that use case, and a five year form that collects a different data set. Each of these forms would have an action attribute; when a form is submitted, the client constructs the appropriate payload, and dispatches the request to the endpoint described by the form.

The core of your question, therefore, is; should these different forms each have a unique endpoint? or should they share an endpoint?

Put slightly differently: what are the implications of this choice?

RFC 7234 describes cache invalidation:

A cache MUST invalidate the effective Request URI (Section 5.5 of RFC7230) as well as the URI(s) in the Location and Content-Location response header fields (if present) when a non-error status code is received in response to an unsafe request method.

In other words, if you POST to the same resource that you GET the representation from, then intermediaries can know that a previously stored representation of the resource should be evicted.

That means, for example, that you can put caching proxies in front of your API endpoints, and get the correct behavior "for free". Likewise, clients that have associated caches can use their local caching for free.

Of course, if you were going to modifying your subscription resource using PUT, or PATCH, you would get the same answer: send the unsafe request to the resource itself, so that the cache invalidation works like it is supposed to.

It's a trade; we accept the constraint upon our choice of endpoints, in exchange we get free caching. In 2008, Fielding wrote

REST is intended for long-lived network-based applications that span multiple organizations. If you don’t see a need for the constraints, then don’t use them.

Caching is not a powerful argument for all API. If it doesn't make sense in your case, then you shouldn't regard this argument as particularly compelling. Document your understanding of the constraints, and move on.

1
  • There are only two hard problems in all of Computer Science: Cache Invalidation, Naming Things, One Off Errors, and Cache Invalidation Jan 30, 2018 at 14:30
0

The second model, no doubt.

It's almost always a good idea to separate endpoints to be consistent on the entry values.

Have a endpoint that can accept different attributes if some another attribute was sent together is a bad idea, because it's:

  • Harder to understand
  • Harder to create a good documentation
  • Harder to maintain
  • Harder to create different versions of the resource (if it's your case).

Bonus

If you can save a subscription without the type information, I have a suggestion: think about sent the common information in a separated POST to /subscription/ and sent the specific information for each type in POST to each type, like: /subscription/{id}/type/weekly/, /subscription/{id}/type/monthly/, etc.

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.