A credit card account (Account) can belong to multiple customers and One customer (Customer) can own multiple credit card accounts. I need to design REST API(s) which can return all accounts owned by a customer. The account number is coming from a manual input by an end user like a service rep into a freeform text box. Following is a constraint though
End consumers/developers know only account number & have no knowledge of customer id (unique identifier of a customer) upfront so to retrieve a list of accounts belonging to a customer -
1.1 find the customer owning the account in question
1.2 then find all the accounts owned by a customer.
I can think of couple of options but feel either they will make interaction chattier or may not be restful.
Only GET scenario has been discussed in below options
Option 1
Ideal way to interact with two separate resources but makes interaction very chatty and will put undue load on the system. Two calls everytime to know all accounts owned by a customer. So 20 Million calls/day in SOAP/RPC will become 40 million calls in REST.
/accounts/{account_nbr}/customers --> returns a list of customers for a specific account
/customers/{customer_id}/accounts --> returns a list of accounts for a customer
Option 2
I don't think this will be restful because query parameter is supposed to be used for identifying a resource in a non-hiearchical data
/customers/accounts?account_nbr = XXXX
Option 3
This option indicates that a list of accounts linked to account_nbr is being returned which is not right because list of accounts are linked to a customer
/accounts/{account_nbr}/linked_accounts
Option 4
Term the relationship between customer and an account as a new type of resource. Its trying to indicate get a list of customer to account relationships and identify specific instance where an account in customer_account_relationships has a value of XXXX.
/customer_account_relationships?account_nbr=XXXX or
Which of the above option, if any, is close to being restful representation? Is there any other way to design this interface?
EDIT
Expected response
{
"customerName" : "Bob",
"customerId" : 1234,
"listOfAccounts": [
{
"accountNbr" : "abcd"
"accountType": "creditcard"
},
{
"accountNbr" : "qrst"
"accountType": "creditcard"
}
]
}
You correctly rejected the first three options. I see two reasonable choices for you. One is to go with option 4:
GET /customer-summaries?account-number=<account-number>
The other is to just make /accounts top-level and do essentially the same thing:
GET /accounts?same-owner-as-account=<account-number>
In the former case, you'd get an instance of your resource above. In the second, you'd just get a list of accounts, each of which presumably has a link to the account owner. It's up to you as to which better suits your use case.
Note that option 4 may return multiple records if there are multiple owners for the same account. That's a common situation for married couples.
Related
I have two entities Hotel, Merchant where each merchant can have many hotels. Right now I am having an api endpoint like this:
/api/v1/merchants/{id}/hotels/{id}
But I was thinking what is wrong with this semantics:
/api/v1/hotels/{id}
The later one is short too.
In my experience, the latter is preferable because it gives you more flexibility later. In six months somebody's going to say "Hey, I want to be able to look up all the hotels in Agraba". The first URL scheme makes that painful - you need to add a new endpoint. The second URL scheme supports that with a query parameter: GET /hotels?location=Agraba.
You may want to keep /merchants/{id}/hotels as a collection endpoint so you can POST/DELETE to add/remove hotels from a particular merchant.
In REST, each URL should uniquely identify a single resource.
So if the hotel's id is globally unique, then sure, there's no problem in using the shorter link. If however hotel id 1 means something different for merchant 1 than for merchant 2, then you ought to stick with the first URL (basically a unique composite key).
It is not clear to me that if I have a micro service that is in place to provide some derived data how the rest api should be designed for this. For instance :-
If I have a customer and I want to access the customer I would define the API as:
/customer/1234
this would return everything we know about the customer
however if I want to provide a microservice that simply tells me if the customer was previously known to the system with another account number what do I do. I want this logic to be in the microservice but how do I define the API
customer/1234/previouslyKnow
customerPreviouslyKnown/1234
Both don't seem correct. In the first case it implies
customer/1234
could be used to get all the customer information but the microservice doesn't offer this.
Confused!
Adding some extra details for clarification.
I suppose my issue is, I don't really want a massive service which handles everything customer related. It would be better if there were lighter weight services that handles customer orders, customer info, customer history, customer status (live, lost, dead....).
It strikes me all of these would start with
/customer/XXXX
so would all the services be expected to provide a customer object back if only customer/XXXX was given with no extra in the path such as /orders
Also some of the data as mentioned isn't actually persisted anywhere it is derived and I want the logic of this hidden in a service and not in the calling code. So how is this requested and returned.
Doing microservices doesn't mean to have a separate artifact for each method. The rules of coupling and cohesion also apply to the microservices world. So if you can query several data all related to a customer, the related resources should probably belong to the same service.
So your resource would be /customers/{id}/previous-customer-numbers whereas /customers (plural!) is the list of customers, /customers/{id} is a single customer and /customers/{id}/previous-customer-numbers the list of customer numbers the customer previously had.
Try to think in resources, not operations. So returning the list of previously used customer numbers is better than returning just a boolean value. /customer/{id}/previous-accounts would be even better, I think...
Back to topic: If the value of previous-accounts is directly derived from the same data, i.e. you don't need to query a second database, etc. I would even recommend just adding the value to the customer representation:
{
"id": "1234",
"firstName": "John",
"lastName": "Doe",
"previouslyKnown": true,
"previousAccounts": [
{
"id": "987",
...
}
]
}
Whether the data is stored or derived shouldn't matter so the service client to it should not be visible on the boundary.
Adding another resource or even another service is unnecessary complexity and complexity kills you in the long run.
You mention other examples:
customer orders, customer info, customer history, customer status (live, lost, dead....)
Orders is clearly different from customer data so it should reside in a separate service. An order typically also has an order id which is globally unique. So there is the resource /orders/{orderId}. Retrieving orders by customer id is also possible:
/orders;customer={customerId}
which reads give me the list of orders for which the customer is identified by the given customer id.
These parameters which filter a list-like rest resource are called matrix parameters. You can also use a query parameter: /orders?customer={customerId} This is also quite common but a matrix parameter has the advantage that it clearly belongs to a specific part of the URL. Consider the following:
/orders;customer=1234/notifications
This would return the list of notifications belonging to the orders of the customer with the id 1234.
With a query parameter it would look like this:
/orders/notifications?customer=1234
It is not clear from the URL that the orders are filtered and not the notifications.
The drawback is that framework support for matrix parameters is varying. Some support them, some don't.
I'd like matrix parameters best here but a query parameter is OK, too.
Going back to your list:
customer orders, customer info, customer history, customer status (live, lost, dead....)
Customer info and customer status most likely belong to the same service (customer core data or the like) or even the same resource. Customer history can also go there. I would place it there as long as there isn't a reason to think of it separately. Maybe customer history is such a complicated domain (and it surely can be) that it's worth a separate service: /customer-history/{id} or maybe just /customer/{id}.
It's no problem that different services use the same paths for providing different information about one customer. They are different services and they have different endpoints so there is no collision whatsoever. Ideally you even have a DNS alias pointing to the corresponding service:
https://customer-core-data.service.lan/customers/1234
https://customer-history.service.lan/customers/1234
I'm not sure if I really understand your question. However, let me show how you can check if a certain resource exist in your server.
Consider the server provides a URL that locates a certain resource (in this situation, the URL locates a customer with the identifier 1): http://example.org/api/customers/1.
When a client perform a GET request to this URL, the client can expect the following results (there may be other situation, like authentication/authorization problems, but let's keep it simple):
If a customer with the identifier 1 exists, the client is supposed to receive a response with the status code 200 and a representation of the resource (for example, a JSON or XML representing the customer) in the response payload.
If the customer with the identifier 1 do not exist, the client is supposed to receive a response with the status code 404.
To check whether a resource exists or not, the client doesn't need the resource representation (the JSON or XML that represents the customer). What's relevant here is the status code: 200 when the resource exists and 404 when the resource do not exist. Besides GET requests, the URL that locates a customer (http://example.org/api/customers/1) could also handle HEAD requests. The HEAD method is identical to the GET method, but the server won't send the resource representation in HEAD requests. Hence, it's useful to check whether a resource exists or not.
See more details regarding the HEAD method:
4.3.2. HEAD
The HEAD method is identical to GET except that the server MUST NOT
send a message body in the response (i.e., the response terminates at
the end of the header section). The server SHOULD send the same
header fields in response to a HEAD request as it would have sent if
the request had been a GET, except that the payload header fields MAY be omitted. This method can be used for obtaining
metadata about the selected representation without transferring the
representation data and is often used for testing hypertext links for
validity, accessibility, and recent modification. [...]
If the difference between resource and resource representation is not clear, please check this answer.
One thing I want to add to the already great answers is: URLS design doesn't really matter that much if you do REST correctly.
One of the important tenets of REST is that urls are discovered. A client that has the customers's information already, and wants to find out what the "previously known" information, should just be able to discover that url on the main customer resource. If it links from there to the "previously known" information, it doesn't matter if the url is on a different domain, path, or even protocol.
So if you application naturally makes more sense if "previouslyKnown" is on a separate base path, then maybe you should just go for that.
Let's take the following example:
We want to expose company and employee information from a RESTful API.
Company data should be quite simply:
GET api/v1/companies
GET api/v1/companies/{id}
Employees BELONG to a company, but we still want to retrieve them individually as well, so which solution is best:
Solution 1: Using sub-resources
Get all employees for a company:
GET api/v1/companies/{companyId}/employees
Get a specific employee:
GET api/v1/companies/{companyId}/employees/{employeeId}
Solution 2: Using an independent resources
Get all employees for a company:
GET api/v1/employees?companyId={companyId}
Get a specific employee:
GET api/v1/employees/{employeeId}
Both options seem to have their pros and cons.
With sub-resources, I may not always have the CompanyId on hand when wanting to retrieve an individual employee.
With an independent resource, getting all employees for a company should use the sub-resource approach if we want to be RESTful.
Otherwise, we could use a mix, but this lacks consistency:
Get all employees for a company:
GET api/v1/companies/{companyId}/employees
Get a specific employee:
GET api/v1/employees/{employeeId}
What is the best approach to take in such a situation if we want to stay true to RESTful standards?
For me this sounds like the common many-to-many relationship problem for RESTful services. (see How to handle many-to-many relationships in a RESTful API?)
Your first solution seems good at first but you will have problems whenever you want to access the relation itself.
Instead of returning the employee with the following GET request you should return the relation.
GET api/v1/companies/{companyId}/employees/{employeeId}
If the relation can be identified by 2 keys this solutions seems to be fine. But what happens if the relation is identified by 3+ id's? The URI becomes rather long.
GET api/v1/companies/{companyId}/employees/{employeeId}/categories/{categoryId}
In this case I would come up with a separate resource for the relation:
GET api/v1/company-employees/{id}
The returned model in JSON would look like this:
{
"id": 1 <- the id of the relation
"company": {
"id": 2
},
"employee": {
"id": 3
},
"category": {
"id": 4
}
}
I think it would be okay to provide both. If you want the client to browse through the list of companies first, then select a company and then get the list of all employees, the first approach is necessary. If, may be in addition, you want the client to be able to filter employees by name or age, but without knowing the company identifier, you must provide the second approach as well. It depends on what you want the client to do. In my opinion, it would not be necessary to provide the second approach, if clients can only filter employees by company identifier.
I would go for the first approach and providing some links to retrieve the subordinate resource.
If I take the example of a new employee that you may add in a company. It seems to be difficult, for the client with the second approach to make a POST on your collections. Why ? Because he has to know the company id that is "somewhere else".
With the first approach, as you followed a path, you already know this information (the companyId)... so it's easier for the client to add a new employee.
Back to your example, the main benefit of the second approach is, if your client want something like "the amount of employees in a city", where you don't care about the notion of company.
But it seems that you need the notion of company, so I would go for the first.
Also, very related to this question: RESTful design: when to use sub-resources?
I have users, and I have groups
GET /user/1 -> {id:1,name:"john"}
GET /user/2 -> {id:2,name:"sam"}
GET /group/1 -> {id:100,name:"myGroup"}
Obviously, group-to-user is a many-to-many relationship. Ignoring the storage implementation, I am trying to figure out the sanest way to represent it.
/user/1/group // lists the groups of which "john" is a member, e.g. [100]
/group/100/user // lists the users who are members of "myGroup", e.g. [1]
One? The other? Both of them?
To take it one step further, what if I allow sam to see john (but no one else), because they are both members of group 100? Conceptually, LinkedIn or Facebook similarly allow you to see info about someone once you are connected.
So if john (1) requests info about sam (2), should I do:
/user/2 // allows it, with some backend mechanism finding the memberships of sam and john, and then finding that they are in the same group?
OR
/group/1/member/2 // this is john saying, "I want to access user 2 who is a member of group 1 and so am I"
The second option makes the request explain why they should be together, and makes the logic far simpler. On the other hand, user 2 already is a resource at /user/2, so the alternate route seems strange?
First, your collections should use a plural form such as:
/users
/groups
Representing your users and groups depends on your business, I would say that your "users" are first-class citizens, so it should be:
/users/{userId}/groups
That would return all groups for user identified by its userId. Note that /users/{userId} could return user's groups as well, but it is up to you.
The following URI would work as well, but then again, it depends on what you want to do:
/groups/{groupId}/users
To take it one step further, what if I allow sam to see john (but no one else), because they are both members of group 100? Conceptually, LinkedIn or Facebook similarly allow you to see info about someone once you are connected.
This could be implemented by some sort of Access Control Lists (ACLs). If user A can't see data of B because they are not friends, then querying /users/B will return nothing. However, if C (who is friend with B) performs the same request on /users/B, he will be able to see data of B.
So I would go for the first option.
I would consider making a separate resource, maybe groupMemberships, which holds the relationship. You can still have something like GET /users/1/groups to get the groups directly, if it was worth it to you.
GET /groupMemberships?user=1
GET /groupMemberships?group=100
{
"href": ".../groupMemberships/abc123"
"user": {
"href": "/users/1"
}
"group": {
"href": "/groups/100"
}
}
This is my first project which I've encountered that I can't get by on NSUserDefaults peppered with some NSCoding protocol. I've been asked to write some POS software.
Essentially, the App needs to store a bunch of products, prices and sales accounts. The user should be able to add items and accounts, and track the balance of accounts over time. The balance of an account should be able to be carried over from one "Session" (time period) to the next.
I'm comfortable with the concepts, but I'd like to be confident that I'm modeling this right. Here's how I've modeled my data. I'd like to know if I've done this properly or if there are any glaring errors/omissions.
I've created an "Account" Entity, which has the following properties:
First Name
Last Name
Account ID
Group
There is a relationship to the transaction entity.
I've created an entity for each Session. Again, a session is just like a fiscal month. The session will have a custom name and an ID.
Session ID
Session Name
There is a relationship to all of the accounts that are applied to that session.
There are of course, products, which have a name and ID. There is also a relationship to the "price" object, so I can change the prices without affecting balances.
Please see this screenshot from Xcode 4 which explains my model in its entirety:
Edit:
Looking at this, it seems that I'm missing some important info, such as dates of transactions etc. That said, am I on the right track?
It has been my experience that point of sale transactions list all the data that is necessary to recreate the receipt in three tables, a header (think date of sale, singular tracking entity), a set of records for all items being sold (linking back to the sale header), and a set of records for all the methods of payment (again linked back to the sale header).
This will give you the opportunity to rebuild the individual transactions in the future. Also, this is a simplistic model, but should suffice for what you're asking. Nominally yo uwould also keep track of applied discounts on a per-line-item basis, and per-invoice discounts, and per-group discounts, and etc.
What's the relationship between sessions and transactions?
You probably don't need to have an entity for price, as it will likely just be a float. I'd recommend adding a price attribute to your product entity instead.
I don't know if transactions will need names or not, I suppose if you want to have notes then they should.
Also transactions should probably have a to-many relationship with products.
Will this be used on a single device or will there be many users? If each user (account) is responsible for its own data then it may make more sense to have transactions/session rather than transactions/user.