Getting a WCF API Right

Having worked on Huddle’s API for 6 months now, and my own now for some time, I’ve come across plenty of mistakes along the way. REST vs SOAP, defining RESTful URL’s, documenting the API, versioning the API etc… Having said that I thought I’d release a few pointers of what I’ve learnt.

  • REST over SOAP. Ok SOAP has its uses, but come on, it can be such a pain. We used it previously, and still do in some of our legacy code. But moving forward we’re only using REST.
    • REST is more light weight and easier to implement (from a UI perspective).
    • REST can be implemented in different formats, such as JSON or XML.
    • REST is cleaner and more intuitive to use
  • Version your web service. We actually did this from the start, but it’s well worth a mention. You don’t want to annoy people by making breaking changes to existing functionality. It’s actually quite easy to do.
    • Create folders in your project for each version, ie v1, v2, v3 etc…. This gives structured hierarchy and also stops namespace clashes as any new .svc’s you create will have the namespace Rob.WebService.v1/v2/v2.MyService.
    • Make sure your documentation is versioned too. We did this by using the XML output generated by MSBuild when compiling a project, and then LINQ for XML to navigate through it and only show information relating to a certain version.
    • The xml comments in C# means you don’t have to just use the standard “summary” etc… nodes, but can also implement your own ones. We use “url”, “id” etc… so that when the project builds, they are included in the .xml output file. We can then access them using LINQ on our documentation page. For example:
      /// get.my.tasks
      /// Gets my tasks. This can be filtered by the status using a....
      /// https://api.huddle.net/v1/json/tasks
      /// ?status={status}
      /// GET
      ///
      /// User authentication required
      ///
      /// [{"Data":
      /// {"AssignedTo":{"Users":[{"Id":345653,"Name":"Example User","Team":"My Team"}]},
      /// "Id":14877212,
      /// "Title":"My First Task",
      /// "WorkspaceId":134076,
      /// "WorkspaceName":"My First Huddle"}],
      /// "Error":null,"Success":true}
      ///
      [OperationContract]
      [WebGet(UriTemplate = "tasks?status={status}", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
      ResponseWrapper GetMyTasks(string status);
  • Make sure you API will scale ok. We originally started with one .svc, and had to start breaking it up as it got larger and larger. It’s worth thinking ahead, and designing your .svc’s in such a way that will allow you to scale out. For example, anything relating to Discussions, is in Discussions.svc, and so on…
  • Use Rewrite Rules. Having .svc files in the url looks horrible. This relates heavily to how you scale (previous point) your api. If carefully done, you can get away with only a few rewrite rules (which is ideal). Otherwise you can potentially end up writing multiple rewrite rules for each instance.
  • Don’t break the rewrite rules! As you write more rules and edit existing ones you have to ensure you don’t break any existing API calls. As part of regression testing, you can use TinyGet. It’s a small application that comes as part of the IIS6 Resource Kit.
    • Write a TinyGet command for each api call (I’ll post an example later). For now refer to my earlier TinyGet post.
    • For each command, you can assert to get a 200 (ie not a 404). Get a 404 for any calls and you know you’ve broken something. We use basic authentication on our API, but can still assert that we should receive a response.
    • After code completion, run the TinyGet commands (we have all of ours in separated .cmd files). Or even better run them as part of the build via NAnt.
  • Be hard, but not too hard. Hard urls, such as personal/files/locked are great, but when you want to make your API queryable, it can become problematic. Instead, we now only use a hard url up to the object type.
    • Instead of /personal/files/locked, a better url would be /files/?type=personal&status=locked.
    • Ok, so it involves using a querystring (which I don’t like). And it took me a while before I began to like it. But it allows for a much more full, queryable API. Should you wish to add extra parameters you easily can.
  • Include the format being returned in the url. We currently use, /v1/json/files. I would have liked /v1/files.json, with the format as the extension (.json, .xml etc…) but had a few problems getting it to work with WCF (anyone else tried?). I think Twitter use this approach and quite like it.

That’s it for now. We’re currently playing around with DTO’s, cookie authentication and basic authentication (we’ve actually had this for a while). So plenty more to detail and I’ll give a few rewrite rule and code examples next time.

If you would like to see how our API currently looks visit my.huddle.net/api.

Update 23/04/2009

Having now used WCF for our API for 6 months or so we’ve found many of its downsides. It’s basically not the ideal solution, especially when developing a REST based API. We recently looked at the Microsoft MVC framework, and OpenRasta as two alternatives, and finally decided on OpenRasta as it suits a request/response design much better, and will less hacking/changes etc… I’ll try to blog on this at some point.