Whenever we have a new feature we want to add to an existing app, as a web developer we are used to updating both, the frontend and the backend simultaneously. And then we deploy the changes to the production environment and call it a day.

However, in the case of mobile apps, the same concept is not always applicable. Because not everybody will auto-update the app. Hence the new changes to the backend code might not work as expected in the older versions of the mobile app. Which is the majority of the users after a new release.

 
Let’s look at an example.

Let’s say we have an app that lets users keep track of their monthly expenses. We have a React native app to get the inputs from the users and a backend that stores the expenses in a database. And following is the JSON format we use to communicate with the backend:

1{
2    data: [
3        {
4            "id": 1,
5            "amount": 100,
6            "date": "2021-01-01",
7            "category": "Food",
8            "subcategory": "Groceries",
9            "description": "Bought groceries for the month",
10        },
11        {
12            "id": 2,
13            "amount": 200,
14            "date": "2021-01-02",
15            "category": "Transportation",
16            "subcategory": "Gas",
17            "description": "Filled up the gas tank",
18        }
19    ]
20}

Assuming we have a React Native app that is consuming the API we are showing the expenses in a list. Like this:

1<View>
2    {result?.data?.map((expense) => (
3        <View>
4            <Text>{expense.amount}</Text>
5            <Text>{expense.date}</Text>
6            <Text>{expense.category}</Text>
7            <Text>{expense.subcategory}</Text>
8            <Text>{expense.description}</Text>
9        </View>
10    ))}
11</View>

But what if we now want to add a feature that lets the users also store their incomes? To achieve that we need to update the backend code to also store the incomes in the database. And while retrieving the data we need to send the data back in the following JSON format:

1{
2    data: {
3        expenses: [
4            {
5                "id": 1,
6                "amount": 100,
7                "date": "2021-01-01",
8                "category": "Food",
9                "subcategory": "Groceries",
10                "description": "Bought groceries for the month",
11            },
12            {
13                "id": 2,
14                "amount": 200,
15                "date": "2021-01-02",
16                "category": "Transportation",
17                "subcategory": "Gas",
18                "description": "Filled up the gas tank",
19            }
20        ],
21        incomes: [
22            {
23                "id": 1,
24                "amount": 10000,
25                "date": "2021-01-01",
26                "category": "Salary",
27                "subcategory": "Full-time",
28                "description": "Received salary for the month",
29            },
30            {
31                "id": 2,
32                "amount": 2050,
33                "date": "2021-01-02",
34                "category": "Freelance",
35                "subcategory": "Writing",
36                "description": "Received freelance payment for the month",
37            }
38        ]
39    }
40}

Hence the mobile app will have to update the code to handle the new JSON format.

1<View>
2-    {result?.data?.map((expense) => (
3+    {result?.data?.expenses?.map((expense) => (
4        <View>
5            <Text>{expense.amount}</Text>
6            <Text>{expense.date}</Text>
7            <Text>{expense.category}</Text>
8            <Text>{expense.subcategory}</Text>
9            <Text>{expense.description}</Text>
10        </View>
11    ))}
12</View>

But in the older versions of the mobile app, the code will still be using the old JSON format. So the app will crash. And the same is applicable while saving the data to the backend. The older versions of the mobile app will not be able to save the data in the new JSON format in case we have a validator that checks the JSON format before it is saved to the database. But here, we will only talk about the response format. We’re sure you can handle the “conditionally saving to the database” part on your own once you know the trick.

 
Now, how do we solve this?

To solve this issue we can conditionally return the old JSON format for the older versions of the mobile app. And return the new JSON format for the newer versions of the mobile app. But how would the backend server know which version of the mobile app the user is using? That’s where the library called `react-native-device-info` comes in. Using this library we can get the version of the mobile app and send it to the backend server using a custom header `MobileAppVersion`. In the backend, we can check if the header `MobileAppVersion` is present and is greater than or equal to the version of the mobile app that we want to support. If it is then we can return the new JSON format, otherwise we can return the old one.

If you are using apisauce (which is recommended) you can set the headers like this:

1import DeviceInfo from "react-native-device-info"
2
3...
4setAllHeaders(value?: string, appVersion?: string) {
5    api.apisauce.setHeader("Authorization", `Bearer ${value}`)
6    api.apisauce.setHeader("MobileAppVersion", appVersion || "")
7}
8...
9
10setAllHeaders(token, DeviceInfo.getVersion())

or if you are not using apisauce you can set the headers manually like this:

1async function getData(url: string, appVersion: string) {
2    const response = await fetch(url, {
3        headers: {
4            'Authorization': 'Bearer Token',
5            'MobileAppVersion': appVersion || ""
6        }
7    })
8    const data = await response.json()
9    return data
10}
11
12...
13// get the data from the api (e.g. inside useEffect, or a button press)
14const result = await getData("https://api.example.com", DeviceInfo.getVersion())
15...

Then in the backend code, you can conditionally return the response based on the app version.

1const express = require('express')
2const app = express()
3
4app.get('/', (req, res) => {
5    const appVersion = req.headers['MobileAppVersion']
6    if (appVersion && appVersion >= '2.0.0') { // if the app version is 2.0.0 or higher
7        res.json({ data: { expenses: [...], incomes: [...]} })
8    }
9    else {
10        // if the app version is lower than 2.0.0, older response format
11        res.json({ data: [] })
12    }
13})

or if you are using Python with Django you can do something like this:

1from django.http import JsonResponse
2
3class get_data(request):
4    def get(self, request):
5        app_version = request.headers.get('MobileAppVersion', '1.0.0')
6        if app_version and app_version >= '2.0.0':
7            # if the app version is 2.0.0 or higher
8            return JsonResponse({'data': {expenses: [...], incomes: [...]}})
9        else:
10            # if the app version is lower than 2.0.0, older response format
11            return JsonResponse({'data': [...]})