1

We're creating a new API to add new Jobs into a legacy system. The question deals with how to handle creating a Job's required parent entities when you don't know the IDs of those parent entities to assign to. The structure of when a Job is created is as such:

  • A Job must be assigned to a Project.
  • A Project must be assigned to a CustomerAccount.
  • There is no knowledge of what the ID is of the Project it should be assigned to. This goes by the Project Name instead.
  • Additionally, two CustomerAccounts could have the same Project Name. For that reason, we also need to pass over the Name of the CustomerAccount.

To summarize, we need to create a new Job while also passing over the Project Name and CustomerAccount Name since we don't have access to the IDs to use. If the Project is not found, one is automatically created then assigned to the Job about to be created.

With this established, the question is: What is the best practice for implementing a REST endpoint that will also create required parent entities?

Our current solution is:

POST /jobs/account/{accountName}/project/{projectName}

(with the Body containing the JobDTO).

In this scenario, what are some alternative solutions or better ideas for the naming of this endpoint?

Thanks in advance.

1

To summarize, we need to create a new Job while also passing over the Project Name and CustomerAccount Name since we don't have access to the IDs to use. If the Project is not found, one is automatically created then assigned to the Job about to be created.

Keep it simple. You don't need the project id or account id (I assume these are database ids). You already have a name that the client already knows about. These names already uniquely identify the jobs. So just PUT the new job resource to

PUT /accounts/{accountName}/projects/{projectName}/jobs/{jobName}

A side effect of this is that the server can create what ever it needs to create sot that accountName and projectName now exist on its end, so that

GET /accounts/{accountName}

now returns that account resource.

  • Including names in the URL can be a bit tricky and unsafe. For example, simple slash (/) in the name may require encoding name in URL-friendly way. – Roman Susi Jun 5 at 14:56
  • This is the route we ended up going. Although I would usually agree with tom's approach mentioned below with limiting to one hop, what we're trying to accomplish is easier to maintain with only one endpoint with our name parameters. Another factor in deciding to go this route is that this is exclusively an internal call that will only be used by an integration from another app that we have. We've locked it down so that users won't be able to abuse it whether externally or internally. – Ant Jun 5 at 15:55
  • @RomanSusi well yeah but you should be correctly encoding/decoding your URLs anyway. Many frameworks do that for you automatically, so it isn't that big a deal. – Cormac Mulhall Jun 5 at 17:04
  • @CormacMulhall Frameworks usually do have some preferred way for the API. It's not productive to fight too much against chosen framework. OP did not mention any frameworks, thus this small warning. – Roman Susi Jun 5 at 18:51
5

As a rule of thumb with RESTful APIs I've found that you should only ever need to do "one hop" when defining relationships between two entities. As soon as you need to start defining relationships across "two hops" it's normally a sign that the endpoint isn't very RESTful any more.

Endpoints should only really be concerned with one entity. Remember this isn't a web application where users can describe complicated relationships via dropdowns populated by AJAX requests. It's fine to have to make a couple of API calls to achieve your goal. API calls are fast and cheap.

Also you mention that you don't know their IDs. How else are you going to identify the linked entities? Unless they have a unique field that you can identify them by you're going to run into problems. All RESTful APIs rely on having an identifier of some type otherwise everything falls over because no one is ever quite sure what entity you're talking about.

So get the IDs and only allow the API to accept immediate relationships.

You also mention routes. The "one hop" rule applies here too. In a fictitious scenario with authors, posts and tags I would set up my routes like so:

  • /authors
  • /authors/1
  • /authors/1/posts
  • /posts
  • /posts/1
  • /posts/1/tags
  • /tags
  • /tags/1

This keeps your routes small and easy to immediately understand.

  • +1 for not trying to cram parallel operations into a single call. – Nelson Jun 5 at 2:06
  • I disagree with this. CRUD-oriented API can be easier to understand, but truly useful APIs take into account more subtle developer ergonomics by facilitating typical use cases. (Of course, pure CRUD may be the prevalent case for some systems) – Roman Susi Jun 5 at 15:01
0

Maybe this one will be considered a marginal answer, and I do not know all the details of this case, but I came to a conclusion that using a graph instead of "linear" API can save your day.

The exact technology for a graph does not matter much (can be JSON-LD, RDF, GraphQL - you name it), but what is important is the ability to transfer subgraphs from one system to another.

In this way, one can easily include all relationships of the child object. There still may be some missing foreign key concerns in the legacy (presumably relational database-powered) system, but graph API can be conceptually easier because it allows to exchange logically aggregated entities right at the transactional borders of those aggregates.

Of course, some kind of framework is needed to simplify graph processing, but the resulting system and API may be more flexible, especially if the use cases of the API are many and diverse. Graph API is also easily extendable, robust, and require much less requests to exchange the data.

The downside may be familiarity of the development team with the technology and query languages.

Also, no RESTful dogma prevents you from having simple CRUD APIs and graph API for more complex updates / queries.

One more note: tree is also a graph. Can be used to increase performance of operations and does not require special format (JSON or XML would be enough).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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