Skip to main content

Overview

The ApiTesting component provides a developer-focused interface for testing protected API endpoints in Cat Web. It demonstrates API integration, displays available containers, and allows testing of authenticated API calls with visual feedback. Source: src/components/ApiTesting.tsx

Key Features

  • Test protected API endpoints with authentication
  • Load and display containers from the API
  • Interactive UI with buttons and dropdowns
  • Toast notifications for success and error states
  • Integration with custom useApiService hook
  • Auth0 authentication awareness

Props

This component does not accept any props.
const ApiTesting: React.FC = () => { ... }

State Management

The component manages container data with React state:
const [containers, setContainers] = React.useState<{ id: number; name: string }[]>([]);

Container Type

Containers are typed with the following structure:
{
    id: number;
    name: string;
}

API Integration

The component uses the useApiService custom hook for API operations:
const apiService = useApiService();

Loading Containers

Containers are loaded automatically when the user is authenticated:
useEffect(() => {
    if (isAuthenticated) {
        console.log('Load containers');
        apiService.getContainers().then((containers) => {
            setContainers(containers);
        });
    }
}, [isAuthenticated, apiService]);

Testing Protected API

The component provides a button to test protected API endpoints:
const handleCallProtectedApi = async () => {
    try {
        const phoneNumbers = await apiService.getPhoneNumbers();
        console.log('Phone numbers:', phoneNumbers);
        toast.success('Phone numbers loaded.');
    } catch (err) {
        console.error('API call failed', err);
        toast.error('Failed to load phone numbers.');
    }
};

User Interface

The component renders a simple testing interface with two main sections:

API Testing Button

<Button onClick={handleCallProtectedApi}>
    Call Protected API
</Button>
Clicking this button:
  1. Calls the getPhoneNumbers() API method
  2. Logs the response to the console
  3. Shows a success toast notification
  4. Shows an error toast if the call fails

Container Dropdown

When containers are loaded, a dropdown appears:
{containers.length > 0 && (
    <Box sx={{ marginTop: 2 }}>
        <FormControl fullWidth>
            <InputLabel id="container-select-label">Container</InputLabel>
            <Select
                labelId="container-select-label"
                id="container-select"
                value={containers.length > 0 ? containers[0].id : ''}
                label="Container"
                onChange={(e) => console.log(e.target.value)}
            >
                {containers.map((container) => (
                    <MenuItem key={container.id} value={container.id}>
                        {container.name}
                    </MenuItem>
                ))}
            </Select>
        </FormControl>
    </Box>
)}

Material-UI Components

The component uses several Material-UI components:
  • Box: Container for layout and spacing
  • Button: Triggers API testing
  • FormControl: Wrapper for form inputs
  • InputLabel: Label for the select dropdown
  • Select: Dropdown menu for containers
  • MenuItem: Individual dropdown options

Toast Notifications

The component uses react-toastify for user feedback:
import { toast } from 'react-toastify';

// Success notification
toast.success('Phone numbers loaded.');

// Error notification
toast.error('Failed to load phone numbers.');

Required Setup

Ensure you have configured the ToastContainer in your app:
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function App() {
    return (
        <>
            <YourApp />
            <ToastContainer />
        </>
    );
}

Usage Examples

Basic Usage in Routes

import { Routes, Route } from 'react-router-dom';
import ApiTesting from './components/ApiTesting';

function App() {
    return (
        <Routes>
            <Route path="/api-tests" element={<ApiTesting />} />
        </Routes>
    );
}

Protected Route with AuthGuard

import AuthGuard from './components/AuthGuard';
import MasterLayout from './components/MasterLayout';
import ApiTesting from './components/ApiTesting';

function App() {
    return (
        <Routes>
            <Route path="/" element={
                <AuthGuard component={<MasterLayout />} />
            }>
                <Route path="api-tests" element={<ApiTesting />} />
            </Route>
        </Routes>
    );
}

Development Access

Add a navigation link in your layout:
<Link to="/api-tests">
    <ListItemButton>
        <ListItemIcon>
            <ApiIcon />
        </ListItemIcon>
        <ListItemText primary="API tests" />
    </ListItemButton>
</Link>

Complete Source Code

import React, { useEffect } from 'react';
import { Box, Button, FormControl, InputLabel, MenuItem, Select } from '@mui/material';
import { toast } from 'react-toastify';
import useApiService from '../hooks/useApiService';
import { useAuth0 } from '@auth0/auth0-react';

const ApiTesting: React.FC = () => {
    const { isAuthenticated } = useAuth0();
    const [containers, setContainers] = React.useState<{ id: number; name: string }[]>([]);
    const apiService = useApiService();
    
    useEffect(() => {
        if (isAuthenticated) {
            console.log('Load containers');
            apiService.getContainers().then((containers) => {
                setContainers(containers);
            });
        }
    }, [isAuthenticated, apiService]);

    const handleCallProtectedApi = async () => {
        try {
            const phoneNumbers = await apiService.getPhoneNumbers();
            console.log('Phone numbers:', phoneNumbers);
            toast.success('Phone numbers loaded.');
        } catch (err) {
            console.error('API call failed', err);
            toast.error('Failed to load phone numbers.');
        }
    };

    return (
        <Box sx={{ padding: 2 }}>
            <Button onClick={handleCallProtectedApi}>
                Call Protected API
            </Button>
            {containers.length > 0 && (
                <Box sx={{ marginTop: 2 }}>
                    <FormControl fullWidth>
                        <InputLabel id="container-select-label">Container</InputLabel>
                        <Select
                            labelId="container-select-label"
                            id="container-select"
                            value={containers.length > 0 ? containers[0].id : ''}
                            label="Container"
                            onChange={(e) => console.log(e.target.value)}
                        >
                            {containers.map((container) => (
                                <MenuItem key={container.id} value={container.id}>
                                    {container.name}
                                </MenuItem>
                            ))}
                        </Select>
                    </FormControl>
                </Box>
            )}
        </Box>
    );
};

export default ApiTesting;

API Service Hook

The component depends on the useApiService hook which should provide:
interface ApiService {
    getContainers: () => Promise<Array<{ id: number; name: string }>>;
    getPhoneNumbers: () => Promise<any>;
}
The hook handles:
  • Authentication token management
  • API endpoint configuration
  • Request/response handling
  • Error handling

Best Practices

  1. Developer Tool: This component is intended for development and testing purposes
  2. Authentication Required: Always use behind authentication (AuthGuard)
  3. Error Handling: Use try-catch blocks for all API calls
  4. User Feedback: Provide toast notifications for all user actions
  5. Console Logging: Keep console logs for debugging API responses
  6. Loading States: Consider adding loading indicators for async operations

Extending the Component

Adding More API Tests

const handleTestAnotherEndpoint = async () => {
    try {
        const result = await apiService.someOtherMethod();
        console.log('Result:', result);
        toast.success('API call successful');
    } catch (err) {
        console.error('API call failed', err);
        toast.error('API call failed');
    }
};

// Add button in JSX
<Button onClick={handleTestAnotherEndpoint}>
    Test Another Endpoint
</Button>

Adding Loading State

const [loading, setLoading] = useState(false);

const handleCallProtectedApi = async () => {
    setLoading(true);
    try {
        const phoneNumbers = await apiService.getPhoneNumbers();
        toast.success('Phone numbers loaded.');
    } catch (err) {
        toast.error('Failed to load phone numbers.');
    } finally {
        setLoading(false);
    }
};

<Button onClick={handleCallProtectedApi} disabled={loading}>
    {loading ? 'Loading...' : 'Call Protected API'}
</Button>
  • useApiService - Custom hook for API operations
  • useAuth0 - Auth0 authentication hook

Build docs developers (and LLMs) love