Consider the frontend makes an API call to a backend service.
client.get('/users');
and the response we are expecting is of the form:
{
data: [
{
id: 1,
name: 'John'
},
...,
{
id: 10,
name: 'Mary'
},
]
}
if I were to use Typescript I would do something like:
type User = {
id: number;
name: string;
}
type UsersResp = {
data: User[]
}
const users = (await client.get<UsersResp>('/users')).data;
Now, lets assume the backend makes a change and instead of sending back the aforementioned response, it just sends:
[
{
id: 1,
name: 'John'
},
...,
{
id: 10,
name: 'Mary'
},
]
Now the frontend will crush since there is no .data
property.
My question is if there is any point at validating the response of the server in order to take some action in case the validation fails, ex. display a nice message to the user that something has gone terribly wrong.
Is there any point on doing that or it is just a boilerplate code that achieves nothing ?
CodePudding user response:
The need for validation depends on the situation. There is no simple yes/no answer and it depends on whether a breaking change is likely or not. When in doubt, I would always implement validation mechanisms because without error handling there could be unexpected behavior.
To answer your specific question:
lets assume the backend makes a change and instead of sending back the aforementioned response
From my understanding of your problem, you cannot completely rule out a malformed response. Therefore, the validation is not unnecessary boilerplate code. Without a validation, your client might run into an undefined state, resulting in the UI not working as expected and bad user experience.
One way of validating the shape of the response would be by using user-defined type guards.
CodePudding user response:
The simple answer is yes, you should always validate the data coming in from the backend.
The most common approach to doing this is by means of creating a default data structure that represents how the data will be organized.
Given your example, i would do something like the following:
const defaultUser = {
id: 0,
name: ''
};
Once I get the data, I just extend the defaultUser
for each entry or as I'm using/storing it somewhere else:
const user = { ...defaultUser, ...actualUser };
or in the case of what you're providing:
const users = userData.map(user => ({ ...defaultUser, ...user});
This will typically ensure you're getting exactly what you expect at the very least, while allowing the backend data structure to change without affecting your frontend implimentation.
Of course the best thing to also do is keep in close contact with your backend team and know about any model changes as they are being planned.