Lesson 69 of 60 – Typing API Responses
100%

Typing API Responses

React Native applications often communicate with APIs to load user data, products, students, posts, and other information. With TypeScript, we can define the structure of API responses and use that data more safely.

Note: TypeScript does not automatically know the structure of data returned by an API. We define the expected response structure using types or interfaces and then use those types in our application code.

1. What is an API Response?

An API response is the data returned by a server after an application sends a request.

For example, an API might return:

{
    "id": 101,
    "name": "Rahul",
    "course": "React Native"
}

2. Why Type API Responses?

API data is often used throughout an application. If the expected structure is not clearly defined, incorrect property names or types can cause problems.

TypeScript allows us to describe the expected structure of the response.

3. Creating an API Interface

An interface can describe a single student returned by an API.

interface Student {
    id: number;
    name: string;
    course: string;
}

4. Example API Response

{
    "id": 101,
    "name": "Rahul",
    "course": "React Native"
}

According to our interface, id should be a number, while name and course should be strings.

5. Using fetch()

The JavaScript fetch() function can be used to request data from an API.

const response =
    await fetch("https://example.com/api/students");

The response can then be converted into JSON.

6. Converting Response to JSON

const response =
    await fetch("https://example.com/api/students");

const data =
    await response.json();

By default, TypeScript does not automatically know the exact structure of the JSON returned by the server.

7. Understanding unknown API Data

Data received from an external API should be treated carefully. The server response may not always match the structure we expect.

A type or interface describes what our application expects, but it does not automatically validate the server response at runtime.

8. Creating a Typed API Function

const getStudent = async (): Promise<Student> => {

    const response =
        await fetch("https://example.com/api/student");

    const data =
        await response.json();

    return data;
};

The return type says that the function is expected to return a Student.

9. Promise with API Data

API functions are usually asynchronous, so their return type is commonly written using Promise.

async function getStudent():
    Promise<Student> {

    // API request

}

This means the function eventually resolves to a Student value.

10. Typing an Array Response

APIs often return multiple records.

interface Student {
    id: number;
    name: string;
}

const getStudents =
    async (): Promise<Student[]> => {

    // API request

};

Student[] means an array of Student objects.

11. Example Array Response

[
    {
        "id": 101,
        "name": "Rahul"
    },
    {
        "id": 102,
        "name": "Priya"
    }
]

This response can be represented as Student[].

12. Typing API State with useState

const [students, setStudents] =
    useState<Student[]>([]);

TypeScript now knows that students contains an array of Student objects.

13. Nullable API State

Sometimes API data is not available when the component first renders.

const [student, setStudent] =
    useState<Student | null>(null);

The state can contain either a Student or null.

14. Loading State

const [loading, setLoading] =
    useState<boolean>(false);

A boolean loading state can be used to display a loading indicator while API data is being requested.

15. Error State

const [error, setError] =
    useState<string | null>(null);

The error state can contain an error message or null when there is no error.

16. Complete API State Structure

const [students, setStudents] =
    useState<Student[]>([]);

const [loading, setLoading] =
    useState<boolean>(false);

const [error, setError] =
    useState<string | null>(null);

These three states are commonly used when loading API data.

17. Fetching Typed Data

const loadStudents = async (): Promise<void> => {

    try {

        setLoading(true);
        setError(null);

        const response =
            await fetch(
                "https://example.com/api/students"
            );

        const data: Student[] =
            await response.json();

        setStudents(data);

    } catch (error) {

        setError("Unable to load students");

    } finally {

        setLoading(false);

    }
};

18. Checking the HTTP Response

It is useful to check whether the HTTP request was successful before processing the response.

const response =
    await fetch(url);

if (!response.ok) {
    throw new Error("Request failed");
}

This allows failed HTTP requests to be handled by the error logic.

19. API Response with Nested Data

Some APIs return an object containing an array of records.

interface Student {
    id: number;
    name: string;
}

interface StudentResponse {
    success: boolean;
    students: Student[];
}

20. Using Nested API Response

const data: StudentResponse =
    await response.json();

if (data.success) {

    setStudents(data.students);

}

The interface describes both the response status and the student list.

21. API Data with Optional Properties

Some API properties may not always be present.

interface User {
    id: number;
    name: string;
    phone?: string;
}

The phone property is optional.

22. Displaying Optional API Data

<Text>
    Phone: {user.phone ?? "Not available"}
</Text>

The nullish coalescing operator provides a fallback when the value is null or undefined.

23. Displaying Typed API Data

{students.map((student: Student) => (

    <View key={student.id}>

        <Text>
            {student.name}
        </Text>

        <Text>
            {student.course}
        </Text>

    </View>

))}

TypeScript knows the properties available on each Student object.

24. API Response Validation

TypeScript types are compile-time descriptions. They do not automatically validate unknown JSON received from a server at runtime.

For applications that require runtime validation, a validation library or custom validation logic can be used.

25. Common TypeScript API Mistakes

  • Assuming API data always has the expected structure.
  • Using any for all API responses.
  • Forgetting to type API state.
  • Not handling loading states.
  • Not handling errors.
  • Accessing optional properties without checking them.
  • Forgetting that JSON data is not automatically runtime-validated.

26. Best Practices for Typed API Responses

  • Create interfaces for important API response structures.
  • Use Promise<Type> for typed async functions.
  • Type API arrays with Type[].
  • Use nullable state when data is not available initially.
  • Keep loading and error states separate.
  • Check the HTTP response before processing data.
  • Use runtime validation when the application requires it.

27. Typed API Hook Pattern

API logic can be placed inside a reusable function or custom hook.

const useStudents = () => {

    const [students, setStudents] =
        useState<Student[]>([]);

    const [loading, setLoading] =
        useState<boolean>(false);

    const [error, setError] =
        useState<string | null>(null);

    return {
        students,
        loading,
        error
    };
};

28. Complete Typed API Example

import React, {
    useEffect,
    useState
} from "react";

import {
    View,
    Text,
    ActivityIndicator
} from "react-native";

interface Student {
    id: number;
    name: string;
    course: string;
}

interface StudentResponse {
    success: boolean;
    students: Student[];
}

export default function App(): React.JSX.Element {

    const [students, setStudents] =
        useState<Student[]>([]);

    const [loading, setLoading] =
        useState<boolean>(true);

    const [error, setError] =
        useState<string | null>(null);

    const loadStudents =
        async (): Promise<void> => {

        try {

            setLoading(true);
            setError(null);

            const response =
                await fetch(
                    "https://example.com/api/students"
                );

            if (!response.ok) {
                throw new Error("Request failed");
            }

            const data: StudentResponse =
                await response.json();

            setStudents(data.students);

        } catch (error) {

            setError(
                "Unable to load student data"
            );

        } finally {

            setLoading(false);

        }
    };

    useEffect(() => {

        loadStudents();

    }, []);

    if (loading) {

        return (
            <View>
                <ActivityIndicator />
            </View>
        );

    }

    if (error) {

        return (
            <View>
                <Text>{error}</Text>
            </View>
        );

    }

    return (
        <View>

            <Text>
                Student List
            </Text>

            {students.map(
                (student: Student) => (

                <View key={student.id}>

                    <Text>
                        {student.name}
                    </Text>

                    <Text>
                        {student.course}
                    </Text>

                </View>

            ))}

        </View>
    );
}

29. Why TypeScript is Useful with API Data

Typed API responses make it easier to understand and safely use data throughout a React Native application.

  • Clear data structures
  • Better editor suggestions
  • Safer state management
  • Better API function definitions
  • Easier refactoring
  • Fewer type-related mistakes

30. Summary

API responses are an important part of React Native applications. TypeScript allows us to describe API data using interfaces and types, then use those types with fetch functions, state, and UI components.

You should now understand typed API objects, arrays, nested responses, loading and error states, optional properties, Promise return types, and displaying API data safely in React Native.

📌 Key Points

  • Create interfaces for important API response structures.
  • Use Promise<Type> for typed asynchronous API functions.
  • Use Type[] for arrays of API objects.
  • Use nullable state when API data is initially unavailable.
  • Keep loading and error states properly typed.
  • Check the HTTP response before processing API data.
  • Optional API properties should be handled safely.
  • TypeScript types do not automatically validate unknown JSON at runtime.

🧠 Quick Quiz

Question: Which TypeScript type represents an array of Student objects?