The API
Your app looks great. But the data is fake. Where does real data come from?
1. What is an API
Your app needs data it doesn't own. Weather, maps, payments, user posts. That data lives on someone else's server. The API is the door between your app and that server.
π§βπ» The Restaurant
| Restaurant | Code |
|---|---|
| You (customer) | Your App |
| The waiter | The API |
| The kitchen | The Server |
| The menu | The Documentation |
| Your order | The Request |
| The food | The Response |
You never enter the kitchen. Your app never touches the database. The API carries everything back and forth.
The Pattern
Every API call follows this shape:

You ask. It answers. That's it.
π Summary
| Term | Meaning |
|---|---|
| API | The door between your app and a server |
| Request | What you send (URL + method) |
| Response | What comes back (status code + data) |
| Server | Someone else's computer with the data you need |
2. JSON
The format your app and the server agree on. Keys and values.
π§βπ» Example
{
"id": 1,
"dish": "Suqaar",
"price": 8
}Rules:
- Keys are always strings in double quotes
- Values can be: strings, numbers, booleans, arrays, objects
- No trailing commas
An array of items:
[
{ "id": 1, "dish": "Suqaar" },
{ "id": 2, "dish": "Bariis" }
]π Summary
| Type | Example |
|---|---|
| String | "Suqaar" |
| Number | 8 |
| Boolean | true |
| Array | [1, 2, 3] |
| Object | { "name": "Ahmed" } |
3. The Four Methods
Four things you can say to a server. That's the entire vocabulary.
π§βπ» Example
| Method | What it does | Example | Status |
|---|---|---|---|
| GET | Read | "get menu" | 200 |
| POST | Create | "new order" | 201 |
| PUT | Update | "update order #45" | 200 |
| DELETE | Remove | "cancel order #45" | 200 |
Status Codes
| Code | Meaning |
|---|---|
| 200 | OK, it worked |
| 201 | Created, something new exists |
| 404 | Not found, wrong address |
| 500 | Server error, not your fault |
π Summary
| Method | Action | Has body? |
|---|---|---|
| GET | Read data | No |
| POST | Create new data | Yes |
| PUT | Update existing data | Yes |
| DELETE | Remove data | No |
4. useEffect
Code that runs after React renders the component. Not inside the return. Not above it. Inside useEffect.
π§βπ» Example
Run once when the component mounts:
useEffect(() => {
console.log("Component loaded");
}, []);Run every time count changes:
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);The dependency array controls when it runs:
| Array | Runs when |
|---|---|
| [] | Once, on mount |
| [count] | Every time count changes |
| [a, b] | Every time a or b changes |
π Summary
| Task | Code |
|---|---|
| Run once | useEffect(() => { }, []) |
| Run on change | useEffect(() => { }, [value]) |
| Update tab title | document.title = "text" |
5. Fetch
Your React app talks to a server. fetch() sends the request. It returns a Promise: not the data, but a guarantee that data is coming.
What is a Promise
You ordered food. The waiter said "coming soon." That slip is a Promise. It's not the food. It's the guarantee that food is coming.
π§βπ» Example
function App() {
const [users, setUsers] = useState([]);
async function getUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await res.json();
return data;
}
useEffect(() => {
getUsers().then((data) => setUsers(data));
}, []);
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}getUsers fetches the data and returns it. useEffect calls it and uses .then() to save the result in state.
π Summary
| Task | Code |
|---|---|
| Create fetch function | async function getUsers() { } |
| Wait for response | const res = await fetch("url") |
| Convert to JSON | const data = await res.json() |
| Return data | return data |
| Call and save | getUsers().then((data) => setUsers(data)) |
6. Loading and Error States
Your fetch works. But the screen is blank for 2 seconds, then data appears. No spinner. No error message. What if the server is down?
π§βπ» Example
function App() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
async function getUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await res.json();
return data;
}
useEffect(() => {
getUsers()
.then((data) => setUsers(data))
.catch((err) => setError("Something went wrong"))
.finally(() => setLoading(false));
}, []);
if (loading) return <Spinner />;
if (error) return <Text>{error}</Text>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Same getUsers from before. Three things chained in useEffect: .then() saves data, .catch() catches errors, .finally() stops the spinner.
The Pattern
Every fetch in every React app follows this shape:
| State | Meaning |
|---|---|
| loading | Waiting for the server |
| error | Something went wrong |
| data | Success, render it |
π Summary
| Task | Code |
|---|---|
| Loading state | const [loading, setLoading] = useState(true) |
| Error state | const [error, setError] = useState(null) |
| Handle error | .catch((err) => setError("Something went wrong")) |
| Stop loading | .finally(() => setLoading(false)) |
| Show spinner | if (loading) return <Spinner /> |
| Show error | if (error) return <Text>{error}</Text> |
7. json-server
A fake backend you own. Saves data to a file. Full CRUD in 30 seconds.
π§βπ» Setup
Install:
npm install json-serverCreate db.json:
{
"posts": [
{ "id": 1, "title": "First post" },
{ "id": 2, "title": "Second post" }
]
}Run it:
npx json-server db.jsonYour API is live at http://localhost:3000/posts.
π Summary
| Task | Code |
|---|---|
| Install | npm install json-server |
| Data file | db.json |
| Run server | npx json-server db.json |
| Endpoint | http://localhost:3000/posts |
8. POST and DELETE
Reading data is not enough. Your app needs to create and remove.
π§βπ» POST (create)
async function addPost(title) {
const res = await fetch("http://localhost:3000/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: title }),
});
const newPost = await res.json();
setPosts([...posts, newPost]);
}π§βπ» DELETE (remove)
async function deletePost(id) {
await fetch(`http://localhost:3000/posts/${id}`, {
method: "DELETE",
});
setPosts(posts.filter((post) => post.id !== id));
}The options object
| Key | What it does | Required for |
|---|---|---|
| method | GET, POST, PUT, DELETE | POST, PUT, DELETE |
| headers | Tells server the format | POST, PUT |
| body | The data you're sending | POST, PUT |
GET has no options. You just pass the URL.
π Summary
| Task | Code |
|---|---|
| POST | method: "POST" + headers + body |
| DELETE | method: "DELETE" (no body) |
| Add to state | setPosts([...posts, newPost]) |
| Remove from state | setPosts(posts.filter(p => p.id !== id)) |