We build apps of all shapes and sizes here at Voila Hub, but a common element is that they communicate with servers. Very few apps today operate without some sort of Internet connectivity, meaning that they interact with a backend, web services, or APIs. These APIs could be provided by Google, Amazon, Facebook, or comparable third-parties. They also could be APIs that are developed internally.
The problem for these internal or in-house APIs are twofold. Many don't take the time to plan out a good API. Additionally, even with the abundance of apps, not everyone has built web services or APIs for apps specifically. In our experience, we've found that establishing guidelines on how to build better APIs for mobile apps saves time and effort during development and reduces headache later on in the process.
We wrote this guide to outline the best practices for building out APIs, web services, and databases for mobile apps and mobile clients. In this post, we go in-depth on creating a RESTful API specifically for mobile apps. This information is not only for us at Voila Hub. It's helpful for our customers and any other web or backend development teams who want to properly build and maintain their own app-focused backends.
REST is by far the most commonly-used style for designing APIs, especially in the mobile world. There are also particular subsets of REST, like OData, that further define how data should be transmitted between your apps and the server. While those subsets may be best for your particular needs, we're going to keep the conversation broad enough to cover all REST styles. Adhering to a popular, generic, RESTful architecture style will ensure that new developers tasked with maintaining your server code in the future will be familiar with how it works and, more importantly, how new services should be built onto it.
In this guide, we'll also be discussing RESTful APIs through the lens of mobile app systems. These rules, however, will certainly help with supporting web apps and other systems with your API too. In most cases, the mobile app client asking for resources and the backend server handing out those resources are going to be written in different programming languages and often by different development teams. Adhering to the REST principles laid out below will ensure that both teams are setting and fulfilling the proper expectations when the time comes for the two platforms to communicate with each other. Nothing is worse than thinking that you've completed a new endpoint before realizing it's not in a format the mobile client can use efficiently. In projects where there are multiple dev team members, it is critical for everyone to stay in constant communication regarding these shared blueprints to avoid unexpected miscommunications, delays, and wasted time. Adhering to a commonly-agreed set of standards and expectations will allow teams to iterate faster and more efficiently, which makes development and maintenance less expensive in the long run.
Before we dive into the hosting, security, architecture, and other considerations for creating your RESTful API, let's examine what makes building an API for mobile apps different from other systems. These mobile-specific concerns are essential to making sure your RESTful API is prepared to work efficiently with a mobile app and the expectations of its users.
The internet was built on HTTP, but mobile platforms enforce HTTPS requirements with modern encryption and trusted signed certificates. A mobile backend needs to use HTTPS for every endpoint. Your development, staging, and production environment servers should all be using the same type of signed certificates. This will save you headaches later when migrating/testing features on each environment, allowing you catch security issues upstream before they become a problem on the live server and start affecting real users.
To save on network data costs and battery life for users, you typically want mobile clients doing as little work as possible. It's quite rare to see a mobile app that couldn't benefit from querying and storing data remotely or offloading burdensome and sensitive tasks to more powerful, remote machines. Plugging into a remote API keeps your stored data secure and allows your app to continue running smoothly for the user, staying focused on presenting your data quickly instead of calculating. Mobile app users expect their data to be synced across all their devices, which is also solved by moving the data off the device with an API.
Letting the server do the heavy lifting also saves time for developers working on multiple platforms by moving and consolidating code to your server and presenting the results to both your iOS and Android apps. The server is going to have access to much better hardware than the mobile clients for number crunching, so it is wise to leverage it.
You can also expect any errors a mobile user experiences to be broadcast with a megaphone. Tech users these days have little patience when something doesn't work as expected, especially mobile app users. There's no bigger stage for issues to be shared than in App Store and Google Play app reviews. If something goes wrong, the server needs to respond with user-friendly error messages or error codes the client can use to assuage the user and, hopefully, help fix the issue. Even a single error can cause a 1-star review and positive reviews are critically linked to the success of an app. Too many negative reviews caused by server issues will stop new downloads for your app.
With mobile app users updating their apps (or not) at different frequencies, versioning your API becomes more important than other, more controlled environments. With several different versions of the app running in the wild, the server needs to consolidate and handle the various requests coming in from new and legacy users alike. We'll dig into effective strategies on how to handle this later.
A useful communication avenue unique to mobile is the push notification. There are third party tools that specialize in push notification, but sometimes you need to manage the process yourself. Your server may be responsible for tracking device tokens that maps devices to users for sending push notifications. Using a service like Firebase, however, to manage device tokens and send push notifications to iOS and Android users is typically more cost effective than building your own.
Many mobile users will expect the app to have some limited functionality even while offline. Once reconnected to the server, reconciling the offline activity with the rest of the database needs to be considered. This is especially important for apps that users can access on multiple devices, such as their phone and tablet. Coordinating API calls with timestamps and order of operations is something that needs to be discussed by the frontend and backend developers.
Now that we've looked at what sets mobile apart from other systems, we can dive deeper into planning your RESTful API. These tips address common concerns for hosting the server, dealing with security, creating the backend architecture, choosing database and storage options, using the right tools, and supporting multiple platforms.
Choosing the location to host your server is a big decision. If you don't have the desire or capacity to host your own bare-metal server, there are plenty of cloud-hosted solutions available these days. Every project is different, each with specific needs for performance, scalability, and administrative features. Some core factors to pay attention to while evaluating services for where to host your server include:
Depending on your needs, you have a wide array of authentication mechanisms to leverage. Any hosted service you choose should already include easy integration of HTTPS and trusted CA certificates. HTTP Basic Authentication is the easiest to implement, but it's also the least secure. OAuth2 is widely accepted as a secure, standard way of performing authentication and is highly recommended. There are plenty of libraries for social logins or phone number authentication you could use as well. Do not try to write your own authentication! There is no need to reinvent the wheel here when you can rely on existing protocols and libraries that have already been vetted by many others on both client and server side. Protecting each API endpoint behind authentication requirements should be the norm. Don't allow free passes on a resource unless necessary for functionality.
Sensitive data should be protected. This is a given, but security is a spectrum, not an absolute. Encrypt your user's sensitive data. Encryption may not be necessary for every project, but it should always be considered. Don't store your passwords in plain text. Please. Not only should you hash passwords, but using random salts for each password will significantly improve security.
As we already discussed, you're hopefully planning on building not one, but three discrete backend environments: development, staging, and production. The development environment is where frequent development changes are rolled out as they're completed by developers. Data here can be generated by developers; this can be achieved through automated scripts to populate a database with a healthy amount of test data. As code passes through all its tests in continuous integration (hopefully you're testing your server's logic and API endpoints) and gets the OK from QA, it graduates into the staging environment.
The staging environment is going to try to resemble production as much as possible. Ideally, data here is an import of transformed real/live sampled data stripped of personal information. The more realistic data used here, the better confidence you'll have of how your system will perform in production. Porting data may not be feasible for your project, but having some sort of quasi-representative data in these first environments is critical to reducing risk and discovering bugs in the logic before becoming a real issue. If the content cannot be reproduced, having at least roughly the same quantity of data you're expecting in production in these environments again will pinpoint bottlenecks in the system early. Otherwise, irreproducible slow downs, hang ups, and bugs may occur in production that can be a nightmare to track down in development.
No matter what type of database you use, it's worth noting that entity IDs should be randomly generated UUIDs, not sequential. This helps secure resources by making IDs much harder to guess. When it comes to storing your data, you might be considering a traditional relational database like MySQL or MariaDB. Or maybe you prefer the scalability of a noSQL document database like MongoDB. Or perhaps you prefer the flexibility of a hybrid approach that something like PostgreSQL can offer with both relational or document storage support. Which database your project should use is really going to depend on your data. Here are some notes to cover the basics:
You're going to need the right tools to get the job done. Whether it's communication between project teams, current team members, or future team members going through the onboarding process for your project, communication is key to success on any software project. During development, make sure you're using a clear dev-tracking tool and that all teammates have access. We're fans of Pivotal Tracker and Trello, but any similar tool will work.
The point is to keep progress out in the open and maintain a historical record. This is especially important with multiple developers on a project. The developer responsible for writing the code to log in a user in an iOS app will really want to know when the server-side authentication API is ready to consume. They may even have a discussion around shifting priorities of the server developers to complete that task sooner rather than later if it's blocking the iOS app from progressing further. Letting the whole team have knowledge of not only what's currently being worked on but also what's up next will allow a more fluid workflow of coordinating work schedules to complete tasks in the most efficient way possible. This will also help circumvent issues where the API may have changed prematurely, causing a delay or otherwise negatively impacting development in the mobile app. It's also worth noting that just like any software iteration, release notes are invaluable when a new API is deployed, even during development.
When done right, RESTful API endpoints should be easy to test and should have tests covering both obvious use cases as well as expected edge cases for each endpoint. One of the core principles of REST is stateless, which makes our API endpoints small, modular black boxes, ripe for testing. New data comes in, successful message comes out, and the newly persisted data changes are easily verifiable in the database. Request for data goes in, expected data response comes out. The testing framework to use will of course depend on the language you're writing the backend server logic in. No matter how you write your tests, they should be well-maintained and run with 100% passing rate before every deploy.
During development, your testing suite may prove sufficient documentation for other developers to review how the system works. Eventually you'll need to write down, in plain english, how the system you built actually works. I know this is the least favorite part for most of us, but it is critical for the success of a project in the long term. Your documentation is only as good as the effort you put into it. Fortunately, these days there are documentation tools out there that can do a lot of the work for you. One of our favorite API documentation tools is Postman. Not only is it useful for exploring or testing an API, but it can help generate all the requests, responses, and handled error codes and store them for you to reference later. You can literally build your documentation as you build an integration test suite for your new API.
No matter how you document your API, try to include successful response codes, sample requests, and examples of both successful and failed responses, with expected error codes and messages. You also need to make sure this documentation is accessible to everyone on the team. It does no good if no one else can read it! Included below are some other documentation tools you may want to look at:
If you're building an API for a mobile app to consume, chances are good that you'll need to support other platforms in the future. The general rule of thumb for building APIs for mobile is to make the client as dumb and thin as possible, while keeping all the heavy sorting, filtering, number crunching, data aggregating, and consolidation on the server. This leverages the more powerful hardware of the server and tries to keep the client logic simple while fetching and showing the data to the user as quickly as possible.
This is important because when you're building your app on iOS, Android, and web, you don't want to rewrite complicated filtering and parsing logic three times if you can avoid it. For this reason, you'll want to allow robust sorting and filtering options, letting the client fetch only what it needs. For example, don't return everything in a collection and force the client app to sort through the data to find what it was looking for. Use pagination of long lists of data to avoid overwhelming both the client and the user and allow the client to determine how many results it should get back. While supporting multiple platforms, it may be beneficial to capture device names, OS versions, and types in request headers since those can be useful while reading logs and debugging in the future.
Now that we've discussed how and where you should set up your server, let's dig into how to actually design your API in a RESTful manner. This section runs through how to use proper url paths, requests and responses, and other guidelines for implementing your RESTful API.
For the rest of this guide, we're going to assume we're building an API for a library. For example, some of your resources might be a book, a book club, an author, a user, or a library location. The client would want access to all of those resources via the API so you could construct some endpoints mapping to:
GET /books
POST /books
GET /books/{bookId}
PUT /books/{bookId}
PATCH /books/{bookId}
DELETE /books/{bookId}
GET /books/{bookId}/authors
GET /bookClubs/book
GET /users/{userId}
GET /users/{userId}/favoriteBooks
Notice the pattern emerging. We hope to construct our API URL endpoints in a way that makes it very clear what that resource contains. Now let's talk about the two useful RESTful design tools: nouns and verbs. The actions GET, POST, PUT, PATCH, DELETE are the verbs, meaning they are the HTTP methods telling the server what action to perform. The URL path itself tells you the nouns, which are the resources that will be acted on. It's important to make this distinction here, because otherwise you could end up with a messy API that's difficult to follow.
Now take a look at these (bad) endpoints:
/getBooks
/createNewBook
/checkOutBook/{bookId}
/returnBook/{bookId}
/addBookToFavorites
/addNewMemberToBookClub
/changeBookClubMeetingTime
/changeBookClubMeetingLocation
/removeBookClubMember
You may glean more about what those API endpoints might be trying to do at a glance, but now you're going to have to add a new API endpoint for every possible action on that resource. Doing so will lead to frustration for the clients trying to consume your API. You want to keep your API concise and modular. You're not only creating an API for your apps right now, but you also need to think about how additional features or data types will fit into the API.
To improve your API's robustness, just let the requests drive the server's response. Now this may seem like a silly statement, because of course a client will make a request and the server's job is to send a response. If the server is dictating what, how, and where a client can fetch or change data, then that means that every time they want to perform a new action or feature, the client developers are going to have to wait for changes to be made to the server. The server is allowed to refuse requests. That's what error messages are for. But by building your endpoints with nouns and reacting to the data in the request, you'll provide a stable API that will require less maintenance over time as you allow clients to do more with less.
Now, just because we're using nouns to drive the URLs, doesn't mean our resources need to be the same as our data model objects. The URL should include model objects where appropriate, but we want to make these paths easy to read and intuitive. For example, the resource /favoriteBooks
probably will just return the same objects as /books
, but we provide the clarification of what type of books we're going to get back. This separation also allows you move the logic of finding, filtering, and calculating favoriteBooks
to the server, again allowing the mobile client to remain thin and focused on pulling the needed data and presenting it to the user.
This leads directly into how to handle filtering, sorting, pagination, and searching. These four sub-actions are all related as they let the client declare what resources it's looking to retrieve from the server. Again, we want to construct our APIs to give the clients as much freedom as possible here, without having to exert too much extra effort on our side. We handle filtering, sorting, pagination, and searching with URL parameters. This allows us to keep our API endpoint quantity low and manageable while giving the clients the tools they need to perform more complicated actions. Once we add the ability to handle these types of query parameters, we open up the door for the client to make changes to better serve up content to the users as they see fit. Today, maybe they want to show all libraries in alphabetical order, but tomorrow they realize that sorting them by geographical location is more useful to end users. Or maybe they want to let end users choose how they want to see libraries sorted as a configurable setting. The client can do all of that without any extra work on the server side, which is what we want.
Here are some examples of handling these URL parameters for our library API:
GET /libraries?sortBy=name&isCurrentlyOpen=true&pageCount=10
GET /books?queryTitle=Sherlock+Holmes&queryAuthor=Arthur+Conan+Doyle
GET /bookClubs?genre=mystery
API versioning is another feature we should implement to achieve the robustness that is especially important for mobile apps. Mobile developers don't always have the luxury of forcing software updates for all end users, so our API is going to have to be able to handle both old and new requests. We'll do this by routing requests with a version number. Some will argue that this version number should go in the URL path, and some will argue it should be placed in the request header. We prefer it to be in the URL path for easier discovery, but that's up to you and your team to discuss. We treat versioned resources as different resources, and that's why we think they deserve a unique path rather than a request header value. Either way, the benefit of versioning API is allowing newer features to be requested while not interfering with legacy requests.
GET /v1/books
GET /v2/books
Another important guideline to call out with all of our URL examples above is that they read from left to right, from broad to specific. You're letting the clients traverse a tree of API endpoints, starting with the root single point of entry, where they can request anything, and letting related resources cascade from there. Again, the aim is to make these APIs easily explorable and intuitive for the clients to navigate. Determining what resources to actually expose in your API is going to be up to you and your dev team to decide. If you have any UI or UX designs for how the client apps are going to work, it's important for the developers working on the backend to be aware of what data they clients will need and when. Ask yourself questions like:
So we've laid out how we'll construct our API endpoints, now let's talk about how to communicate effectively within each of those endpoints. Let's start with the request. When handling a request, don't force the client to only send one or two fields. Allow them to send full objects if they wish, while the server just uses the fields it needs. If a request does come in with only a few fields, don't assume the missing fields are null
. Fields that are null
should be stated explicitly in both requests and responses. This prevents the other end f