Skip to main content

Overview

The Nadie Sabe Nada podcast application integrates with two primary APIs:
  1. Podcast API: Custom API for fetching podcast episodes and metadata
  2. YouTube Data API v3: For finding and displaying video versions of podcast episodes

Podcast API

The application uses a custom podcast API hosted on Netlify to fetch all podcast episodes.

Base URL

https://nsn-podcast-api-rapidapi.netlify.app

Endpoints

GET /podcast
endpoint
Fetches all podcast episodes with full metadata.Implementation: src/store/slices/podcastSlice.js:4-7Response Structure:
{
  "allEpisodes": [
    {
      "title": "Episode Title",
      "description": "Episode description",
      "audio": "https://example.com/episode.mp3",
      "pubDate": "2024-01-15"
    }
  ]
}

Implementation

The podcast data is fetched using Redux Toolkit’s createAsyncThunk:
podcastSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";

export const fetchPodcasts = createAsyncThunk(
    "podcast/fetchPodcasts", 
    async () => {
        const response = await axios.get(
            "https://nsn-podcast-api-rapidapi.netlify.app/podcast"
        );
        return response.data.allEpisodes;
    }
);

State Management

The podcast slice manages the following state:
{
    songs: [],
    loading: true,
    error: null,
    listenedEpisodes: [],
    favoriteEpisodes: [],
    completedEpisodes: [],
    listenLaterEpisodes: [],
    searchTerm: ""
}

Error Handling

The API integration includes error handling at multiple levels:
  1. Network Errors: Caught by Redux Toolkit’s rejection handler
  2. Invalid Response: Handled in component level
  3. Missing Data: Navigates to 404 page
Error Handling Example
if (!foundPodcast) {
    navigate("/404");
}

YouTube Data API v3

The application uses YouTube’s Data API to find video versions of podcast episodes.

Endpoint

Searches for videos on YouTube matching the podcast episode title.Implementation:
  • src/components/PodcastDetail/PodcastDetail.jsx:193-197
  • src/components/LastPodcast/LastPodcast.jsx:188-192

Request Structure

The application constructs YouTube API requests with the following parameters:
Request Example
const response = await fetch(
    `https://www.googleapis.com/youtube/v3/search` +
    `?part=snippet` +
    `&q=${encodeURIComponent(podcast.title)}` +
    `&key=${YT_API_KEY}` +
    `&channelId=${CHANNEL_ID}` +
    `&type=video` +
    `&maxResults=1`
);

Query Parameters

part
string
default:"snippet"
The part parameter specifies a comma-separated list of one or more search resource properties.
q
string
required
The search query term. In this case, the podcast episode title (URL-encoded).
key
string
required
Your YouTube Data API key from environment variables (REACT_APP_YT_API_KEY).
channelId
string
required
Restricts search results to videos from the specified channel (REACT_APP_CHANNEL_ID).
type
string
default:"video"
Restricts results to a particular type of resource (video, channel, or playlist).
maxResults
number
default:"1"
Maximum number of results to return (1-50). Set to 1 to get only the most relevant video.

Response Structure

YouTube API Response
{
  "items": [
    {
      "id": {
        "videoId": "dQw4w9WgXcQ"
      },
      "snippet": {
        "title": "Episode Title",
        "description": "Video description",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/..."
          }
        }
      }
    }
  ]
}

Video Player Integration

Once a video ID is retrieved, it’s displayed using the react-youtube component:
YouTube Component
import YouTube from "react-youtube";

<YouTube
    videoId={youtubeVideoId}
    opts={{
        height: "390",
        width: "640",
        playerVars: {
            autoplay: 0
        },
        modestbranding: 1,
        showinfo: 0
    }}
    className={styles.youtubePlayer}
    onPlay={() => dispatch(togglePlay(false))}
/>

Implementation Details

The YouTube API is called when a podcast detail page loads:
const YT_API_KEY = process.env.REACT_APP_YT_API_KEY;
const CHANNEL_ID = process.env.REACT_APP_CHANNEL_ID;

useEffect(() => {
    const foundPodcast = songs.find(
        (song) => slugify(song.title) === id
    );
    setPodcast(foundPodcast);

    const fetchYoutubeVideo = async () => {
        if (foundPodcast) {
            try {
                const response = await fetch(
                    `https://www.googleapis.com/youtube/v3/search` +
                    `?part=snippet` +
                    `&q=${encodeURIComponent(foundPodcast.title)}` +
                    `&key=${YT_API_KEY}` +
                    `&channelId=${CHANNEL_ID}` +
                    `&type=video` +
                    `&maxResults=1`
                );
                const data = await response.json();
                if (data.items && data.items.length > 0) {
                    setYoutubeVideoId(data.items[0].id.videoId);
                }
            } catch (error) {
                console.error("Error fetching YouTube video:", error);
            }
        } else {
            navigate("/404");
        }
    };

    fetchYoutubeVideo();
}, [id, songs, navigate]);

Error Handling

Podcast API Errors

Errors from the podcast API are captured in the Redux state:
Error State
state.error = action.error.message;
Components can check the error state and display appropriate messages:
Component Error Handling
const { songs, loading, error } = useSelector((state) => state.podcast);

if (error) {
    return <div>Error loading podcasts: {error}</div>;
}

YouTube API Errors

YouTube API errors are handled with try-catch blocks:
YouTube Error Handling
try {
    const response = await fetch(youtubeApiUrl);
    const data = await response.json();
    // Process data
} catch (error) {
    console.error("Error fetching YouTube video:", error);
    // Video player won't render, but audio player continues to work
}
If the YouTube API fails, the application gracefully degrades - the audio player continues to function without the video component.

Rate Limiting

YouTube API Quota

  • Daily Quota: 10,000 units per day (default)
  • Search Cost: 100 units per request
  • Requests per Day: ~100 searches per day
Monitor your YouTube API usage in the Google Cloud Console to avoid hitting quota limits. Consider implementing caching for production use.

Optimization Strategies

  1. Cache video IDs: Store video IDs in localStorage to reduce API calls
  2. Lazy loading: Only fetch videos when the detail page is viewed
  3. Conditional loading: Check if video ID exists before making API call

Testing API Integration

You can test the APIs directly:
curl https://nsn-podcast-api-rapidapi.netlify.app/podcast

Dependencies

The following packages are used for API integration:
  • axios (^1.7.7): HTTP client for podcast API requests
  • react-youtube (^10.1.0): YouTube player component
  • @reduxjs/toolkit (^2.2.1): State management and async thunks

Best Practices

  1. Always encode URLs: Use encodeURIComponent() for search queries
  2. Handle errors gracefully: Don’t crash the app if APIs fail
  3. Show loading states: Provide feedback while fetching data
  4. Implement retry logic: For transient network errors
  5. Cache responses: Reduce unnecessary API calls

Build docs developers (and LLMs) love