Lesson 125 of 158 – Generic API Response
79%

Generic API Response

REST APIs often return responses with a common structure. For example, a student API, user API, product API, and login API may all return success, message, and data. TypeScript generics allow us to create one reusable response interface for different types of API data.

Note: In this lesson, we will learn how to create a reusable ApiResponse<T> interface and use it with PHP REST APIs, Axios, React Native, authentication, students, lists, errors, and pagination.

1. What is an API Response?

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

{
    "success": true,
    "message": "Data loaded",
    "data": {
        "id": 1,
        "name": "Rahul"
    }
}

2. Common API Response Structure

A common API response can contain:

  • success
  • message
  • data
  • errors
  • meta

Keeping a consistent structure makes API integration easier.

3. Basic Response Interface

interface ApiResponse {
    success: boolean;
    message: string;
    data: any;
}

The problem with any is that TypeScript loses information about the actual data structure.

4. Problem with any

interface ApiResponse {
    success: boolean;
    message: string;
    data: any;
}

With any, TypeScript cannot properly check what properties exist inside data.

Generics provide a better solution.

5. What is a Generic?

A generic allows a type to be supplied when the interface or function is used.

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
}

Here, T represents the actual data type.

6. Generic ApiResponse with User

interface User {
    id: number;
    name: string;
    email: string;
}

type UserResponse =
    ApiResponse<User>;

Now data is automatically treated as a User.

7. Generic ApiResponse with Student

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

type StudentResponse =
    ApiResponse<Student>;

8. Generic Response with an Array

A generic response can also contain an array.

type StudentListResponse =
    ApiResponse<Student[]>;

In this case, data is an array of students.

9. Generic Response with Axios

const response =
    await axios.get<ApiResponse<Student[]>>(
        "/students"
    );

console.log(response.data.data);

Axios now knows that the response contains an array of students.

10. Understanding Nested Generics

ApiResponse<Student[]>

The outer type is ApiResponse. The type supplied to T is Student[].

data: Student[]

11. Generic Login Response

interface LoginData {
    token: string;
    user: User;
}

type LoginResponse =
    ApiResponse<LoginData>;

This keeps the common response structure reusable.

12. Generic Profile Response

type ProfileResponse =
    ApiResponse<User>;

The same generic interface can be used for the user profile API.

13. Generic Product Response

interface Product {
    id: number;
    name: string;
    price: number;
}

type ProductResponse =
    ApiResponse<Product>;

14. Generic API Function

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

    const response =
        await axios.get<
            ApiResponse<Student[]>
        >("/students");

    return response.data;
};

15. Generic API Response with Error Information

A standard response can also include validation or error information.

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data?: T;
    errors?: string[];
}

The optional properties allow different responses to contain different information.

16. Successful Response

{
    "success": true,
    "message": "Student loaded",
    "data": {
        "id": 10,
        "name": "Amit",
        "course": "React Native"
    }
}

17. Error Response

{
    "success": false,
    "message": "Validation failed",
    "errors": [
        "Email is required",
        "Password is required"
    ]
}

The same response interface can represent this structure when data is optional.

18. Generic Response with Pagination

interface PaginationMeta {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
}

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
    meta?: PaginationMeta;
}

19. Student Pagination Response

type StudentListResponse =
    ApiResponse<Student[]>;

The response can contain both student data and pagination metadata.

response.data.data
response.data.meta

20. Using Generic Response with FlatList

const response =
    await api.get<
        ApiResponse<Student[]>
    >("/students");

setStudents(response.data.data);

The returned data can be directly assigned to a typed student array.

21. Generic Response with POST

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

const response =
    await api.post<
        ApiResponse<Student>
    >(
        "/students",
        {
            name: "Ravi",
            course: "React Native"
        }
    );

console.log(response.data.data);

22. Generic Response with PUT

const response =
    await api.put<
        ApiResponse<Student>
    >(
        "/students/10",
        {
            name: "Ravi Kumar",
            course: "TypeScript"
        }
    );

console.log(response.data.data);

23. Generic Response with DELETE

A DELETE endpoint may return a response without a large data object.

interface DeleteData {
    id: number;
}

type DeleteResponse =
    ApiResponse<DeleteData>;
const response =
    await api.delete<DeleteResponse>(
        "/students/10"
    );

24. Generic Response and Authentication

interface LoginData {
    token: string;
    user: User;
}

type LoginResponse =
    ApiResponse<LoginData>;

const response =
    await api.post<LoginResponse>(
        "/login",
        loginData
    );

const token =
    response.data.data.token;

25. Generic Response Service Function

const getUsers =
    async (): Promise<
        ApiResponse<User[]>
    > => {

    const response =
        await api.get<
            ApiResponse<User[]>
        >("/users");

    return response.data;
};

26. Reusable Generic API Function

Generics can also be used directly in reusable functions.

async function getData<T>(
    url: string
): Promise<ApiResponse<T>> {

    const response =
        await api.get<ApiResponse<T>>(
            url
        );

    return response.data;
}

The same function can request users, students, products, or other data.

27. Using the Reusable Function

const users =
    await getData<User[]>(
        "/users"
    );

const students =
    await getData<Student[]>(
        "/students"
    );

console.log(users.data);
console.log(students.data);

The same function can work with different response types.

28. Complete Generic API Types

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data?: T;
    errors?: string[];
    meta?: PaginationMeta;
}

interface PaginationMeta {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
}

interface User {
    id: number;
    name: string;
    email: string;
}

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

interface LoginData {
    token: string;
    user: User;
}

29. Complete Axios Generic API Example

import axios from "axios";

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
}

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

const api = axios.create({
    baseURL: "https://example.com/api"
});

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

    const response =
        await api.get<
            ApiResponse<Student[]>
        >("/students");

    return response.data;
};

The response is strongly typed from the API layer to the React Native component.

30. Complete Generic API Flow

PHP REST API
      ↓
JSON Response
      ↓
Axios
      ↓
ApiResponse<T>
      ↓
TypeScript
      ↓
User / Student / Product / Login Data
      ↓
React Native State
      ↓
UI Component

Generic API responses allow one common response structure to be reused for many different API resources. This reduces duplicate TypeScript code while maintaining strong type checking.

📌 Key Points

  • Generic API responses provide reusable TypeScript structures.
  • ApiResponse<T> allows different data types to be supplied.
  • Using any can reduce TypeScript type safety.
  • Generics can represent users, students, products, and login data.
  • ApiResponse<Student[]> represents a response containing student arrays.
  • Axios generics can strongly type API responses.
  • Generic responses can contain errors and pagination metadata.
  • The same API response interface can be used with GET, POST, PUT, and DELETE.
  • Generic functions can request different API data types.
  • Consistent API response structures make React Native applications easier to maintain.

🧠 Quick Quiz

Question: What does T represent in ApiResponse<T>?