Before You Start
Both servers must be running:
- Frontend:
http://localhost:5173(npm run dev) - Backend:
http://localhost:8000(uvicorn main:app --reload)
1Install TanStack Query
Run this in the frontend folder (my-admin):
# Run in the my-admin folder
npm install @tanstack/react-query
Add the QueryClientProvider to src/main.tsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App'
import './index.css'
const queryClient = new QueryClient()
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>,
)2Create API Client
Create the src/api/client.ts file:
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async get<T>(path: string): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`);
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json() as Promise<T>;
}
async post<T, D>(path: string, data: D): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json() as Promise<T>;
}
async delete(path: string): Promise<void> {
const response = await fetch(`${this.baseUrl}${path}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
}
}
export const apiClient = new ApiClient(API_URL);Term Tip: Env Vars / JSON
- Environment Variable: Runtime configuration injected outside code.
- JSON: The common data format used between frontend and backend APIs.
- HTTP Status Code: Numeric response signals for success/failure.
- Deep dive: Environment and Runtime, CORS troubleshooting
3Create React Hooks
Create the src/hooks/useProjects.ts file:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '../api/client';
// Type definitions
interface Project {
id: string;
name: string;
description?: string;
created_at: string;
updated_at: string;
}
interface ProjectCreate {
name: string;
description?: string;
}
interface ProjectListResponse {
items: Project[];
total: number;
}
// Hooks
export function useProjects() {
return useQuery({
queryKey: ['projects'],
queryFn: () => apiClient.get<ProjectListResponse>('/api/v1/projects'),
});
}
export function useCreateProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: ProjectCreate) =>
apiClient.post<Project, ProjectCreate>('/api/v1/projects', data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['projects'] });
},
});
}
export function useDeleteProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => apiClient.delete(`/api/v1/projects/${id}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['projects'] });
},
});
}4Use in Components
Ask AI to update your existing components to use the API connection:
AI Prompt
Copy and use
"Update the existing App.tsx to connect with the API. Changes: 1. Use useProjects, useCreateProject, useDeleteProject hooks instead of local state 2. Show loading state (isLoading) 3. Show error state (isError) 4. Disable buttons during create/delete (isPending) Keep the existing UI, only change the data source to the API. Hooks to use: - useProjects() - { data, isLoading, isError } - useCreateProject() - { mutate, isPending } - useDeleteProject() - { mutate, isPending }"
Show your existing code to the AI when requesting the update.
5Verify Connection
When you create a project from the frontend, it will be saved via the backend API.
If the project you created from the frontend also appears in the backend's /docs GET API, you're all set!
Getting a CORS Error?
If you see a "CORS" error in the browser console, check the backend's CORS settings:
- Check
allow_originsin the backend'smain.py ["http://localhost:5173"]must be included- Restart the backend server
For detailed solutions, see the CORS Troubleshooting guide.