Joshua asks:
I have a hosted web app that I developed and am selling accounts to and am to the point where I would like to build an API to the data. What would be the basic structure behind an API?
Would it be making a top level CFC that is for authentication and all the others are like "children" of that main cfc so that it gets the authentication status and things trickling down to the others?
APIs are a subject that are near and dear to my heart. For some reason, I've always enjoyed writing ColdFusion wrappers to APIs. Maybe because I love turning some remote complex service into a simple CFC call. Maybe it's the idea of creating a link between two completely separate systems. Whatever it is, it's something I've had a lot of experience with (especially on the consumer side) so I think I can give some good advice here.
I know you are looking specifically at the code setup in regards to authentication, but I'm going to take the opportunity to preach about API design in general as well.
So from a code perspective, what is the best way to design an API that handles performing some arbitrary action while also handling authentication and authorization? Joshua mentioned a top level CFC with children under it. By that I assume he means inheritance. Now I do not profess to be an OO expert in any way whatsoever. I do know that one of the guidelines (not rules, guidelines, since nothing is really black and white) is that you should use inheritance only for a IsA relationship. To me, this scenario does not satisfy a IsA relationship. Instead you would probably have an instance of the authentication code injected into the CFC. This is a bit trickier though over the wire as you don't have a persistent CFC in place. You could - however - simply make use of the Application scope from within the CFC. I know that I've often said you shouldn't use outside scopes in a CFC, but a remote service like this certainly is a special example. Another option is to use ColdSpring to generate your remote CFCs. This will give you full remote access to your CFCs with everything necessary injected into them. But the flip side to that is - if your remote authentication/authorization system has nothing to do with the rest of your application, would you even be injecting that into the service anyway?
And this is where I'd probably suggest building one CFC for the entire API. You probably already have a set of CFCs designed for your application, but if your API is going to have it's own authentication system it probably makes sense to have one CFC work as the gateway to the rest of the system. That leaves the rest of the CFCs alone and lets you focus on one main set of code as your core gateway. Generally this code would do:
- Authenticate the request. This can be done by checking a username and password, a secret key, or maybe all three.
- Authorize the request. This wouldn't apply to all APIs, but perhaps some users can run a limited amount of calls per day, while users (who pay more of course) can run more.
- Direct the request. Depending on what data is requested (latestWeather, latestStocks), your main CFC would handle calling the proper service to fetch the result.
- Return the request. Return the data obviously. Tips on return format below.
- And somewhere in there you probably also want logging as well. Who called me at what time and for what data.
Now that I've talked a bout about the security aspect of the API, let's take it a step higher and talk about the API in general. The following doesn't apply to ColdFusion alone, but to anyone creating a service that would be used by others. Here are some things to consider in no particular order.
KISS (Keep It Simple, Stupid)
I've long said that working with Yahoo and their services is a dream while Google tends to be a chore. I don't think it's because Google is necessary 'better' or 'worse' than Yahoo, but their APIs are not as friendly. In all things I cannot recommend enough that a simpler approach will make life easier for your developers and actually encourage them to use your API. As a good example of unnecessary complexity, Google likes to make you set up multiple requests. They also like to respond with requests that make you request new requests. I'm sure they have a good reason for this as - well - they are Google and are all brainiacs, but I can't help but wonder if they actually use the APIs they build.
Consistency
This is obvious, but try to use similar method names, argument names, and result formats. So for example, take a getWeather and getStockInfo API call. While they return pretty different data, if both calls supported a date filter, you would want to use the same arguments for both. Perhaps beginDate and endDate. You wouldn't want one call using those names and another call using fromDate/toDate. Ditto for results. If you return results in XML, consider a common package type.
Return Type
What format of data should you return? JSON? XML? SOAP? I say return them all. ColdFusion makes this ridiculously easy so there isn't any real excuse to not offer a variety of return formats to the end user. As an example, Yahoo supports JSON, RSS, and SerializedPHP.
Documentation, both technical and legal
Obviously you want to document your API. Good documentation should show both examples of constructing requests as well as response examples. Also be very clear about the legal/usage rules for your API. If your API restricts folks to 1K hits per day, then be obvious. Many of the APIs out there have limits, but a lot of them are vague and seem to be a bit unclear on what they will do when you go over the limit. Even worse, many don't provide a way to go over the limit. I can certainly understand throttling the use of your API, but to not provide a way to give you money and get more use of your API is simply dumb. There should be a clear statement like so:
You may use API Foo 1000 times in a 24 hour period. Each request made from your host is considered one unique request. If you request more than your limit, we will respond with an XML packet that contains the following error code. Contact sales@foo.com if you wish to buy a license for more API calls.
Nothing bothers me more than an API that is vague about what it will do under heavy usage.
Backwards Compatibility
Another big one to watch out for is backwards compatibility. If you update your API, you generally never want to break any old code out there that is assuming a particular format. The flip side to this is - I do think it's fair to warn developers and give them a good amount of time to update. YouTube did this with their API updates. I've seen some services actually provide a whole new entry point (URL) to support a new version of their service. That's something to consider as well.
I hope this is helpful Joshua, and I'm definitely open to suggestions/critiques of the above.
Archived Comments
Thank you thank you!
Very helpful!
As always, I like to peddle using oAuth for public API's. It's great in that there is already a code base for implementing oAuth in ColdFusion, which saves you time, and pretty much every other language out there, saving developers who want to use your API time.
Gotta love those cross-platform open standards!
Oops, forgot the link:
http://oauth.net/
Problem is this isn't very open, I have my own authentication methods and the data isn't open to anyone like it is on Twitter, or flickr, etc.
I want to regulate the authentication of who gets to what data since multiple churches use the same application but with a different church identifier.
Currently my API has a getGroups method and when you pass in your requested authkey it matches it up with a Church and finds that churches groups and delivers it in XML or JSON depending on what you specify.
@JR: So taking my advice above, you could imagine that they would hit a URL like so:
your.com/api.cfc?authkey=X&...
API.cfc will authenticate authkey, and then call _another_ CFC that handles getGroups (perhaps groups.cfc). Again, this is a good place for ColdSpring to handle giving you access to a bean to handle that logic.
enjoyable read :) many thanks. :)
Ray, yeah I actually had this much from yesterday.
http://test.chrrch.com/_com...
You can swap format to XML if you want but nothing else yet.
My auth key will also be a bit more extensive but this is just for testing hehe.
Thanks for all of your help!
I see that you used format=. Folks should remember that returnFormat has a special meaning for HTTP calls to CFCs. Don't use that unless you want CF to handle the formatting.
@Ray - Yeah and I wanted to have more options than the ones that returnFormat offered - seemed limited...
Good timing - I'm giving a talk online later this week that's explores how ColdSpring can help you with exactly some of these problems (though not the design of the API itself, which Ray has covered here in great depth), and it'll even show case a 'token manger' you can use.
The details are at http://coldfusion.meetup.co...
Thanks for the tutorial, Ray :)
@Ray - good post and timing for a new project I'm just starting to work on. It will be a internetconnected desktop-app (java).
I'm still wondering what would be the best way to handle the authentication at the API. Users logon to the desktop-application and can sumbit data-updates from the application (which will be auto-started and so might be running the whole day during which it will do some ping-requests for server-updates etc.).
The 2 alternatives I've been looking at are:
- let the app send the username/password with every request,
- mark the application on serverside as logged on and allow all updates from that application (with an application UUID) as long as the regular ping requests keep coming (clear this authorization when the app is shut-off of after an x-period of time)
- create a cftoken at logon etc. and have the application send that info with each request
- ...
I'm not sure what route to chose, but somewhere out there must have solved this in a some (better?) way?!
@Bart: You can use the token and security services from my site already (even without AOP, though the examples are not yet online): http://falkensweb.com/other...
@Tom - thanks. The SessionToken.cfc looks like it has been thought through quite extensively already. Of course a short example of an implementation could be usefull, but I gather you have different work to do..:-) Do you have anything ready that you can share?
@BB:I'd just send the u/p once. If you are using Flex, I know it can keep a session alive, so you wouldn't need to bother sending tokens back and forth (well maybe Flex does it behind the scenes, but YOU don't have to worry about it). That would be simplest. If you aren't using Flex, then a token of some sort based on a session could work.