# React API Integration – Fetching & Managing Data

A frontend application becomes much more powerful when it can communicate with a backend.

Until now, most of our React applications have worked with data that already exists inside the component:

```jsx
const users = [
  { id: 1, name: "Saurabh" },
  { id: 2, name: "Rahul" }
];
```

But real applications don't usually work this way.

Instead:

```text
React Frontend
      ↓
    HTTP Request
      ↓
Backend / API
      ↓
Database
      ↓
Backend Response
      ↓
React Frontend
      ↓
UI
```

Today we're learning how to build this connection.

* * *

# 🔹 What Is API Integration?

API integration means allowing one application to communicate with another system through an API.

For a React application, this commonly means communicating with:

```text
Frontend
   ↓
REST API
   ↓
Backend
   ↓
Database
```

For example, imagine a MERN application:

```text
React
  ↓
Express.js
  ↓
Node.js
  ↓
MongoDB
```

React doesn't directly communicate with MongoDB.

Instead, React communicates with your backend API.

```text
React → Express/Node → MongoDB
React ← Express/Node ← MongoDB
```

This architecture is extremely important for the MERN stack.

* * *

# 🔹 What Is an HTTP Request?

When React wants information from a server, it sends an HTTP request.

Common HTTP methods include:

| Method | Purpose |
| --- | --- |
| GET | Retrieve data |
| POST | Create data |
| PUT | Replace/update data |
| PATCH | Partially update data |
| DELETE | Delete data |

For example:

```text
GET /api/users
```

might return:

```json
[
  {
    "id": 1,
    "name": "Saurabh"
  },
  {
    "id": 2,
    "name": "Rahul"
  }
]
```

* * *

# 🔹 Using `fetch()`

JavaScript provides the `fetch()` API for making HTTP requests.

Basic example:

```jsx
fetch("https://example.com/api/users");
```

But `fetch()` returns a Promise.

Therefore we can use:

```jsx
fetch("https://example.com/api/users")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });
```

Or, preferably, with `async/await`:

```jsx
async function getUsers() {
  const response = await fetch(
    "https://example.com/api/users"
  );

  const data = await response.json();

  console.log(data);
}
```

* * *

# 🔹 Why Use `useEffect`?

Fetching data is a side effect.

That's why it commonly happens inside `useEffect`.

```jsx
useEffect(() => {
  async function fetchUsers() {
    const response = await fetch(
      "https://example.com/api/users"
    );

    const data = await response.json();

    setUsers(data);
  }

  fetchUsers();
}, []);
```

The empty dependency array means the effect runs when the component mounts.

* * *

# 🔹 The Basic React Data-Fetching Pattern

A typical component needs three pieces of state:

```jsx
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
```

Why three?

Because the request can be in different states:

```text
Loading
   ↓
Success

OR

Loading
   ↓
Error
```

This is one of the most important patterns to understand.

* * *

# 🔹 Building Our First API Component

Let's create a simple user component.

```jsx
import { useEffect, useState } from "react";

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchUsers() {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users"
        );

        if (!response.ok) {
          throw new Error("Failed to fetch users");
        }

        const data = await response.json();

        setUsers(data);
      } catch (error) {
        setError(error.message);
      } finally {
        setLoading(false);
      }
    }

    fetchUsers();
  }, []);

  if (loading) {
    return <h2>Loading...</h2>;
  }

  if (error) {
    return <h2>Error: {error}</h2>;
  }

  return (
    <div>
      <h1>Users</h1>

      {users.map(user => (
        <div key={user.id}>
          <h2>{user.name}</h2>
          <p>{user.email}</p>
        </div>
      ))}
    </div>
  );
}

export default Users;
```

Let's break down what is happening.

* * *

# 🔹 Step 1 — Initial State

```jsx
const [users, setUsers] = useState([]);
```

Initially:

```text
users = []
```

Then:

```jsx
const [loading, setLoading] = useState(true);
```

means:

```text
loading = true
```

And:

```jsx
const [error, setError] = useState(null);
```

means:

```text
error = null
```

* * *

# 🔹 Step 2 — Component Renders

Initially:

```text
loading = true
```

Therefore:

```jsx
if (loading) {
  return <h2>Loading...</h2>;
}
```

The user sees:

```text
Loading...
```

* * *

# 🔹 Step 3 — API Request

After rendering, `useEffect` executes.

```jsx
fetch("https://jsonplaceholder.typicode.com/users")
```

The browser sends the request.

* * *

# 🔹 Step 4 — Server Responds

The server returns data.

For example:

```json
[
  {
    "id": 1,
    "name": "Leanne Graham",
    "email": "leanne@example.com"
  }
]
```

We convert the response into JavaScript:

```jsx
const data = await response.json();
```

* * *

# 🔹 Step 5 — Update State

```jsx
setUsers(data);
```

Now React receives the data.

State changes:

```text
[] 
 ↓
[users]
```

The component re-renders.

* * *

# 🔹 Step 6 — Loading Ends

```jsx
setLoading(false);
```

Now:

```text
loading = false
```

So the loading UI disappears.

* * *

# 🔹 Step 7 — Render Data

React now executes:

```jsx
users.map(user => (
  <div key={user.id}>
    <h2>{user.name}</h2>
    <p>{user.email}</p>
  </div>
))
```

And the users appear.

* * *

# 🔹 The Complete Flow

This is worth memorizing conceptually:

```text
Component renders
       ↓
loading = true
       ↓
"Loading..."
       ↓
useEffect executes
       ↓
API request
       ↓
Server responds
       ↓
setUsers(data)
       ↓
setLoading(false)
       ↓
Re-render
       ↓
Display users
```

This is the foundation of data-driven React applications.

* * *

# 🔹 Why `response.ok` Matters

A common beginner mistake is:

```jsx
const response = await fetch(url);
const data = await response.json();
```

without checking the response.

Remember:

> `fetch()` doesn't automatically reject the Promise for HTTP errors such as 404 or 500.

Therefore:

```jsx
if (!response.ok) {
  throw new Error("Something went wrong");
}
```

is important.

* * *

# 🔹 Handling Errors

Let's make our error UI better.

```jsx
if (error) {
  return (
    <div>
      <h2>Something went wrong.</h2>
      <p>{error}</p>
    </div>
  );
}
```

Now our application has:

```text
Loading State
Success State
Error State
```

* * *

# 🔹 Empty State

What if the API successfully returns an empty array?

```json
[]
```

That's not an error.

It's a successful request with no data.

We can handle it separately:

```jsx
if (users.length === 0) {
  return <p>No users found.</p>;
}
```

Now our UI has four meaningful states:

```text
Loading
   ↓
Success → Data
   ↓
Empty → No data

OR

Error → Request failed
```

This is much closer to production-quality UI.

* * *

# 🔹 Fetching Based on an ID

Now let's make the request dynamic.

Suppose we have:

```jsx
function User({ userId }) {
```

We want to fetch a specific user.

```jsx
useEffect(() => {
  async function fetchUser() {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/users/${userId}`
    );

    const data = await response.json();

    setUser(data);
  }

  fetchUser();
}, [userId]);
```

Notice:

```jsx
[userId]
```

The effect depends on `userId`.

If:

```text
userId = 1
```

we fetch:

```text
/users/1
```

If it changes:

```text
userId = 2
```

React runs the effect again:

```text
/users/2
```

* * *

# 🔹 Why Dependencies Matter

Consider:

```jsx
useEffect(() => {
  fetchUser(userId);
}, [userId]);
```

The dependency array tells React:

> "Whenever `userId` changes, synchronize the component with the corresponding user."

This is exactly what we learned yesterday about effects.

Today we're applying it to API calls.

* * *

# 🔹 POST Request

GET retrieves data.

But what about creating data?

We use POST.

```jsx
async function createUser() {
  const response = await fetch(
    "https://example.com/api/users",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        name: "Saurabh",
        email: "saurabh@example.com"
      })
    }
  );

  const data = await response.json();

  console.log(data);
}
```

The important pieces are:

```jsx
method: "POST"
```

and:

```jsx
headers: {
  "Content-Type": "application/json"
}
```

and:

```jsx
body: JSON.stringify(data)
```

* * *

# 🔹 Why `JSON.stringify()`?

JavaScript object:

```jsx
const user = {
  name: "Saurabh",
  age: 23
};
```

HTTP request bodies commonly send JSON text.

Therefore:

```jsx
JSON.stringify(user)
```

produces:

```json
{"name":"Saurabh","age":23}
```

On the backend, this JSON can be parsed back into an object.

* * *

# 🔹 React Form + API

Now let's connect concepts from previous days.

```jsx
function Register() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  async function handleSubmit(event) {
    event.preventDefault();

    const response = await fetch(
      "https://example.com/api/users",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          name,
          email
        })
      }
    );

    const data = await response.json();

    console.log(data);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={event => setName(event.target.value)}
        placeholder="Name"
      />

      <input
        value={email}
        onChange={event => setEmail(event.target.value)}
        placeholder="Email"
      />

      <button type="submit">
        Register
      </button>
    </form>
  );
}
```

Look at how many React concepts we've combined:

```text
Controlled Forms
      +
State
      +
Events
      +
API
      +
POST
```

This is exactly how your earlier React knowledge starts becoming practical.

* * *

# 🔹 PATCH Request

Suppose we want to update only part of a resource.

```jsx
await fetch(`/api/users/${userId}`, {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "Updated Name"
  })
});
```

PATCH is commonly used for partial updates.

* * *

# 🔹 DELETE Request

To delete something:

```jsx
await fetch(`/api/users/${userId}`, {
  method: "DELETE"
});
```

Then we can update our local state.

For example:

```jsx
setUsers(prevUsers =>
  prevUsers.filter(user => user.id !== userId)
);
```

Notice that we're not necessarily making another GET request.

We can update the UI using our existing state.

* * *

# 🔹 API Integration With Your MERN Stack

This is where today's topic becomes especially important.

Imagine you eventually build:

```text
React Frontend
       ↓
Axios / Fetch
       ↓
Express API
       ↓
Controller
       ↓
MongoDB
```

For example:

```text
POST /api/auth/register
```

React sends:

```json
{
  "name": "Saurabh",
  "email": "saurabh@example.com",
  "password": "password"
}
```

Express receives it.

Then:

```text
Express
   ↓
Validate
   ↓
Controller
   ↓
MongoDB
   ↓
Response
```

React receives:

```json
{
  "message": "User registered successfully"
}
```

This is the bridge between your **React learning** and your eventual **MERN projects**.

* * *

# 🔹 Fetch vs Axios

You can make API requests using:

```text
fetch
```

or libraries such as:

```text
Axios
```

For now, understanding `fetch()` is extremely valuable because it is built into modern browsers.

Once the fundamentals are clear, learning Axios becomes much easier.

* * *

# 🔹 Avoid Putting API Logic Everywhere

Instead of doing this repeatedly:

```jsx
fetch("/api/users")
```

inside many components, larger applications often separate API logic.

For example:

```text
src/
├── components/
├── pages/
├── hooks/
├── services/
└── api/
```

You might create:

```jsx
// api/userApi.js

export async function getUsers() {
  const response = await fetch("/api/users");

  if (!response.ok) {
    throw new Error("Failed to fetch users");
  }

  return response.json();
}
```

Then your component can use:

```jsx
useEffect(() => {
  async function loadUsers() {
    const data = await getUsers();
    setUsers(data);
  }

  loadUsers();
}, []);
```

This separation makes applications easier to maintain.

* * *

# 🔹 Environment Variables

You shouldn't hard-code every backend URL throughout your application.

For example:

```jsx
fetch("http://localhost:5000/api/users");
```

During deployment, your backend may have a different URL.

Instead, frontend projects can use environment variables.

With Vite, client-exposed environment variables use the `VITE_` prefix.

For example:

```text
VITE_API_URL=http://localhost:5000
```

Then:

```jsx
const API_URL = import.meta.env.VITE_API_URL;

fetch(`${API_URL}/api/users`);
```

This becomes useful when moving from:

```text
Local Development
      ↓
Production
```

* * *

# 🔹 CORS

While working with your MERN stack, you will eventually encounter:

> **CORS**

Suppose:

```text
React
localhost:5173
```

communicates with:

```text
Express
localhost:5000
```

These are different origins.

The browser may block the request unless the backend allows the frontend origin.

In Express, this is commonly handled with the `cors` package.

For example:

```js
import cors from "cors";

app.use(cors());
```

CORS is a browser security mechanism, not a React feature.

Understanding this now will save you a lot of confusion when connecting your React frontend to your Express backend.

* * *

# 🔹 Common API Mistakes

### ❌ Forgetting `await`

```jsx
const data = response.json();
```

Instead:

```jsx
const data = await response.json();
```

* * *

### ❌ Forgetting `response.ok`

```jsx
const response = await fetch(url);
```

Always consider:

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

* * *

### ❌ Updating state after assuming success

Don't blindly assume every request succeeds.

Handle:

```text
Loading
Success
Error
Empty
```

* * *

### ❌ Putting async directly on `useEffect`

Avoid:

```jsx
useEffect(async () => {
  // ...
}, []);
```

Instead:

```jsx
useEffect(() => {
  async function fetchData() {
    // ...
  }

  fetchData();
}, []);
```

* * *

### ❌ Hard-coding API URLs everywhere

Prefer a centralized configuration/API layer as your application grows.

* * *

# 🔹 Best Practices

### ✅ 1. Always handle loading

```jsx
if (loading) {
  return <Loading />;
}
```

### ✅ 2. Handle errors

```jsx
catch (error) {
  setError(error.message);
}
```

### ✅ 3. Check HTTP status

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

### ✅ 4. Keep API logic organized

Separate API/service logic when your project grows.

### ✅ 5. Keep UI state separate from server data

Don't mix every piece of application state into one giant object.

### ✅ 6. Use stable dependencies

When fetching based on something such as `userId`, include it:

```jsx
[userId]
```

### ✅ 7. Think about race conditions

If requests can overlap, consider cancellation or a strategy that ensures an older response doesn't overwrite newer data.

* * *

# 🧠 My Biggest Takeaway

Today I learned that **React isn't just about creating UI — it's about creating UI that communicates with the real world.**

The important pattern I learned is:

```text
React State
    ↓
useEffect
    ↓
API Request
    ↓
Server
    ↓
Response
    ↓
setState()
    ↓
Re-render
```

I also understood how GET, POST, PATCH, and DELETE requests fit into frontend development, how to handle loading/error/empty states, and how React eventually connects to an Express + MongoDB backend.

This is a major step toward building my own **full-stack MERN applications**.

* * *
