# Chapter 2: Enter TypedFetch - Your API Superpower

*"The difference between a tool and a superpower is how it makes you feel when you use it."*

---

## The Moment Everything Changes

Remember Sarah from Chapter 1? She'd figured out APIs, but her code was getting messy. Error handling was a nightmare. Every API call looked like this:

```javascript
fetch('https://api.weather.com/forecast')
  .then(response => {
    if (!response.ok) {
      if (response.status === 404) {
        throw new Error('City not found')
      } else if (response.status === 401) {
        throw new Error('Invalid API key')
      } else {
        throw new Error('Something went wrong')
      }
    }
    return response.json()
  })
  .then(data => {
    // Finally! The actual data
    updateWeatherDisplay(data)
  })
  .catch(error => {
    console.error('Error:', error)
    showErrorMessage(error.message)
  })
```

15 lines of code just to make one API call. And she had dozens of these throughout her app.

Then her colleague Dave walked by. "Why are you writing all that boilerplate? Just use TypedFetch."

"TypedFetch?"

Dave smiled and rewrote her code:

```javascript
import { tf } from 'typedfetch'

const { data } = await tf.get('https://api.weather.com/forecast')
updateWeatherDisplay(data)
```

Sarah stared. "That's it?"

"That's it."

## Installing Your Superpower

Let's get TypedFetch into your project. It takes literally one command:

```bash
npm install typedfetch
```

Or if you prefer yarn/pnpm/bun:

```bash
yarn add typedfetch
pnpm add typedfetch
bun add typedfetch
```

That's it. No configuration files. No setup wizard. No initialization. It just works.

## Your First TypedFetch Call

Let's rewrite that dad joke fetcher from Chapter 1:

```javascript
// The old way (fetch)
fetch('https://icanhazdadjoke.com/', {
  headers: { 'Accept': 'application/json' }
})
.then(response => response.json())
.then(data => console.log(data.joke))
.catch(error => console.error('Error:', error))

// The TypedFetch way
import { tf } from 'typedfetch'

const { data } = await tf.get('https://icanhazdadjoke.com/', {
  headers: { 'Accept': 'application/json' }
})
console.log(data.joke)
```

Notice what's missing? All the ceremony. The `.then()` chains. The manual JSON parsing. The basic error handling. TypedFetch handles all of that for you.

## But Wait, What About Errors?

Great question! Let's break something on purpose:

```javascript
try {
  // This URL doesn't exist
  const { data } = await tf.get('https://fakesiteabcd123.com/api')
} catch (error) {
  console.log(error.message)
  console.log(error.suggestions)  // <- This is new!
}
```

Output:
```
Failed to fetch https://fakesiteabcd123.com/api: fetch failed

Suggestions:
• Check network connection
• Verify URL is correct  
• Try again in a moment
```

TypedFetch doesn't just tell you something went wrong - it helps you fix it. Every error comes with:
- A clear error message
- Suggestions for fixing it
- Debug information when you need it

## The Magic of Zero Configuration

Here's what TypedFetch configures automatically:

1. **JSON Parsing** - Response automatically parsed
2. **Error Handling** - Network and HTTP errors caught
3. **Content Headers** - Sets 'Content-Type' for you
4. **Smart Retries** - Retries failed requests intelligently
5. **Request Deduplication** - Prevents duplicate simultaneous calls

Let's see this in action:

```javascript
// Making multiple simultaneous calls to the same endpoint
const promise1 = tf.get('https://api.github.com/users/torvalds')
const promise2 = tf.get('https://api.github.com/users/torvalds')
const promise3 = tf.get('https://api.github.com/users/torvalds')

const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3])

// TypedFetch only made ONE actual network request!
// All three promises got the same result
```

## Let's Upgrade Weather Buddy

Remember our weather app from Chapter 1? Let's give it the TypedFetch treatment:

```html
<!DOCTYPE html>
<html>
<head>
    <title>Weather Buddy 2.0</title>
    <script type="module">
        import { tf } from 'https://esm.sh/typedfetch'
        
        window.getWeather = async function() {
            const city = document.getElementById('cityInput').value || 'Seattle'
            const resultDiv = document.getElementById('result')
            
            try {
                // So much cleaner!
                const { data } = await tf.get(`https://wttr.in/${city}?format=j1`)
                
                resultDiv.innerHTML = `
                    <h2>Weather in ${data.nearest_area[0].areaName[0].value}</h2>
                    <p>🌡️ Temperature: ${data.current_condition[0].temp_C}°C / ${data.current_condition[0].temp_F}°F</p>
                    <p>🌤️ Condition: ${data.current_condition[0].weatherDesc[0].value}</p>
                    <p>💨 Wind: ${data.current_condition[0].windspeedKmph} km/h</p>
                    <p>💧 Humidity: ${data.current_condition[0].humidity}%</p>
                `
            } catch (error) {
                // TypedFetch gives us helpful error messages
                resultDiv.innerHTML = `
                    <h2>Oops! Something went wrong</h2>
                    <p>${error.message}</p>
                    <ul>
                        ${error.suggestions?.map(s => `<li>${s}</li>`).join('') || ''}
                    </ul>
                `
            }
        }
    </script>
</head>
<body>
    <h1>Weather Buddy 2.0 🌤️</h1>
    <input type="text" id="cityInput" placeholder="Enter city name" />
    <button onclick="getWeather()">Get Weather</button>
    <div id="result"></div>
</body>
</html>
```

Look at that error handling! If something goes wrong, TypedFetch tells the user exactly what happened and how to fix it.

## The TypedFetch Philosophy

TypedFetch follows three core principles:

### 1. **Batteries Included** 🔋
Everything you need is built-in. No plugins to install, no middleware to configure.

```javascript
// Caching? Built-in.
const user1 = await tf.get('/api/user/123') // Network call
const user2 = await tf.get('/api/user/123') // Cache hit!

// Retries? Built-in.
const data = await tf.get('/flaky-api')     // Automatically retries on failure

// Type safety? Built-in. (We'll cover this in Chapter 7)
const user = await tf.get<User>('/api/user/123')
```

### 2. **Progressive Disclosure** 📈
Simple things are simple. Complex things are possible.

```javascript
// Simple: Just get data
const { data } = await tf.get('/api/users')

// Advanced: Full control when you need it
const { data, response } = await tf.get('/api/users', {
  headers: { 'Authorization': 'Bearer token' },
  cache: false,
  retries: 5,
  timeout: 10000
})
console.log('Status:', response.status)
console.log('Headers:', response.headers)
```

### 3. **Developer Empathy** ❤️
Every feature is designed to make your life easier.

```javascript
// Debugging? One line.
tf.enableDebug()

// Now every request logs helpful information:
// [TypedFetch] GET https://api.example.com/users
// [TypedFetch] ✅ 200 OK (123ms)
// [TypedFetch] 📦 Response size: 2.4kb
// [TypedFetch] 💾 Cached for 5 minutes
```

## Real-World Comparison

Let's fetch a GitHub user with both approaches:

### The fetch() Way:
```javascript
async function getGitHubUser(username) {
  try {
    const response = await fetch(`https://api.github.com/users/${username}`)
    
    if (!response.ok) {
      if (response.status === 404) {
        throw new Error(`User ${username} not found`)
      } else if (response.status === 403) {
        throw new Error('Rate limit exceeded. Try again later.')
      } else {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }
    }
    
    const data = await response.json()
    return data
    
  } catch (error) {
    if (error instanceof TypeError) {
      throw new Error('Network error. Check your connection.')
    }
    throw error
  }
}
```

### The TypedFetch Way:
```javascript
async function getGitHubUser(username) {
  const { data } = await tf.get(`https://api.github.com/users/${username}`)
  return data
}
```

Both handle errors properly. Both work with async/await. But which one would you rather write 50 times in your app?

## Common Questions (With Answers!)

**Q: "Is TypedFetch just a wrapper around fetch()?"**  
A: It's like asking if a Tesla is just a wrapper around wheels. Yes, it uses fetch() internally, but adds intelligent caching, automatic retries, error enhancement, type safety, request deduplication, and more.

**Q: "What about bundle size?"**  
A: The entire core is ~12KB gzipped. For context, that's smaller than most images on a webpage.

**Q: "Does it work in Node.js/Deno/Bun?"**  
A: Yes! TypedFetch works everywhere fetch() works.

**Q: "What if I need the raw Response object?"**  
A: You got it:
```javascript
const { data, response } = await tf.get('/api')
console.log(response.headers.get('content-type'))
```

**Q: "Can I still use async/await?"**  
A: That's the ONLY way to use TypedFetch. No more callback hell or promise chains.

## Your New Superpowers

Here's what you can now do that you couldn't before:

```javascript
// 1. Automatic caching
const user = await tf.get('/api/user')  // First call: ~200ms
const cached = await tf.get('/api/user') // Second call: ~1ms

// 2. Smart errors
try {
  await tf.get('/bad-endpoint')
} catch (error) {
  console.log(error.suggestions) // Actually helpful!
}

// 3. Request deduplication
// If you accidentally call the same endpoint multiple times
const [a, b, c] = await Promise.all([
  tf.get('/api/data'),
  tf.get('/api/data'),
  tf.get('/api/data')
])
// Only ONE network request is made!

// 4. Built-in debugging
tf.enableDebug() // See everything that's happening

// 5. Zero config
// No setup, no initialization, just import and use
```

## Practice Time! 🏋️

### Exercise 1: Convert to TypedFetch
Take these fetch() calls and rewrite them using TypedFetch:

```javascript
// 1. Basic GET
fetch('https://api.quotable.io/random')
  .then(r => r.json())
  .then(data => console.log(data))

// 2. POST with data
fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  body: JSON.stringify({
    title: 'My Post',
    body: 'This is the content',
    userId: 1
  }),
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(r => r.json())
.then(data => console.log(data))
```

### Exercise 2: Error Enhancement
Make this request fail and examine the error:

```javascript
try {
  // This domain doesn't exist
  await tf.get('https://this-domain-definitely-does-not-exist-123456.com/api')
} catch (error) {
  console.log('Message:', error.message)
  console.log('Type:', error.type)
  console.log('Suggestions:', error.suggestions)
  
  // Try the debug function
  if (error.debug) {
    error.debug()
  }
}
```

### Exercise 3: Cache Detective
Prove that TypedFetch is caching:

```javascript
// Time the first call
console.time('First call')
await tf.get('https://api.github.com/users/torvalds')
console.timeEnd('First call')

// Time the second call
console.time('Second call')
await tf.get('https://api.github.com/users/torvalds')
console.timeEnd('Second call')

// What's the difference?
```

## Key Takeaways 🎯

1. **TypedFetch is a zero-config API client** - Just install and use
2. **It handles all the boilerplate** - JSON parsing, error handling, headers
3. **Errors are actually helpful** - With suggestions and debug info
4. **Smart features work automatically** - Caching, retries, deduplication
5. **Progressive disclosure** - Simple by default, powerful when needed

## What's Next?

You've got TypedFetch installed and you've seen its power. But we've only scratched the surface. In Chapter 3, we'll dive deep into GET requests and discover features like:

- Query parameter magic
- Response transformations  
- Custom headers and authentication
- Performance optimization
- Real-time data fetching

We'll also evolve Weather Buddy to show live updates, handle multiple cities, and add a search feature - all powered by TypedFetch's GET superpowers.

Ready to master the art of reading data from APIs? See you in Chapter 3! 🚀

---

## Chapter Summary

- TypedFetch is a zero-configuration API client that makes fetch() calls simple
- Installation is one command: `npm install typedfetch`
- Basic usage: `const { data } = await tf.get(url)`
- Automatic features: JSON parsing, error handling, caching, retries, deduplication
- Errors include helpful messages and suggestions
- Works everywhere: browsers, Node.js, Deno, Bun
- Progressive disclosure: simple things are simple, complex things are possible
- Weather Buddy upgraded with better error handling and cleaner code

**Next Chapter Preview**: Deep dive into GET requests - the foundation of API communication. Learn query parameters, headers, authentication, and real-time updates.