NotionCommotion Posted April 10, 2019 Share Posted April 10, 2019 I implemented an endpoint which receives name/value pairs: PUT someresource/123 {"name": "description", "value": "Some new description"} I think I made a mistake, and should have implemented it as: PUT someresource/123 {"description": "Some new description"} The reason I think so is updating multiple properties is only (easily) possible using the later: PUT someresource/123 {"description": "Some new description", "otherProperty": "bla bla bla"} Assuming I am not using some 3rd party client library which only works with name/value pairs, any compelling reason why one shouldn't default to the later approach with key/values? Quote Link to comment Share on other sites More sharing options...
requinix Posted April 10, 2019 Share Posted April 10, 2019 It should be either [{"name": "description", "value": "Some new description"}, ...] or {"description": "Some new description", ...} The second one tends to be much nicer to work with. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted April 10, 2019 Author Share Posted April 10, 2019 Gotcha! I will go with the second. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted April 11, 2019 Author Share Posted April 11, 2019 Before moving on... The following seems clean enough: //PUT /some/resource/123 body: {"description": "Some new description", "name": "new name" ...} $app->get('/some/resource/{id:[0-9]+}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $params=$request->getParsedBody(); foreach($params as $name=>$value) { $resource->$name=$value; //but implement more securely } }); If I only wanted to update a single property (i.e. {"description": "Some new description"} ), the same server code works. But I can also change the client request and do something like the following. //PUT /some/resource/123/description body: Some new description $app->get('/some/resource/{id:[0-9]+}/{property}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $value=$request->getBody(); $resource->$args['property']=$value; //but implement more securely }); Under normal circumstances, probably doesn't make sense. Agree? What if I wasn't updating some value stored in the DB but some "option" object which is stored as MySQL type JSON and wanted to keep it more flexible? { "title": { "text": "Activity", "style": { "fontSize": "24px" } }, "tooltip": { "borderWidth": 0, "backgroundColor": "none", "shadow": false, "style": { "fontSize": "16px" }, "pointFormat": "{series.name}<br><span style=\"font-size:2em; color: {point.color}; font-weight: bold\">{point.y}%</span>" }, "pane": { "startAngle": 0, "endAngle": 360, "background": [{ "outerRadius": "112%", "innerRadius": "88%", "backgroundColor": "rgba(124,181,236,0.3)", "borderWidth": 0 }, { "outerRadius": "87%", "innerRadius": "63%", "backgroundColor": "rgba(67,67,72,0.3)", "borderWidth": 0 }, { "outerRadius": "62%", "innerRadius": "38%", "backgroundColor": "rgba(144,237,125,0.3)", "borderWidth": 0 } ] }, "yAxis": { "min": 0, "max": 100, "lineWidth": 0, "tickPositions": [] }, "plotOptions": { "solidgauge": { "dataLabels": { "enabled": false }, "linecap": "round", "stickyTracking": false, "rounded": true } } } I was thinking of something like the following to implement: //PUT /some/resource/123/option/foo/bar/someproperty body: TBD $app->get('/some/resource/{id:[0-9]+}/option/{subproperties:.*}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $properties=explode('/', $args['subproperties']); //Update specific property in $resource->option using $properties }); For this it kind of makes sense to target the specific property, but this is contrary to the earlier approach. Or maybe I should keep with the {name: value} approach? Quote Link to comment Share on other sites More sharing options...
requinix Posted April 11, 2019 Share Posted April 11, 2019 Can't say I agree with that approach. It's too weird. Stick with the standard: the data in the request body. If, however, modifying one or three specific properties is a common use case then that suggests your API should support it with a specific operation. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted April 12, 2019 Author Share Posted April 12, 2019 (edited) 12 hours ago, requinix said: Can't say I agree with that approach. It's too weird. Stick with the standard: the data in the request body. Just to make sure I was correctly communicating on my previous post, to change option.plotOptions.solidgauge.dataLabels.enabled from false to true, I would do one of the following requests: PUT /some/resource/123/option/plotOptions/solidgauge/dataLabels body: {enabled: true} //or PUT /some/resource/123/option/plotOptions/solidgauge/dataLabels/enabled body: true //If this is an acceptable approach, which request should be used? and then get resource 123, get the option property, and finally use [plotOptions, solidgauge, dataLabels, enabled] to target the specific property. Still too weird? And to make sure I understand you, stick with the standard where the entire option JSON string is in the body, right? Edited April 12, 2019 by NotionCommotion Quote Link to comment Share on other sites More sharing options...
requinix Posted April 12, 2019 Share Posted April 12, 2019 Yes. PUT should be the entire body, PATCH should be whatever the changes are, and POST can sorta be either. So if you want to support a generic API for updating specific properties, use PATCH. PATCH /some/resource/123 HTTP/1.1 Content-Type: application/json {"option":{"plotOptions":{"solidgauge":{"dataLabels":{"enabled":true}}}}} Unless you think a sort of "enable/disable chart label" API would make more sense, along the lines of /toggle/label/name/state: POST /some/resource/123/toggle/label/solidgauge/enabled HTTP/1.1 Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted April 13, 2019 Author Share Posted April 13, 2019 Ah, that is when I use PATCH! I've never used the method, but was actually planning on using this approach but as PUT. I even came up with the following to use in concert with xeditable. function conv2obj(keys, value, object) { keys=path.split('.'); object=object?object:{}; var last = keys.pop(); keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value; return object; } var o=convert('option.plotOptions.solidgauge.dataLabels.enable', true); //{"option":{"plotOptions":{"solidgauge":{"dataLabels":{"enable":true}}}}} Shouldn't your second example us PUT since it is idempotent? It doesn't replace the entire /some/resource/123 resource, but it does replace the entire /some/resource/123/toggle/label/solidgauge/enabled resource. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.