We are trying to figure out if there is a generally accepted way of providing an API parent -> child resource. Say we have a Person entity and each Person has 0 or more addresses represented by the Address entity.
In terms of basic API we'd have:
POST: /api/v1/person
GET: /api/v1/person/{id}
PUT: /api/v1/person/{id}
DELETE: /api/v1/person/{id}
Then we have 2 ways to retrieve the addresses for a person:
/api/v1/person/{id}/addresses
/api/v1/addresses/{personId}
We feel it's more natural to pick the former option /person/{id}/addresses for GET but at the same time if we wanna retrieve an address by its id it should be /api/v1/address/{id}.
The question is, is there a convention in dealing with POST, PUT and DELETE calls? To me it makes sense that these should be called to the address service at /api/v1/address OR /api/v1/address/{id} but at the same time I can see why someone would POST to `/api/v1/person/{id}/address' instead of passing the person id in the request body.
So yeah, can you guys point us in the right direction here - is there a written or unwritten rule in API design when it comes to parent -> child relations?
Thanks in advance!
Can an address exist without a person? If the answer is yes, then an address should be a resources of its own.
/api/v1/addresses: the collection of all addresses
/api/v1/addresses/{addressId}: a single address
/api/v1/addresses?person={personId}: all addresses for a person
I'd not use /api/v1/addresses/{personId} because it is not immediately obvious that personId is the ID of a person, not of an addresse.
But at the same time, /api/v1/person/{id}/addresses should be available to navigate from a person to all his addresses.
If an address can not exist without a person, only use /api/v1/person/{id}/addresses.
Related
First of all I am really not very familiar with the REST practice, and I am not very confident about the title of my question.
So I am trying to built a RESTful API using Laravel for a phonebook application. The phonebook may contain telephones of either employees (i.e real persons) or offices. For that reason I have three models
a Directorate with id and name fields,
an Employee with id and name fields and
a Telephone with id, tel, employee_id, directorate_id, description and type fields.
Telephones corresponding to a directorate's office have only the id, tel, directorate_id and description fields set, while the telephones corresponding to a person (i.e an employee) have set only the id, tel, employee_id, directorate_id, and type fields. That is how I separate them: a telephone having a description can only be an office's telephone, while a telephone having both the employee_id and the type_id set is an employee's telephone.
The models are related as follows:
an employee may have many telephones
a directorate, may have many telephones
class Directorate extends Model
{
public function telephones()
{
return $this->hasMany(Telephone::class);
}
public function employees()
{
return $this->hasMany(Employee::class);
}
}
class Employee extends Model
{
public function telephones()
{
return $this->hasMany(Telephone::class);
}
public function directorate()
{
return $this->belongTo(Directorate::class);
}
}
class Telephone extends Model
{
public function employee()
{
return $this->belongsTo(Employee::class);
}
}
My question is what should I a consider as my resource.
So far I am thinking of the following approach:
I shall use the concept of contact as resource. A contact may be the joined information of either an employee and a telephone, or a directorate and a telephone. For instance, a "contact" may contain the name of an employee with his related telephone numbers and telephone types, or it can contain the name of a directorate with the description of the telephone and the telephone number.
The "problem" with this approach is that I have ended up with (let's put it this way) two different types of resource: the employee's contacts and the directorate office's contacts, which contain slightly different information and thus, I need also to have different create and edit forms to interact with these two "types" of resources.
In order to implement the REST API, I am thinking of two different scenarios:
Use two different RESTful controllers, one EmployeeContacts and another OfficesContacts for separating conceptually the resource to an employee's and an office's resource, and accessing them through different URIs like:
example.com/phonebook/employees/{id}/edit
example.com/phonebook/offices/{id}/edit
example.com/phonebook/employees/create
etc...
Use a single RESTful controller, e.g. PhonebookContacts to access the resources through the same URIs as one resource (i.e. both employee's and office's contact resources now are considered to be just a "contact" resource)
//this refers to a contact resource that can be either an office's or a n employee's contact
example.com/phonebook/contact/{id}/edit
//this should list all resources (both employees and offices contacts)
example.com/phonebook/contact/
and then use conditional statements in the controller's create/store/edit/update methods to deal with them separately (e.g if an http POST request contains a description_id then it is an office contact and do this, or else if has an employee_id then it is an employee's contact so do that...)
I would like to hear your views, what of these two different scenarios do you consider to be better in the context of REST for my phonebook app? Would be better to think of a single "contact" resource and handle it using conditional statements with different return in the controller, or shall I separate the concept of "contact" to "employee's contact" and "office's contact" and use separate controllers and URI's to handle them?
Is there another approach that I could follow instead?
The way I would do it is with 2 different controllers for the simple reason of speed and responsiveness. Loading all contacts and filtering isn't as quick as loading the one part only.
However, you can always set in your controller the same return with different data. Such as EmployeeController#index returns view('contacts.index', compact('employeeContacts')), and OfficesController#index returns view('contacts.index', compact('officesContacts'))
EDIT:
Sorry, I have misread it...I thought you wanted to do the filtering in the view. Anyway, my practice is to do it separately, simply because the code is cleaner. If you want to make the whole REST more readable, you can put both resources in a group like so: Route::group(['prefix' => 'contact'], function(){ //routes here// });
So now you will have routes like:
example.com/contact/employees/
example.com/contact/offices/
I am not familiar at all with Laravel but since this question is about REST concepts (I have a small background on these) I should give it a try.
Since you are building a RESTful application, you must not consider others as human beings but only as machines. IMO the urls should determine the action that will be performed. Thus, by using different urls for different actions (perform a CRUD on a contact - either an Employee or a Directorate or SomethingElseInTheFuture) sounds good to me and fits the REST nice.
Hope this clarify the things for you!
[EDIT]
I believe jannis is right. It should be the verbs (GET, POST, PUT, PATCH etc) that make the action instead of the URLs. The urls are just respresenting the resources. My mistake. So both of your points of view are correct. It's just how convenient each approach is for your project (for now and for the near future of your project). IMO, I see #1 (two different restful controllers) more approchable.
Cheers and sorry for any misconception!
Creating my first REST API and I have a question. I have a resource venue:
/venues
each venue has guests:
/venues/1/guests
This is where I am confused about. If I want to update a guest resource, is it better to do this:
POST /venue/1/guests/1/
or
POST /guests/1
The second option is shorter and since guests ids are unique, the 2nd one works just fine. But first is more explicit.
So I am stuck on which route to take from here.
Thanks
Both are pretty much the right approaches although, if you guarantee the clients (client developers) that the guest ids are always going to be unique regardless of the venue they visit, I will go with
POST /guests/{guestId}
I will normally use this:
POST /venues/{venueId}/guests/{guestId}
when I have to change the status of that guest for that given venue. For example, Guest John Doe with id 1 has RSVP'ed "yes" for the venue with venueId 1 then I will POST the data to the following url (this is just an example where it is assumed that the only thing you can add/update for a given guest for a given venue is the RSVP data):
POST /venues/1/guests/1
request body: {"RSVP", true}
but if I want to update John Doe's name to John Foo, I will probably do
PUT /guests/1
request body: {"firstName":"John","lastName":"Foo"}
I hope that helps!
You can think of you having two RESTful resources to manage
A) Venues and
B) Guests
These resources can be independently managed using POST/GET/DELETE/PUT (CRUD operations)
POST /venues/
GET /venues/
POST /guests/
GET /guests/
Also you can use CRUD to map one resource to another like
POST /venues/[venue-id]/guests/[guest-id]/
Let's say I have the following entities in my libraries app - Library Room, shelf, Book.
Where Room has N shelves, and shelves have N Books.
Now the following url brings me a list of books whose
library is 3, room no. is 5 and shelf no. is 43.
.../library/3/room/5/shelf/43/books
Assuming shelf 43 is unique per room only
(There is shelf 43 also in other rooms)
and Rooms are not unique (There's a few room no. 5 ) in the library.
Here is my questions:
I want to filter with more fields on the entities, here is what i want to do
(representation not in rest):
.../library/id=3&type=3/room/decade=21&topic=horror/shelf/location=east&/books
This is not rest.
How do I represent it in rest?
Notes:
I don't want to do this way
.../books¶m1=X¶m2=X¶m3=X¶m4=X
because not all params are related to books.
Couple of things that you need to look into while designing your apis.
1) are type, decade, topic etc required fields? if so, I will probably make them a part of the path itself, such as:
../libraries/{libraryId}/type/{typeId}/rooms/{roomId}/decades/{decadeId}/topics/{topicName}/shelves/{shelfId}/locations/{shelfLocation}/books
Here I am assuming that each library can have rooms which have unique room ids per library, each room can have shelves which has unique ids/locations per room (and so on and so forth). Yes, the url is pretty long, but that's kind of expected
2) if these fields are not required, you could use a different approach which is a bit less verbose but a bit more confusing for client developers who have never used such approach here. Here's a straight up example Restful Java with JAX-RS by Bill Burke
#Path("{first}-{last}")
#GET
#Produces("application/xml")
public StreamingOutput getCustomer(#PathParam("first") String firstName,
#PathParam("last") String lastName) {
...
}
Here, we have the URI path parameters {first} and {last}. If our HTTP request is
GET /customers/bill-burke, bill will be injected into the firstName parameter and
burke will be injected into the lastName parameter.
If we follow this somewhat academic approach (I have not seen this implemented on many platforms. Most platforms normally go with approach # 1, a more verbose but clear approach), your URL would look somewhat like this:
../libraries/{libraryId}-{typeId}/rooms/{roomId}-{decadeId}-{topicName}/shelves/{shelfId}-{shelfLocation}/books
This way, if the client developer doesn't pass in the non-required fields, you can handle it at the business logic level and assign these variables a default value, for example:
../libraries/3-/rooms/2-1-horror/shelves/1-/books
With this url, libraryId = 3, typeId = null (thus can be defaulted to it's default value) and so on and so forth. Remember that if libraryId is required field, then you might want to actually make it a part of the pathparam itself
Hope this helps!
I'm developing a REST API, and I've got the following tables in the DB: classes, subjects, classes_subjects; and the following resources: http://api.example.com/class resource for the classes table, and http://api.example.com/subject resource for the subjects table.
I would like to assign a subject to a class of students (e.g. assign Literature to class 1), i.e. save the class_id and subject_id in the classes_subjects table, but I've got a hard time figuring out how to name the new resource for the classes_subjects table.
I can't name it http://api.example.com/assign because that would be against REST principles, but it would also be awkward to name it http://api.example.com/classes-subjects
Should I just use the /class resource and use PUT when assigning a subject?
I can't figure out any noun to use for assigning subjects to classes. Does any of you know how to handle this kind of issue?
You can use the following schema:
Get all classes with subject 123:
GET /subjects/123/classes
Associate a class with a subject:
POST /subjects/123/classes?classId=<classId>
Hi guys I have a situation where on a form I'm taking in orders for a car servicing application. I have the following models:
Car
belongs_to :car_company
Car_company
has_many :cars
Services
attributes_accessible :car_company_id, :car_id
#virtual attributes
attributes_accessible :car_company_name, :car_reg
The thing is that on a single form the user can enter in the name of the car company as well as the registration number of a car. If the company name doesnt exist it creates a new company and associates it with the service and the same goes for the car. I got this part working however the thing is that I want that on submitting this form the car created should be automatically associated with the car_company whether the carcompany exists or doesn't exist.
I'm pretty stuck here on how to get this thing done the right way? Its basically just to avoid having to enter the car details and the company details seperately just to use them on a form. Any ideas guys?
I see you are using an unconventional model name. By convention in rails, your model should be CarCompany. However, I think what you have will work.
Putting something like this in the appropriate controller may be something like what you want. If not, please clarify what you want.
car_company = Car_company.find_or_initialize_by_name(params[:car_company_name])
car = Car.find_or_initialize_by_registration(params[:car_registration])
car_company.cars << car
car_company.save
You actually may be able to combine the middle two lines with car_company.cars.find_or..., but I'm not sure if that works or not.
I hope that helps.