Menu

Atom API Update

February 3, 2004

Joe Gregorio

Version 8 of the draft Atom API for weblog authoring has just been released. The new version contains substantial changes from version 7, which was covered by Mark Pilgrim in a recent XML.com article "The Atom API ". Despite the changes, the API hasn't left its guiding principles behind:

  • Well-defined data model -- with schemas and everything!
  • Doc-literal style web services, not RPC
  • Take full advantage of XML and namespaces
  • Take full advantage of HTTP
  • Secure; thus, no passwords in the clear

While version 8 is longer than version 7 the size of the API has shrunk, with simplification happening on several fronts. Two of the big changes have to do with API discovery. Version 7 contained a file called the Introspection file. The Introspection file listed all of the API endpoints supported by the server and the URIs that handled them. Here is an example of a version 7 Introspection file:

<?xml version="1.0" encoding="utf-8"?>

        <introspection xmlns="http://purl.org/atom/ns#" > 

        <search-entries>

            http://example.com/myblog/atom.cgi/search

        </search-entries>

        <create-entry>

            http://example.com/myblog/atom.cgi/edit

        </create-entry>

        <edit-template>

            http://example.com/atom.cgi/templates

        </edit-template>



        <user-prefs>

            http://example.com/myblog/atom.cgi/prefs

        </user-prefs>

        <categories>

            http://example.com/atom.cgi/categories

        </categories>

        </introspection>

Each of the URIs listed in the Introspection file may have different mime-types and allowed HTTP methods. For example, you can only do a GET on the URI in search-entries, and it returns the results in an XML format specific to search results in the Atom API.

This has changed dramatically in revision 8: there are only 3 types of URIs and they all work with the Atom format, either as a whole or by using an entry fragment of the Atom feed format.

URI Type Specified Methods
PostURI POST
EditURI GET, PUT, DELETE
FeedURI GET
PostURI
Posting an Atom entry element to this URI creates a new entry on the site. This has the same functionality as the create-entry facet in revision 7. The behavior of the PostURI also covers creating comments or trackbacks, which was documented seperately under revision 7.
EditURI
Editing the content of an Entry is unchanged from revision 7 and won't be covered further in this article. See Mark Pilgrim's article for discussion.
FeedURI
Doing a GET on this URI returns information formatted as an Atom Feed. More on this later.

Another aspect of Atom is auto-discovery. One of the first uses of auto-discovery was the HTML link tag to provide a machine readable pointer to a site's feed. The Atom API also uses a link tag to provide a method for automatically discovering the URI of the Introspection file from a web page. Here is an example of such a link tag, which points from a web page to the Introspection file. (Note: this is in the obsolete revision 7 format.)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" 

    lang="en" xml:lang="en">



<head>

<title>My Weblog</title>

<link rel="service.edit" type="application/atom+xml" 

    href="/myblog/atom.cgi/introspection" title="Atom API" />

</head>

<body>

...

</body>

</html>

There are several weaknesses with this approach. The first weakness is that the Introspection file introduces yet another XML format that needs to be parsed. The Atom API uses the Atom format and also introduces different formats for Introspection, Preferences, and Search results. Why are mulitple formats bad? The more formats there are the more difficult it is to implement the API. If the number of formats could be reduced, then the implementation work could be simplified. Fewer file formats means easier implementations.

The second drawback with this approach is that auto-discovery is only available in the HTML file. Its helpful if you are viewing the page in a browser or have the URI of the resource which contains the link. But it leaves aggregators at a distinct disadvantage, making it harder for them to take advantage of the Atom API. That is a bit of a handicap given the number of aggregators that already support weblog editing functions; they would need to retrieve the HTML of the home page to discover the URI of the Introspection file. It would be better if the auto-discovery information could also exist in the Atom feed for a site, a file which an aggregator is already going to have.

Version 8 of the draft RFC solves both of these problems by changing the format of the Introspection file to be an Atom Feed. It also uses a new link element that mirrors the one found in HTML.

Let's review the format of the link tag in Atom 0.2, which revision 7 of the API was based on:

<link>http://www.example.com/</link>

Revision 8 of the API is based on the format in Atom 0.3; thus the linke tag now looks like this:

<link rel="alternate" type="text/html" 

      href="http://www.example.com/"/>

If should look familiar, the style of the HTML link tag has been borrowed for Atom. Note that each link now has a 'rel' attribute which denotes its relation to the containing element. That is, if the 'link' tag is in an entry, then the 'rel' attribute states the relationship between the entry and the thing at the other end of the link URI. Ditto for the feed: the 'rel' states the relationship between the feed and the resource at the given URI. The 'link' element also contains an 'href' attribute which contains the URI, which used to be a value of the element in older specs and not in an attribute. Lastly the 'type' attribute indicates the mime-type of the resource found at the URI given in the 'href' attribute. These three pieces of information combine to uniquely determine a resource. For example, consider a feed element containing a link of the form:

<link rel="service.post"

type="application/atom+xml"

href="http://blog.example.net" />

Now a 'rel' of 'service.post' means that the URI given in the href attribute is a URI of type PostURI. This implies that posting an Atom Entry fragment to the URI http://blog.example.net creates a new entry on the site.

Here's another example. A link tag can appear in one of two places inside an Atom Feed, and it may have different meanings based on which element it is contained in. The two places a link tag can appear are as a child of the feed element or as the child of one of the entry elements. Here is an example Atom feed with two link elements, both with a 'rel' attribute set to 'alternate', that appear in the feed and entry elements.

<?xml version="1.0" encoding="utf-8"?>

<feed version="0.3" xmlns="http://purl.org/atom/ns#">

  <title>BitWorking</title>

  <link 

      rel="alternate" 

      type="text/html" 

      href="http://blog.example.org/"/>

  <modified>2003-12-13T18:30:02Z</modified>

  <author>

    <name>Joe Gregorio</name>

  </author>



  <entry>

    <title>The Big Apple</title>

    <link 

        rel="alternate" 

        type="text/html" 

        href="http://blog.example.org/2003/12/13/apple"/>

    <id>tag:blog.example.org,2003:3.2397</id>

    <issued>2003-12-13T08:29:29-04:00</issued>

    <modified>2003-12-13T18:30:02Z</modified>

  </entry>



</feed>

When found in a Feed, the 'href' attribute points to the home page that this Atom feed represents. When found in an Entry, the 'href' attribute contains the URI of the page that this Entry is about. Note that the meaning has changed based on context. Let's add another set of link tags, this time with a 'rel' attribute with a value of 'comments'.

<?xml version="1.0" encoding="utf-8"?>

<feed version="0.3" xmlns="http://purl.org/atom/ns#">

  <title>BitWorking</title>

  <link 

    rel="alternate" 

    type="text/html" 

    href="http://blog.example.org/"/>

  <link 

    rel="comments" 

    type="text/html" 

    href="http://blog.example.org/comments.atom"/>

  <modified>2003-12-13T18:30:02Z</modified>

  <author>

    <name>Joe Gregorio</name>

  </author>



  <entry>

    <title>The Big Apple</title>

      <link 

       rel="alternate" 

       type="text/html" 

       href="http://blog.example.org/2003/12/13/apple"/>

     <link 

       rel="comments" 

       type="text/html" 

       href="http://blog.example.org/2003/12/13/atom03/comments.atom"/>

    <id>tag:blog.example.org,2003:3.2397</id>

    <issued>2003-12-13T08:29:29-04:00</issued>

    <modified>2003-12-13T18:30:02Z</modified>

  </entry>



</feed>

Note that these examples of using the link tag aren't normative and that they are open to interpretation. The point is to demonstrate some of the power that comes with the new link tag. The semantics of the allowed values for the rel attribute are currently under discussion.

The FeedURI

The new FeedURI takes the place of the search interface and also of the Introspection file. It does that by leveraging the flexibility of the new link tag format. Remember our three URI types? With a link tag that has type="application/atom+xml", the following 'rel' values map to our three types of URIs:

URI Type Rel
PostURI service.post
EditURI service.edit
FeedURI service.feed

Below is an example Atom Feed found by dereferencing a FeedURI. Note that it is a valid Atom Feed in it's own right, but it contains more than the minimum required link tags. Those extra link tags supply the Introspection and search functionality.

<?xml version="1.0" encoding="utf-8"?>

<feed version="0.3" xmlns="http://purl.org/atom/ns#">

 <title>BitWorking</title>

 <link 

    rel="service.post" 

    type="application/atom+xml" 

    href="http://blog.example.org/post.cgi"/>

  <link 

    rel="next" 

    type="application/atom+xml" 

    href="http://blog.example.org/entries/2-20"/>

  <link 

    rel="alternate" 

    type="text/html" 

    href="http://blog.example.org/"/>

  <modified>2003-12-13T18:30:02Z</modified>

  <author>

    <name>Joe Gregorio</name>

  </author>



  <entry>

    <title>The Big Apple</title>

    <link 

        rel="service.edit" 

        type="application/atom+xml" 

        href="http://blog.example.org/edit.cgi/apple"/>

    <link 

        rel="alternate" 

        type="text/html" 

        href="http://blog.example.org/2003/12/13/apple"/>

    <id>tag:blog.example.org,2003:3.2397</id>

    <issued>2003-12-13T08:29:29-04:00</issued>

    <modified>2003-12-13T18:30:02Z</modified>

  </entry>



</feed>

The first link tag in the file has rel="service.post". This means that the URI http://blog.example.org/post.cgi is the PostURI and that POSTing an Atom Entry to that URI will create a new entry on this site.

The second link tag in the file has rel="next". This means that the URI http://blog.example.org/entries/2-20 is another Atom Feed, of type FeedURI, that contains the 'next' set of entries. The word next refers to some ordering of the entries, which will often mean reverse chronological order. This allows the client to walk through all the entries on a site in a structured manner. By having the client follow links with values of either 'prev' or 'next', the client can fetch sets of entries to be presented to the user for editing.

Note that there is no restriction on the number of entries that can be present in an Atom feed; a server may present FeedURIs as entries grouped by day, week, or month.

The first link element in the 'entry' has rel="service.edit", which means that the URI http://blog.example.org/edit.cgi/atom03 is the EditURI to be used to edit that entry.

Typical Editing Scenarios

So how would typical weblog usage scenarios play out?

Create a new Entry

What we need when we talk about publishing a new entry on a site is the PostURI. The PostURI always appears in the FeedURI, so we'll locate that first. Now the FeedURI could be entered directly into the Atom editing client, or the client could support auto-discovery of the FeedURI. Since the FeedURI is also useful for searching for old entries to edit, and may contain other useful link elements, once it is found, it should be persisted so that the client doesn't have to go through the discovery phase every time.

Let's walk through the auto-discovery process to see how it works. Starting from either an Atom feed, or the main web page of a site, we can load either of those representations and look at the appropriate "service.feed" link element which points to the FeedURI. For example,

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">



 <head>

 <title>My Weblog</title>

 <link rel="service.feed" type="application/atom+xml" 

       href="http:/blog.example.org/service.atom" 

       title="Main Blog" />

 </head>

 <body>

 ...

 </body>

 </html>

The FeedURI in this case is http:/blog.example.org/service.atom. We could also have discovered the same FeedURI from the sites main syndication feed. Now the client loads the Atom feed found at the FeedURI and searches it for the PostURI. For example the FeedURI could return:

<?xml version="1.0" encoding="utf-8"?>

<feed version="0.3" xmlns="http://purl.org/atom/ns#">

  <title>BitWorking</title>

  <link 

      rel="alternate" 

      type="text/html" 

      href="http://blog.example.org/"/>

  <link 

      rel="service.post" 

      type="application/atom+xml" 

      href="http://blog.example.org/post.cgi"/>

   <link 

      rel="next" 

      type="application/atom+xml" 

      href="http://blog.example.org/archives/2-20"/>

  <modified>2003-12-13T18:30:02Z</modified>

  <author>

      <name>Joe Gregorio</name>

  </author>



  <entry>

      <title>The Big Apple</title>

      <link 

          rel="alternate" 

          type="text/html" 

          href="http://blog.example.org/2003/12/13/apple"/>

       <link 

          rel="service.edit" 

          type="application/atom+xml" 

          href="http://blog.example.org/apple.atom"/>

      <id>tag:blog.example.org,2003:3.2397</id>

      <issued>2003-12-13T08:29:29-04:00</issued>

      <modified>2003-12-13T18:30:02Z</modified>

  </entry>





</feed>

The link tag with a rel="service.post" and type="application/atom+xml" contains the PostURI. In this example it is http://blog.example.org/post.cgi. Now that we have the PostURI we can create a new Entry on the site by POST'ing an Atom entry to that URI.

POST /post.cgi HTTP/1.1

Host: blog.example.org

Content-Type: application/atom+xml



<?xml version="1.0" encoding="utf-8"?>

<entry xmlns="http://purl.org/atom/ns#">

  <title>My Entry Title</title> 

  <created>2003-11-17T12:29:29Z</created> 

  <content type="application/xhtml+xml" xml:lang="en"> 

    <div xmlns="http://www.w3.org/1999/xhtml">



      <p>Hello, <em>weblog</em> world!</p>

      <p>This is my third post <strong>ever</strong>!</p>



      </div>

  </content>  

</entry>

The server responds with an HTTP status code 201 "Created" and gives the entry's EditURI in the HTTP Location: header.

HTTP/1.1 201 Created

Location: http://blog.example.org/myblog/atom.cgi/edit/3

Editing an old Entry

To edit an existing entry, we need to find its EditURI. Once we have that URI we can perform three operations on an entry. We can do an HTTP GET on it to get the entry, PUT on it to update the entry, and DELETE on it to delete the entry. Note that each and every entry has its own URI.

So how do we find the EditURI? There are several options. Once we created an entry, the server returns the EditURI in the Location: header. The client software could persist that URI once it gets that response.

Another way is to find the EditURI by browsing though the FeedURI. Note in the Atom Feed returned from the FeedURI above there is a link with rel="next" and a type="application/atom+xml". This link points to the "next" Atom Feed of entries on the site. Now I put next in quotes to emphasize that it's a pretty vague concept. The idea of "next" assumes some sort of linearity, some way of putting all the entries in order. In the case of the rel="next", the ordering is defined by the server and can really be anything, though the most likely ordering will be reverse chronological, which is the most common ordering for a weblog. But there is nothing to stop you from ordering your posts in alphabetical order by title, for example..

To find an entry to edit, the client retrieves the Atom feed at the FeedURI and presents the entries it contains to the user, along with a way to navigate to the "next" and "prev" Feeds. In this way the user can navigate through all entries till she finds the one she wants to edit.

Once the user has picked an Entry to edit, the client locates the EditURI in that entry and does a GET on that EditURI. The user can then edit the entry, and when they are done the client PUTs the updated entry back to the same EditURI. In the FeedURI above, the first entry has an EditURI denoted as

<link

   rel="service.edit"

   type="application/atom+xml"

   href="http://blog.example.org/apple.atom"/>

Doing a GET on the URI http://blog.example.org/atom03.atom retrieves the most current representation.

GET /apple.atom HTTP/1.1

Host: blog.example.org

Content-Type: application/atom+xml

The server responds with an HTTP status code 200 "Ok" and the complete entry.

HTTP/1.1 200 Ok 

Content-Type: application/atom+xml



<entry xmlns="http://purl.org/atom/ns#">

    <title>The Big Apple</title>

    <link 

        rel="alternate" 

        type="text/html" 

        href="http://blog.example.org/2003/12/13/apple"/>

    <id>tag:blog.example.org,2003:3.2397</id>

    <issued>2003-12-13T08:29:29-04:00</issued>

    <modified>2003-12-13T18:30:02Z</modified>

    <content mode="escaped" type="text/plain">

       New York, New York what a ...

    </content>

           

</entry>

The user edits the entry, the content for example, then the modified entry is PUT back to the EditURI.

PUT /apple.atom HTTP/1.1

Host: blog.example.org

Content-Type: application/atom+xml



<entry xmlns="http://purl.org/atom/ns#">

    <title>The Big Apple</title>

    <link 

        rel="alternate" 

        type="text/html" 

        href="http://blog.example.org/2003/12/13/apple"/>

    <id>tag:blog.example.org,2003:3.2397</id>

    <issued>2003-12-13T08:29:29-04:00</issued>

    <modified>2003-12-13T18:30:02Z</modified>

    <content mode="escaped" type="text/plain">

       I love New York. 

    </content>

           

</entry>

And the server responds with an HTTP status code of 201 if everything went well.

HTTP/1.1 201 Created

Next time

In this article I showed how the Atom API can be used to edit the content of a typical weblog. Atom isn't necessarily restricted to just weblogs, and in a followup article I will look at other uses of the Atom API.