In 2007, Steve Jobs introduced the iPhone, which revolutionized the high-tech industry and changed our approach to work and business. Now 2012 and more and more sites offer native iOS and Android clients for their services. Meanwhile, not all startups have the finances to develop applications in addition to the main product. To increase the popularity of their product, these companies offer open APIs that can be used by third-party developers. Perhaps Twitter was the first in this area and now the number of companies following this strategy is growing rapidly. This is a really great way to create an attractive ecosystem around your product.
The life of a startup is full of changes, turning points in which the fate of the project depends on the decisions made. If your codebase is not able to provide the embodiment of a variety of your solutions - you have lost. The server code, which is flexible enough to adjust to the needs of the business in a short time, decides whether to be a project or not. Successful startups are not the ones that just offered a great idea, but those that were able to put it into practice. The success of a startup depends on the success of its product, be it an iOS application, service, or API. For the past three years, I have been working on different iOS applications (mainly for startups) using web services, and in this blog I tried to gather my accumulated knowledge and show you the best techniques you need to follow when developing the RESTful API. A good RESTful API is one that can be changed easily and simply.
The target audience
This post is intended for those who have knowledge in the development of a RESTful API level from intermediate to advanced. As well as some basic knowledge of object-oriented (or functional) programming in server languages ​​such as Java / Ruby / Scala. (I intentionally ignored PHP or Programmable Hyperlinked Pasta).
Note Per. Here the author provided a link to a humorous article about the history of programming languages ​​where PHP was decoded as Programmable Hyperlinked Pasta (Programmable Hyperlinked Noodles). What characterizes the attitude of the author to PHP.
The structure and organization of the article
The article is quite detailed and consists of two parts. The first describes the basics of REST, while the second describes the documentation and support for different versions of your API. The first part for beginners, the second for the pros. I have no doubt that you are a pro, and therefore here is a
link for you to jump directly to the chapter “API Documentation”. Perhaps you should start from there, if it seems to you that this post is from the category “Many bukaf, niasilil ...”.
')
RESTful principles
A server can be considered RESTful if it complies with
REST principles. When you develop an API that will be primarily used by mobile devices, understanding and following the three most important principles can be very useful. And not only in the development of the API, but also with its support and development in the future. So let's get started.
Statelessness
The first principle is independence from state. Simply put, the RESTful server should not monitor, store, or even use the current contextual information about the client. On the other hand, the client must take on this task. In other words, do not make the server remember the state of the mobile device using the API.
Let's imagine that you have a startup called “New Facebook”. A good example where a developer could make a mistake is providing an API call that allows a mobile device to set the last read item in a stream (let's call it the Facebook feed). An API call, usually returning a feed (let's call it / feed), will now return items that are newer than the one set. Sounds smart, doesn't it? Have you “optimized” the data exchange between the client and the server? And no.
What can go wrong in the above case is that if your user uses the service from two or three devices, then in the case when one of them installs the last element read, the rest will not be able to load the tape elements read on other devices. earlier.
State independence means that the data returned by a particular API call should not be affected by calls made earlier.The correct way to optimize this call is to pass the creation time of the last read tape entry as an API call parameter returning the feed (/ feed? LastFeed = 20120228). There is another, more “correct" method - the use of the HTTP header If-Modified-Since. But we will not go into this direction for now. We will discuss this in the second part.
The client, on the other hand, may (must) remember the parameters generated on the server when accessing it and use them for subsequent API calls, if required.
Cached and layered architecture
The second principle is to provide the client with information that the server's response can be cached for a certain period of time and reused without new requests to the server. This client can be either a mobile device itself or an intermediate proxy server. I will tell you more about caching in the second part.
Client - server split and single interface
A RESTful server must hide as many details of its implementation as possible from the client. The client should not be aware of which DBMS is being used on the server or how many servers are currently processing requests and other such things. Organizing the proper separation of functions is important for scaling if your project starts to gain popularity quickly.
These are probably the three most important principles to be followed during the development of a RESTful server. Next will be described three less important principles, but they all have a direct bearing on what we are talking about here.
REST requests and four HTTP methods
Get
POST
PUT
DELETE
The principle of “cacheability” and GET requests
The main thing to remember is that a call made via GET should not change the state of the server. This in turn means that your requests can be cached by any intermediate proxy (load reduction). Thus, as a server developer, you should not publish GET methods that change data in your database. This violates the RESTful philosophy, especially the second paragraph described above. Your GET calls should not even leave entries in access.log or update “Last logged in” data. If you are changing data in the database, these must be POST / PUT methods.
That discussion of POST vs PUT
The HTTP 1.1 specification states that PUT is
idempotent . This means that a client can perform multiple PUT requests on a single URI and this will not create duplicate entries. Assignment operations are a good example of an idempotent operation.
String userId = this.request["USER_ID"];
Even if this operation is performed twice or three times, there will be no harm (except for extra processor cycles). POST, on the other hand, is not idempotent. This is something like an increment. You should use POST or PUT, taking into account whether the action being taken is idempotent or not. In the language of programmers, if the client knows the URL of the object to be created, use PUT. If the client knows the URL of the method / class that creates the desired object, use POST.
PUT www.example.com/post/1234
Use PUT if the client knows the URI, which itself could be the result of the request. Even if the client calls this PUT method many times, no harm or duplicate entries will be created.
POST www.example.com/createpost
Use POST if the server itself creates a unique identifier and returns it to the client. Duplicate records will be created if this request is repeated later with the same parameters.
More information in
this discussion .
DELETE method
DELETE is absolutely unique. It is idempotent as PUT, and should be used to delete a record if it exists.
REST answers
Answers from your RESTful server can be used as XML or JSON. Personally, I prefer JSON, because it is more concise and a smaller amount of data is transmitted over the network than when transmitting the same response in XML format. The difference may be of the order of several hundred kilobytes, but, taking into account the speeds of 3G and the instability of the exchange with mobile devices, these several hundred kilobytes can be important.
Authentication
Authentication must be done via https and the client must send the password in encrypted form. The process of getting sha1 hash NSString in Objective-C is quite clear and simple, and the code below clearly shows this.
- (NSString *) sha1 { const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding]; NSData *data = [NSData dataWithBytes:cstr length:self.length]; uint8_t digest[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(data.bytes, data.length, digest); NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; for(int i = 0; i <; CC_SHA1_DIGEST_LENGTH; i++) [output appendFormat:@"%02x", digest[i]]; return output; }
The server should compare the received password hash with the hash stored in its database. In any case, under no circumstances should passwords be transmitted from the client to the server in open form. There are no exceptions to this rule! The day when your users find out that you store their passwords in clear text may be the last day of your startup. Trust, once lost, can not be returned.
RFC 2617 describes two methods of authentication on an HTTP server. The first is Basic Access, the second is Digest. For mobile clients, either of these two methods is suitable and most server (and client too) languages ​​have built-in mechanisms for implementing such authentication schemes.
If you plan to make your API public, you should also look towards oAuth or better oAuth 2.0. oAuth will allow your users to publish content created in your application on other resources without exchanging keys (logins / passwords). oAuth also allows users to control what exactly is in access and what permissions are given to third-party resources.
The Facebook Graph API is the most advanced and common implementation of oAuth at the moment. Using oAuth, Facebook users can give access to their photos to third-party applications without publishing other private and identification information (login / password). The user can also restrict access to unwanted applications without having to change their password.
Until now, I have been talking about the basics of REST. Now we come to the essence of the article. In subsequent chapters, I will talk about practical techniques that should be used when documenting, creating new ones and completing support for old versions of my API ...
API Documentation
The worst documentation a server developer can write is a long, consistent list of API calls describing parameters and return data. The main problem with this approach is that making changes to the server and the format of the returned data as the project develops becomes a nightmare. I will make some suggestions on this subject so that the client software developer understands you better. Over time, this will also help you develop as a developer of server software.
Documentation
The first step I would recommend is to think about the basic, high-level data structures (models) that your application operates on. Then think about the actions that can be performed on these components. The foursquare API documentation is a good example to study before you start writing your own. They have a set of high-level objects, such as places, users, and the like. They also have a set of actions that can be performed on these objects. Since you know the high-level objects and actions on them in your product, creating an API call structure becomes easier and more understandable. For example, to add a new place, it would be logical to call a method like / venues / add
Document all high-level objects. Then document the requests and responses to them using these high-level objects instead of simple data types. Instead of writing “This call returns three string fields, the first contains id, the second name, and the third description” write “This call returns a structure (model) describing the place”.
Documenting request parameters
Let's imagine that you have an API that allows the user to log in using the Facebok token. Call this method as / login.
Request /login Headers Authorization: Token XXXXX User-Agent: MyGreatApp/1.0 Accept: application/json Accept-Encoding: compress, gzip Parameters Encoding type – application/x-www-form-urlencoded token – “Facebook Auth Token” (mandatory) profileInfo = “json string containing public profile information from Facebook” (optional)
Where profileinfo is a high-level object. Since you have already documented the internal structure of this object, such a simple mention is enough. If your server uses the same Accept, Accept-Encoding and the Encoding type parameter, you can always document them separately, instead of repeating them in all sections.
Documenting Response Parameters
Responses to API calls should also be documented based on a high-level object model. Quoting the same example of foursquare, calling the method / venue / # venueid # returns a data structure (model) describing the location of the event.
Sharing ideas, documenting or informing other developers that you will return in response to a request will become easier if you document your API using the structure of objects (models). The most important result of this chapter is the need to perceive documentation as a contract that you conclude, as a server-side developer and client application developers (iOS / Android / Windows Phone / to be completely).
Reasons for creating new and stopping support for old versions of your API
Before the advent of mobile applications, in the era of Web 2.0, creating different versions of the API was not a problem. Both the client (JavaScript / AJAX front-end) and the server were deployed simultaneously. Consumers (your customers) have always used the latest version of client software to access the system. Since you are the only company developing both the client and server parts, you fully control how your API is used and changes in it are always immediately applied to the client side. Unfortunately, this is not possible with client applications written for different platforms. You can deploy API version 2, assuming that everything will be fine, but this will lead to inoperability of iOS applications using the old version. Because there may still be users using such applications despite the fact that you posted an updated version in the App Store. Some companies have resorted to using push notifications to remind you to update. The only thing that will lead to this is the loss of such a client. I saw a lot of iPhones, which had more than 100 applications awaiting updates. The chances that your will become one of them are very high. You should always be ready to separate your API into versions and to stop supporting some of them as soon as it is required. However, maintain each version of your API for at least three months.
Split into versions
Deploying your server code to different folders and using different URLs for calls does not mean that you have successfully divided your API into versions.
So
example.com/api/v1 will be used by version 1.0 of the application, and your freshest and coolest version 2.0 will use
example.com/api/v2When you make updates, you almost always make changes to the internal data structures and models. This includes changes to the database (adding or deleting columns). For better understanding, let's imagine that your “new Facebook” has an API call called / feed which returns the Ribbon object. Today, in version 1, your Ribbon object includes the user's avatar URL (avatarURL), user name (personName), post text (feedEntryText) and creation time (timeStamp) of the post. Later, in version 2, you introduce a new feature that allows advertisers to place descriptions of their products in the feed. Now the “Ribbon” object contains, let's say, the new “sourceName” field, which overlaps the user's name when the ribbon is displayed. Thus, the application should display “sourceName” instead of “personName”. Since the application no longer needs to display “personName” if “sourceName” is set, you decide not to send “personName” if there is a “sourceName”. It all looks good until the old version of your application, version 1, contacts the updated server. It will display your advertisements from the tape without a title because there is no “personName”. The “literate” way to solve this problem is to send both “personName” and “sourceName”. But friends, life is not always that simple. As a developer, you will not be able to track all single changes that have ever been made with each data model in your object. This is not a very effective way to make changes since in half a year you will almost forget why and how something was added to your code.
Returning to web 2.0, this was not a problem at all. The JavaScript client was immediately modified to support API changes. However, the installed iOS applications are no longer dependent on you. Now their update is the user's prerogative.
I have an elegant solution for tricky situations of this kind.
Paradigm versioning via URL
The first solution is sharing using a URL.
api.example.com/v1/feeds will be used by version 1 of the iOS application whereas
api.example.com/v2/feeds will be used by version 2.
Despite the fact that it sounds all good, you cannot continue to create copies of your server code for each change in the format of the returned data. I recommend using this approach only in case of global changes in the API.
Paradigm of versioning through model
Above, I showed how to document your data structures (models). Treat this documentation as a contract between the developers of the server and client parts. You should not make changes to the model without changing the version. This means that in the previous case there should be two models, Feed1 and Feed2.
Feed2 has a sourceName field and it returns sourceName instead of personName if sourceName is set. The behavior of Feed1 remains the same as it was specified in the documentation. The algorithm of the controller will be something like this:

You should move the class creation logic to a separate class according to the Factory method pattern. The corresponding controller code should look something like this:
Feed myFeedObject = Feed.createFeedObject("1.0"); myFeedObject.populateWithDBObject(FeedDao* feedDaoObject);
Where the decision on the version of the API used will be made by the controller in accordance with the UserAgent field of the request text.
Addition:Instead of using the version number from the UserAgent string, it would be better to use the version number in the Accept header. Thus, instead of sending
Accept: application/json
should be sent
Accept: application/myservice.1.0+json
Thus, you have the opportunity to specify the API version for each request to the REST server. Thanks to the hacker news readers for this tip.
The controller uses the Feed factory method to create a valid feed object (tape) based on information from a client request (all requests include a UserAgent field that looks like AppName / 1.0) for the version concerned. When you develop a server in this way, any change will be easy. Changes will not violate existing agreements. Just create new data structures (models), make changes to the factory method to create an instance of a new model for the new version and that's it!
With this approach, your version 1 and 2 applications can continue to work with a single server. Your controller can create version 1 objects for old client applications and version 2 objects for new ones.
Termination of support
With the paradigm of separating an API into versions proposed through the model, stopping support for your API becomes much easier. This is very important in the latter stages when you publish your API. When you do a global API update, audit all of the factory methods in your models to reflect the changes in your business logic.
If, during the release of version 3 of your API, you decide to stop supporting version 1, then all you have to do is to remove the corresponding models and delete the lines that create their instances in your factory method. Creation of new versions and the termination of support for old will necessarily accompany your project, showing how flexible it is to support key decisions dictated by the business. A business incapable of sudden changes and turns is doomed. Typically, the inability to make key changes is due to technical imperfections of the project. This technique can solve this problem.
Caching
Another important thing about performance that needs attention is caching. If you think that this is a client application task, think carefully. In part 2 of this article, I will explain how to organize caching using http 1.1 tools.
Error handling and internationalizing your API
Bringing to the client the reason for the error in case of its occurrence is no less important than sending the correct data. I will talk about error handling and internationalization in part 3 of this article. I will not promise anything, in any case it will take time to write.
From the translator:I myself am not a developer for iOS and I did not develop web services; my level in this area can be described as “I am going to be a beginner”. But the topic is interesting to me and I liked the article, so much so that I decided to translate.
The second part of