REST (or RESTful) APIs are a big part of the present day web. A RESTful API uses the somewhat coherent language of HTTP to express interacting with your server through reading, updating, or deleting entities.
Where REST APIs shine
Often, the HTTP verbs
DELETE are enough to describe everything you’d like to tell your server do. For basic data-driven APIs, where you get some data from the server and maybe update some records, these would probably be sufficient.
If your API is just a passive source for querying data, with no business logic, you might only need to work directly with entities. There are plenty of openly accessible APIs that offer that; they don’t know what kind of business logic their clients implement and they don’t care about it.
For example, a mobile app that gives the user “around you right now” information might make several REST calls to different servers. It could grab the current weather from one server by providing the current location, the list of news items from another server, and nearby restaurants from yet another server. Finally, it would compile these together to present to the user.
In the above example, the app takes up the role of the brain, and the APIs are only data providers. But if a mobile app relies on the server to implement business logic independently from the app, speaking between the two in terms of CRUD might quickly become insufficient.
Let’s have a look at few of the pain points that come up while talking to your server through a RESTful API.
Just some of the issues with RESTful APIs…
REST has been widely adopted because of its simplicity, its flexibility, and the fact that it makes use of the popular HTTP protocol as its base language. Unfortunately, some of these are also the reasons that RESTful APIs are often difficult to design, implement, and use.
Get more development news like this
No schema or type validation
Since each RESTful API gets designed according to the entities it works with, there’s no commonly accepted way to provide a validation schema, expected data types, or any other way to create a contract between the two communicating parties.
For example, you could ask the server to give you all available accounts (
GET /accounts), but there’s no guarantee this endpoint exists at all. Or it might have existed in a previous version of the API, but not in the current one.
If you do get all accounts from the server, now it’s the time to read all the details about a particular one:
GET /accounts/145. Does this entry exist? Yes, calling
GET /accounts just gave you back the id
145. But does the entity still exist? It depends… Another client could have modified it or deleted it from the server since you asked for the account list.
But let’s look at another issue caused by the lack of schema - data is not typed. Individual endpoints might return the same data due to relationships in the data model, but depending on who implemented that endpoint, small differences might appear in the responses:
The difference between the two responses above is that
amount is a string in one response and a number in the other, which can easily cause your mobile app to crash.
An ongoing data transformation
Handling data types is another source of annoyance and bugs in complex mobile applications. Just to talk to a RESTful API, you need a setup something like this:
The data being sent over the network goes over multiple transformations:
- Define native data models
- Define your data models in native code.
- Serialize data into JSON
- Convert those native objects into a JSON-compatible representation, using only strings, numbers, lists, and dictionaries.
- Massage your native data entities into a structure that your REST endpoint expects.
- Transport via HTTP
- Encode the data as a JSON string, and then encode that JSON string as multipart HTTP form data to send over a
- Encode the data as a JSON string, and then encode that JSON string as multipart HTTP form data to send over a
- Decode data on server
POSTdata, then decode the JSON string.
- Store data in an SQL database
- Cherry-pick request items, and create objects that map to your server storage.
- Convert, yet again, the objects into data that you’re able to store in a SQL database.
The data you need to transport over the network ends up being continuously transformed because the format and available data types change multiple times along the way. And when this constant type transformation pairs up with the lack of schema or contract, the request or response might at any point contain data the other party does not expect, validate, or understand.
Wrapping up your business logic in terms of transport means
Finally, not only you need to transform and transcode your data repeatedly along the way, but you also need to change the language to describe what you actually want to achieve.
In your app’s code you will use the language of the business domain of your app; an expressive set of APIs you designed to fit the product’s goals and to specifically address the business logic you are implementing. You would design your app’s API to use the latest and greatest features of the programming language in use, and you would design your data entities to best describe your model.
Once you have this beautifully designed system, you need to re-purpose all of that to be able to be expressed as a set of CRUD operations. You would take the data entities you designed to best describe your needs and break them down to a JSON structure.
You lose the expressiveness of your API and the richness of your model, just to wrap your business logic around the simplicity and flexibility of a RESTful API.
Network requests can always fail
Networking requests can fail for a number of reasons out of your control - a momentary loss of connectivity, an issue on the web server, or a glitch in the server application.
Having to implement the complete communication logic in your code (both on the mobile app and the server) means you need to take care of all the error handling yourself. Sending and receiving data back is just the tip of the iceberg — what makes for a solid API is much more.
At first glance, the flowchart above doesn’t seem too complex — there seem to be a finite number of fail points. But with RESTful APIs, many times you need to make several dependent requests one after the other. The first request creates an entity, which you get back as a response, then the second request uses the data from the first one, and so forth.
Now add up the complexity of what’s under the tip of the iceberg together for all those dependent requests and consider a situation in which the last request in that chain fails.
What is your recovery strategy? Do you:
- Keep trying the last request until it succeeds?
- Ignore the failure?
- Somehow roll back all the previous, successful requests?
Unfortunately there is no single error strategy with RESTful APIs. That’s why prototyping mobile apps using RESTful API is straightforward, but building a release-quality product gets tricky.
Is everything lost with RESTful APIs?
Since REST is just the means of the conversation between your mobile app and your server, there are also many solutions to the problems above implemented for both the server and the mobile app.
Micro-services or data-driven APIs achieve a good degree of automation with REST, which removes the burden of implementing repeated tasks like data transcoding, discoverability, and others.
Creatively designed endpoints to wrap transactions can help solve issues like having to make a large number of requests to achieve a single atomic action. And speaking of creative work, people have been adding
GET parameters, custom
POST JSON payloads, and more to their REST requests to make them smarter.
Ultimately, many of the issues remain and are considered “occupational hazards”:
- You do need to validate the structure and type of the response.
- You do need to transform multiple times your data during its trip to the server.
- You do need to express your business logic in terms of CRUD operations.
- Finally, your networking does need to be asynchronous, and therefore error-prone.
And in a perfect world? 👼
Let’s step back for a moment, and imagine a world where anything is possible. What would a connected app look like?
It would probably share the same data entities across devices, with the same structure and the same means to access and modify data. They would never be outdated and would represent a single source of truth across all connected devices.
Of course, you could always just use your language of choice, be it Swift, Java, or any other language. You would implement all of your business logic in that language, design your APIs to use that language’s features, and focus on making your users happy instead of burning cycles on transforming data back and forth.
Last but not least, your network layer and your model would never error out. No more “can’t save data” or “different type expected”. You would just focus on implementing logic, improving performance, and the overall product stability.
The Realm Platform
The Realm Platform tries to address most (if not all) of the pain points that current API networking brings to the table.
For starters, the Realm Platform completely solves the transport issues - it seamlessly pairs a local database on the user’s device with one on the server. It automatically synchronizes all changes between the two locations, which means that you, as the developer, simply work with your local database as you always have.
- Your app is automatically offline-first — you just work with the local database, and it gets automatically synced with your server whenever there is network connectivity.
- Since you’re not restricted by the transport layer, you get to use your native programming language to define your data entities and your business logic! You never have to limit your business domain to what your networking layer can do.
- The Realm Platform takes care to implement industry-standard encryption on your data, whether it’s on the mobile device, on your server, or in transit. On top of that, it provides user authentication on the server, out of the box.
- Your app does not need a caching strategy, because the Realm Platform updates your local data as soon as there are updates on the server.
- Last but not least - you get to create stunning reactive apps, allowing people to collaborate across platforms. Whenever a user deletes an inventory item from their Android phone, you will receive a fine-grained notification in your iOS app, so you can remove that precise table row off the screen.
In the end, you are working with your local Realm database as you always did and letting the Realm Platform handle networking automatically. If that sounds interesting, you can use the Developer Edition of the Realm Platform for free:
What is the best way?
Networking and caching are hard. (Also naming, but we’re not solving that. Yet.) You have to change the language you use from your business domain to what your transport protocol allows you to do.
A successful app networking layer would balance a number of tasks like connectivity, data transformation, request and response validation, talking to your data model, and more. Overlooking any one of these quickly leads to seemingly random crashes in production. And sometimes a change made by the server team in an apparently unrelated area of the server application could modify the response you get and start crashing the mobile app.
There are a lot of good libraries out there trying to automate and demystify networking and data synchronization. They solve some of the pain points for some apps and help decrease complexity for others.
The Realm Platform offers a new way to tackle these issues: Remove the REST API altogether. It removes the need to implement a custom networking layer in mobile apps, thus solving all the pain points of talking to a RESTful or another server API.
Try the Realm Platform now
We’re excited to see what you can build now that you don’t have to worry about networking headaches. What apps will simple data sync enable?
About the content
This content has been published here with the express permission of the author.