Jump to content

Thoughts on CRUD API design?


Sepodati

Recommended Posts

I'm looking for any thoughts or feedback on this CRUD API design. (CRUD being Create, Read, Update, Delete, for those that don't know.)

 

I've got three resources that I interact with in my program: interfaces, configs (for those interfaces) and templates (for the configs).

 

My plan is to implement a CRUD-based API through the URL. I've already gotten mod_rewrite configured to support this overall structure in the URLs.

 

http://example.com/[crud]/{interface(s)?|config|template}/{interface-id|template-id}?
So to Read the current configurations applied to all interfaces, you'd request /r/config/. To request the config for a specific interface, you'd request /r/config/enp0s3, for example.

 

/d/config/enp0s3 would delete the configuration on that interface. /r/interface or /r/interfaces would get you a list of all interfaces.

 

Everything returns an HTTP 200 OK response and a JSON object, if successful.

 

I plan to use HTTP 400 codes if there is an error with the data the client sends (invalid interface or config value) and HTTP 500 codes if there's an error on the server (can't run command line program).

 

So trying /r/config/enp0s999 would return a 404. /u/interface/eth0 would return a 501 because I do not support updating interfaces. /d/interface/eth1 would return a 503 because you can't delete interfaces.

 

Create or Update actions would be expected to have the relevant information sent in the POST data. Although I guess I could use $_REQUEST so that it wouldn't matter if the data came in via POST data or the query string.

 

I don't plan for any security for accessing the API. This is meant for dev environments. So if you can send the URL, you can "crud" whatever you want.

 

 

I chose this versus a REST design, processing HTTP PUT, DELETE, etc. requests, to keep the client simple. You can interface with everything via a GET request and a properly formed query string, although I'll personally use POST for create/update actions.

 

I know this kind of rambles a bit, as I'm still thinking things through myself. After 10 years away from this stuff it's both fun and daunting to get back into it again.

 

I'd appreciate any thoughts you have, though.

 

-John

Link to comment
Share on other sites

Allowing GET requests to change data violates the HTTP protocol and is potentially harmful. I also fail to see how building a reinvention of HTTP on top of HTTP simpifies anything. If at all, it creates a lot of extra complexity and confusion.

 

The HTTP protocol is very simple: One resource has one URL, and clients can interact with it using various methods. URL parameters, cookies and body parameters have clearly defined meanings. All HTTP clients already support this, and this is how they expect it to work. In your case, you need an entire set of URLs (which aren't actually resource locators), you completely ignore the method (which can break caching or, even worse, lead to unwanted caching), and you throw all parameters into one big bucket (which can lead to name collisions and breaks the parameter semantics).

 

This sounds like a bad idea.

Link to comment
Share on other sites

Hmmm.... I agree with your point on using GET to change data.

 

I'll have to consider everything... I think that it's easy enough, via jquery/ajax, for my own UI to send any relevant PUT or DELETE requests (GET and POST are obviously easy). I just don't know how easy it is for anything else to generate those same requests to the API. I expect that simply requesting /r/config/eth0?delay=500ms is pretty easy to do in any language, though.

 

I appreciate your input on this.

Link to comment
Share on other sites

I just don't know how easy it is for anything else to generate those same requests to the API.

 

I have never encountered any HTTP client in any language that doesn't understand methods.

 

Classical HTML forms don't support all methods, but you can simply tunnel PUT and DELETE through POST requests. This is a common workaround.

Link to comment
Share on other sites

Well, I don't like doing things the wrong way, even if they work and are easy. So you've changed my mind.

 

With that, do you have any suggestions on how I should organize my resources.

 

I have physical interfaces on the machine as one resource. Those interfaces may or may not have a netem configuration applied to them. Templates can also be defined, which will assign a saved configuration to an interface.

 

I need an API to get a list of all the interfaces on the machine. I also need an API that'll return all of the applied configurations to interfaces on the machine. I need to be able to assign or delete a configuration to an interface (maybe update, but that's a read/delete/create action on the backend). I need to be able to assign a template to an interface and create/update/delete those templates.

 

My thought is that I'm not creating or deleting interfaces, I'm creating or deleting configurations which reference an interface. This aligns with my original post, such that

 

GET /interface(s)/ = returns list of all interfaces

GET /config(s)/ = returns details of configurations currently applied to interfaces. If no configuration is applied to eth0, then eth0 would not be in the list returned.

POST /config/eth0/ = assign a new configuration to the eth0 interface with the data in the POST body

POST /config/eth1/template = assign a new configuration based on "template" to the eth0 interface

DELETE /config/enp0s3/ = delete the configuration from the enp0s3 interface

GET /template(s)/ = returns list & details of all templates

GET /template/xxx/ = returns details for template "xxx"

POST /template/abc/ = create template "abc" with data in POST body

DELETE /template/xyz/ = delete template "xyz"

 

I'm looking at this as a "configurations have an interface" versus an "interfaces have a configuration" viewpoint. Not sure if that's right.

 

Any other type of request not listed (unless I missed something) would return a 4/5xx status.

 

Thoughts on that?

Link to comment
Share on other sites

I'm looking at this as a "configurations have an interface" versus an "interfaces have a configuration" viewpoint. Not sure if that's right.

Sounds backwards to me. I'd go with the "interfaces have a configuration" view.

 

Then, since your configurations belong to interfaces, I would just put them under your /interfaces namespace.

  • GET /interface/{name}/config - Get the current configuration
  • PUT|POST /interface/{name}/config - Update/create the configuration.
  • DELETE /interfaces/{name}/config - Delete the configuration
This assumes that an interface only ever has one active configuration. If that's not the case you may need to tweak things.

 

 

If you want to get a list of all your interface configurations in a single request you could

  • Have GET /interfaces return the list of interfaces including the current configurations (I would probably do this)
  • Use a query string and have GET /interfaces?config return the list of interfaces with just the configuration. The query string specifies which properties to return.
  • Do something like GET /interfaces/config
Link to comment
Share on other sites

Question is, do I need /config at all? /interface/eth0/ will return (GET) a config, set (POST) a config or DELETE a config. /interface/eth1/template/ will set (POST) a template to the interface. If I stop thinking of the config as a resource and instead as properties of an interface, that makes sense to me. Then I could do things like /interface/eth0/delay or /interface/eth1/rate to get specific properties?

 

Then, /interface/ would just return all interfaces and their config, if they exist.

 

I was always thinking "I don't care about the interface if there's no config", but that decision should be left up to the UI.

Link to comment
Share on other sites

You shouldn't bind the API too closely to the implementation you happen to use at the moment. Chances are the implementation will change, and that shouldn't force you to change or reinterpret all URLs.

  • /interfaces should represent all physical interfaces which you can list, filter etc.
  • /interfaces/eth0 is a particular interface (not a configuration). Deleting it makes no sense.
  • The configuration can either be an attribute of the interface or a subresource. If it's an attribute, you UPDATE /interfaces/eth0. A subresource makes sense if you want to support multiple configurations: /interfaces/eth0/configurations.
  • Templates are a separate resource: /templates. Applying a template is just a special case of creating a configuration, so there should be no special URL for that.

Make sure you fully understand the difference between PUT, POST and UPDATE: PUT creates a new resource under the specified URL. POST appends data to a resource or creates a subresource under a group resource. UPDATE changes the data of a resource.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.