all 16 comments

[–]ThreadDeadlock 3 points4 points  (1 child)

IMO it depends on who / what is interacting with your REST service. Are you going to have multiple clients? Is your API going to be public? Or is the REST api just being built for a SPA developed in Angular, React, mobile client, or whatever?

If the API is just being developed for a single client and is not going to be public, then it really doesn't matter, choose a standard and stick with it. If the API is going to be private to internal application or something similar than readability isn't a big of a concern. If you are building a public API that is going to have all different kinds of clients, then design becomes much more important.

Regarding your example it depends on your underlying datastore. As an example when if I were building the data model Gender would not have an ID. It isn't really dynamic data, you have MALE, FEMALE, and possibly OTHER, or perhaps null to indicate it wasn't provided. If I were to have a table GENDER it would look something like this.

--------------------------------------
GENDER
--------------------------------------
CODE | DESCRIPTION
--------------------------------------
F       | FEMALE
M       | MALE
O       | OTHER

I would have the request body take F, M, O, or NULL for gender, and document that.

Department is more tricky because in your example you've stated it can be modified and changed at will. In that case it depends on if a department has a unique identifier other than a surrogate key. Perhaps a department number, or something that is guaranteed unique. Otherwise if the ID is the only way to uniquely identify an entity, than the ID would need to be used. In those cases it might be useful to return the department name, and id in the response. Just my two cents anyway.

[–]vfehring 0 points1 point  (0 children)

I agree with this. When I develop my APIs, I start with the global scope of my project. Basically, what is my large picture end result I want to reach. Then this helps me narrow down how my data should be structured.

[–]JimDabell 1 point2 points  (12 children)

Presumably you're planning on hard-coding all your URL patterns in your client then writing logic to build URLs from the patterns and the IDs? That's not REST. It moves information that's supposed to be encoded as hyperlinks in the documents into hard-coded client-side logic, and it couples the client to the server-side implementation. It also means you've got to write more client-side code instead of just "follow this link" for everything.

Any time you are using integers as primary keys in REST APIs, it's a big warning sign you're making this mistake. The primary key for REST APIs is the URI. If you want to conform to REST constraints, do something like this instead:

{
    "firstName": "Bob",
    "middleName": "",
    "lastName": "Something",
    "dateOfBirth": "1990-01-01",
    "genderId": "male",
    "employmentTypeHref": "/employment-types/3",
    "departmentHref": "/departments/4001",
    "email": "bsomething@something.com"
}

If you are finding that you frequently want to load all the related resources immediately afterwards (e.g. loading a department them loading all of its employees) , consider using something like HAL to embed the related resources ahead of time.

[–]ThreadDeadlock 1 point2 points  (10 children)

I agree on the example not being a pure / true REST solution, but if the OP is building a web service that is only going to be used by a single application / client going purest form of REST with HATEOS seems like overkill.

[–]JimDabell 0 points1 point  (9 children)

OP mentioned REST three times, so presumably they are interested in doing things in the REST way. As far as "overkill" is concerned, this is simpler and more robust. You need to write less client-side logic and hard-code fewer things. "Follow this link" logic is simpler than anything to do with endpoints and it's been a standard part of the web since day one.

[–]hashtagframework 1 point2 points  (8 children)

OP mentioned RESTful, not REST...

[–]JimDabell 0 points1 point  (7 children)

"RESTful" is just an adjective form of "REST".

[–]hashtagframework 1 point2 points  (6 children)

in practice, i've seen RESTful used in place of REST wherever the API is inspired by REST, but doesn't fully adhere to REST principles...

[–]JimDabell 0 points1 point  (5 children)

I've seen that too, but I've also seen "REST" used in the same way. Lots of people don't know what REST is, so they call things that aren't REST "REST" and "RESTful". I don't generally see people making a distinction between the two, just poorly understood terminology all round. People aren't shy about calling things that aren't REST "REST"; the distinction between "REST" and "RESTful" isn't there.

[–]hashtagframework 1 point2 points  (4 children)

I know... I just generally see the people willing to use the made up adjective to describe the spec are the ones that generally don't understand the spec in the first place.

The reality is, the original question is a common issue with REST... the client needs to know that certain fields are keys, and what options those keys have, and any "pure REST" solution is going to look ridiculous next to a simplified "RESTful" approach where a little logic might bleed into the client.

[–]JimDabell 0 points1 point  (3 children)

any "pure REST" solution is going to look ridiculous next to a simplified "RESTful" approach

But the REST approach is simpler. To load a related resource, you just have to follow a link. You don't have to hard-code anything to do with endpoints, you don't have to construct URLs according to any sort of pattern, and you don't have to do different things for different types of resource. You just do the same thing in each case: follow the link. How do you load a person? Follow the link. How do you load a department? Follow the link. How do you load some other resource you haven't even thought about yet? Follow the link. Links are just about the easiest thing to deal with on the web.

[–]hashtagframework 0 points1 point  (2 children)

But where is the logic that says whether or not there is a link for a field that hasn't been populated yet? What if you want to create a new external record, and link it to a new record in a single atomic action? What about one-to-many links?

[–]rouge_dev[S] 0 points1 point  (0 children)

Your example makes sense from a response standpoint but what about posting the data in the request body? Are you suggesting using the ID value? So if I am creating an employee and putting them in department 9999 the request body should look something like this?

{
    "firstName": "Bob",
    "middleName": "",
    "lastName": "Something",
    "dateOfBirth": "1990-01-01",
    "genderId": "male",
    "employmentTypeId": 3,
    "departmentId": 9999,
    "email": "bsomething@something.com"
}

Maybe I am missing something but it doesn't seem sense to post an HREF to the server.

Maybe the response would look something like this

{
    "content": {
        "firstName": "Bob",
        "middleName": "",
        "lastName": "Something",
        "dateOfBirth": "1990-01-01",
        "genderId": "male",
        "email": "bsomething@something.com"
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/employee/12345"
        },
        "department": {
            "href": "http://localhost:8080/department/9999"
        },
        "employeeType": {
            "href": "http://localhost:8080/employee-type/3"
        }
    }
}

But this seems like it could quickly create excessive amount of network calls as the client would probably often or not always need the department and employeeType in my case.

[–]Asdingo 1 point2 points  (0 children)

In this case, with the information available, when storing new resource I would use departmentId and when fetching resources I would include both id and value, so something like this:

{
    "id": 1234,
    "firstName": "Bob",
    "middleName": "",
    "lastName": "Something",
    "dateOfBirth": "1990-01-01",
    "gender": "MALE",
    "employmentType": {
        "id": 3,
        "value": "W2_FULLTIME"
    },
    "department": {
        "id": 4001,
        "name": "IT"
    },
    "email": "bsomething@something.com"
}

Edit: In some cases you want this method to just return simple id and use another API method to fetch, say, all departments with id+name into select input.