Posted on 02/10/2013. By Pete Otaqui.
I love Digital Ocean – they offer great value for money, excellent support and a really refreshing and clean interface. However their API sucks, and this is something they should probably try and deal with if they want to expand beyond individuals or small companies into providing services for larger applications and corporates.
Versioning the API
First off, I think they should version their API . Some people think that REST precludes versioning in a URL but personally I don’t agree. RESTful APIs aid in discovery, but do not make it automatic, and adding a version at least allows concurrent incompatible implementations – especially important when you are still developing a service. The devx article linked to there suggests that “/v1/” or similar in an API URL is a bad idea, but doesn’t say that you can’t provide such versions from subdomains, so I guess that would appease the author.
Let’s suggest then that they could have all API requests to URLs like this:
https://api-1.digitalocean.com/
Allowing them to provide a different API in the future without breaking existing clients, such as:
https://api-2.digitalocean.com/
There are other ways of adding versioning, within the url (i.e. https://api.digitalocean.com/v1/
) but that ship has mostly sailed for DO with their current implementation in place, or by requiring extra header information to specify
API version in requests.
Be at REST
Assuming it’s now possible to create a new version of the API, let’s look at what that might contain (spoiler: more than only GET requests ).
I’m going to apply a more correctly RESTful style of API using the appropriate HTTP verbs to their current API actions – I’m not going to suggest any new features.
Droplets
Method | URL | Description |
---|---|---|
GET | /droplets |
Show All Active Droplets As original |
POST | /droplets |
New Droplet As original
|
GET | /droplets/:droplet_id |
Show Droplet As original |
GET | /droplets/:droplet_id/actions |
Get a list of actions available on this server Lists the actions available, i.e. |
POST | /droplets/:droplet_id/actions |
Perform an action on a droplet. The action and parameters are provided in the post body, i.e.:
|
DELETE | /droplets/:droplet_id |
Destroy a droplet |
GET | /droplets/:droplet_id/snapshots |
List Snapshots |
POST | /droplets/:droplet_id/snapshots |
Create Snapshot |
GET | /droplets/:droplet_id/snapshots/:snapshot_id |
Show Snapshot (NB – equivalent to |
DELETE | /droplets/:droplet_id/snapshots/:snapshot_id |
Delete Snapshot (NB – equivalent to |
GET | /droplets/:droplet_id/backups |
List Backups |
GET | /droplets/:droplet_id/backups/:backup_id |
Show Backup (NB – equivalent to |
Regions
Method | URL | Description |
---|---|---|
GET | /regions |
List regions |
Images
Method | URL | Description |
---|---|---|
GET | /images |
List Images |
PUT | /images/:image_id |
Transfer Image Supply |
DELETE | /images/:image_id |
Destroy Image |
SSH Keys
Method | URL | Description |
---|---|---|
GET | /ssh_keys |
List SSH Keys |
POST | /ssh_keys |
Create SSH Key |
GET | /ssh_keys/:ssh_key_id |
Show SSH Key |
PUT | /ssh_keys/:ssh_key_id |
Update SSH Key |
DELETE | /ssh_keys/:ssh_key_id |
Destroy SSH Key |
Sizes
Method | URL | Description |
---|---|---|
GET | /sizes |
List sizes |
Domains
Method | URL | Description |
---|---|---|
GET | /domains |
List Domains |
POST | /domains |
Create Domain |
GET | /domains/:domain_id |
Show Domain |
DELETE | /domains/:domain_id |
Destroy Domain |
GET | /domains/:domain_id/records |
List Domain Records |
POST | /domains/:domain_id/records |
Create Domain Record |
GET | /domains/:domain_id/records/:record_id |
Show Domain Record |
PUT | /domains/:domain_id/records/:record_id |
Update Domain Record |
DELETE | /domains/:domain_id/records/:record_id |
Destroy Domain Record |
Events
Method | URL | Description |
---|---|---|
GET | /events/:event_id |
Show Event |
In this process I’ve tried to take a pragmatic approach to the API. You could easily make the case that to be more theoretically correct a lot of the droplet “actions” should be done with a PUT request to /droplets/:droplet_id
and
while I think that would work fine, it seemed to me that splitting out the actions to a different URL made things a bit more obviously with less likelihood of messing up with trying to PUT too many things at once.
I do think at least this much work on the API is worthwhile – I’m somewhat terrified of GET requests that perform actions (especially if those actions might cost me money). It’s nice to write scripts against APIs, and a very nice safety belt if you know that you’re only doing (safe) GET requests in a loop or something.
There are a couple of extra features I would like to see DO to implement:
- Better Auth so we don’t have to include the client_id and api_key right there in the URL .
- Although this isn’t related to the API, I’d love to see custom data applied to new droplets in the same way that AWS / OpenStack et al manage it. So I can apply a small amount of custom data to dynamically created droplets without having to the lengths of making lots of images or some other out-of-bound system to manage config data.