2

I have an update API (PUT/PATCH) entity/{id} to update a certain entity in my DB. Let's say:

entity {
  id: number, // Primary key, unique, not null
  content: text,
  parentId: number // Foreign key, nullable, points to another entity's Id from the same table
}

When I call this API, if the id doesn't exist in the DB, it throws an exception and returns 404 Not found. No problem here.

My question is: what if my id is valid (i.e. I can update the correct entity), but the payload contains a parentId of an inexistent entity (so that my FK is invalid)? Should I return 400 Bad Request, 404 Not Found or even another 4xx code?

To be clearer, my requisite is: if the parentId is invalid, the entity update should not be made, an error/exception should be thrown and the requester should be informed the request couldn't be fully processed.

I fear using 404 Not Found could mislead the client to believe the target id is invalid. I also fear that using 400 Bad Request might imply the client sent a logically invalid payload (e.g. malformatted) instead of a value that came to be "forbidden", which is not the case.

Thanks in advance!

(I know this question is similar to this one, but the answer goes in a different way, so it doesn't help me...)

3
  • 2
    Is there a reason why you're including the parentId in the request? Are you actually changing the parent ID because of this request?
    – Thomas Owens
    Nov 28, 2022 at 23:14
  • 2
    I fear using 404 Not Found could mislead the client to believe the target id is invalid. I also fear that using 400 Bad Request might imply the client sent a logically invalid payload Both 404 and 400 can go along with a message. Don't just throw a 4xx error and hope for the best. Tell what made the request to be bad request or what wasn't found.
    – Laiv
    Nov 29, 2022 at 8:09
  • @ThomasOwens yes, the parentId can be changed in the request Nov 30, 2022 at 20:14

2 Answers 2

3

While the request is syntactically valid, it violates the constraints of your data model. Thus, a 4xx response is appropriate.

Having looked through all HTTP 4xx client error status codes, none of them seem applicable other than 400 or maybe 409 or 422.

  • 400 bad request – yes, fits here
  • 401 unauthorized – no
  • 402 payment required – no
  • 403 forbidden – no, the request was properly authorized
  • 404 not found – no, this would indicate that no representation for the requested resource exists. However, this create-entity resource does exist in your case, the URL is correct.
  • 405 method not allowed – no, PUT/PATCH are supported in your case
  • 406 not acceptable – no. Sounds good, but relates specifically to content negotiation via the Accept header.
  • 407 proxy authentication required – no
  • 408 request timeout – no
  • 409 conflict – maybe, see discussion below
  • 410 gone – no
  • 411 length required – no
  • 412 precondition failed – no, relates specifically to conditional requests
  • 413 payload too large – no
  • 414 URI too long – no
  • 415 unsupported media type – no
  • 416 range not satisfiable – no
  • 417 expectation failed – no. Sounds good, but relates specifically to server feature checks via the Expect header.
  • 421 misdirected request – no
  • 422 unprocessable content – maybe, see discussion below
  • 426 upgrade required – no

About 409 conflict: RFC 9110 provides the following explanation of this code:

The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource. This code is used in situations where the user might be able to resolve the conflict and resubmit the request. The server SHOULD generate content that includes enough information for a user to recognize the source of the conflict.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the representation being PUT included changes to a resource that conflict with those made by an earlier (third-party) request, the origin server might use a 409 response to indicate that it can't complete the request. In this case, the response representation would likely contain information useful for merging the differences based on the revision history.

While your resource may not have a current representation, the state supplied by the client would be invalid if it were accepted. So arguably, there is a conflict here.

About 422 unprocessable content: RFC 9110 provides the following explanation of this code:

The 422 (Unprocessable Content) status code indicates that the server understands the content type of the request content (hence a 415 (Unsupported Media Type) status code is inappropriate), and the syntax of the request content is correct, but it was unable to process the contained instructions. For example, this status code can be sent if an XML request content contains well-formed (i.e., syntactically correct), but semantically erroneous XML instructions.

This error can be used for semantic problems, as opposed to syntactic errors as perhaps implied by a 400 or 415 error. However, I think the request in your case is semantically OK, it is only an external constraint that is violated.

Conclusion: A couple of errors might fit, and there are arguments for using a more exotic error like 409 or 422. However, a simple 400 “bad request” is definitely not wrong, and is unlikely to cause problems with clients that use your API. I would use a 400 error.

1
  • Tnx for the very detailed answer! I guess I'm gonna go with 400 Bad Request and a good error message Nov 30, 2022 at 20:18
0

I believe a 200 OK with a response that contains an error code, and ideally an error message, is more suitable than an HTTP error code.

The client has issued a request to entity/{id}, which exists. If an invalid ID was provided here, I could see the use of a 4xx error code. For example, a 404 could be used to indicate that there is no entity with the ID {id}. A 403 would indicate that the client doesn't have permission to access or modify the entity with the ID {id}. A 405 can be returned if the entity with ID {id} cannot be modified by the PUT or PATCH request the client sent. A 415 could be returned if the server was expecting valid JSON but received invalid JSON or something else entirely (such as XML). A 429 would indicate that the client is making too many requests in a given period of time, if you have rate limiting. All of these are about fundamental communication issues between the client and the server.

The problem here isn't about communication between the client and the server. The client has sent a well-formed and valid request object to the server to make a modification to an entity that the client has permission to modify. The HTTP request has been successfully sent from the client to the server and accepted. The definition of the 200 OK status message is for successful HTTP requests, and since the server was able to receive and process the request, the HTTP request is successful.

Once the server begins working on the request, there is an error. Since the client is allowed to change the parent ID of the entity, the receiving application can attempt to perform and update and then find that there is no valid parent ID.

If you absolutely can't return a 2xx error code, the next one that I consider would be a 409. The server could not fully process the request because of the state of the resources. That is, since there is no currently available entity with the specified parentId, the entity could not be updated.

You also have questions that need to be resolved.

If you are allowing the content and the parentId to be updated, what do you do when one is valid and the other is invalid? If the parentId doesn't exist, will you still update the content of the entity with ID {id}? If you return a 4xx error code, I would not expect any aspect of the request to have succeeded. I would expect the entity to remain unchanged, but you may opt to implement a system that updates the content and returns a response indicating which field(s) were updated and which ones had errors (and what those errors were).

You may also want to consider what to do when the {id} in the request endpoint and the id field in the request body don't match. Perhaps this is a 4xx error.

11
  • 1
    HTTP error codes are never about communication between the client and the server, unless there's a proxy involved. Codes are sent back by the server, so if you get any code back at all you know the communication worked. Reading the code is meant to tell you something about happened inside the server.
    – bdsl
    Nov 30, 2022 at 21:44
  • dylanbeattie.net/songs/bad_name.html
    – bdsl
    Nov 30, 2022 at 21:45
  • 1
    @bdsl I don't agree. All of the status codes are about communication between a client and a server over HTTP and not business or domain logic. Once it hits the application layer, you shouldn't be using HTTP status codes anymore.
    – Thomas Owens
    Nov 30, 2022 at 22:12
  • But the original use of HTTP was for web servers serving documents that people have written. That is an application. It didn't say "we can't find the document they want, but they found the web server so we'll send a 200". Or "We can find the document, but it's private so we won't serve it, so we'll send a 200".
    – bdsl
    Dec 1, 2022 at 9:25
  • 1
    The client shouldn't be expected to care about the distinction between layers on the server side.
    – bdsl
    Dec 2, 2022 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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