Initial commit
This commit is contained in:
37
skills/react-setup/README.md
Normal file
37
skills/react-setup/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# React Setup Skill (Vite + Django Backend)
|
||||
|
||||
Spin up a fresh React app (Vite) that talks to a Django backend over HTTPS with correct CORS/CSRF handling, Axios defaults, and a basic router/layout scaffold.
|
||||
|
||||
## What This Skill Does
|
||||
- Initializes a new Vite React project in `frontend/`.
|
||||
- Configures `vite.config.js` for HTTPS dev using mkcert certs from `certs/`, port 5173, and proxying `/api` to `https://localhost:8000`.
|
||||
- Adds CSRF service and Axios interceptors that inject `X-CSRFToken` for mutating requests.
|
||||
- Sets up React Router layout (`Root`), navbar/footer, home page, and backend health check component that verifies CSRF + `/api/health/`.
|
||||
- Provides a minimal API config file to centralize endpoints.
|
||||
- Validates the build with `npm --prefix ./frontend run build`.
|
||||
|
||||
## Prerequisites
|
||||
- Django backend running over HTTPS (see `django-setup` skill) with endpoints `/api/csrf/` and `/api/health/`.
|
||||
- mkcert certificates stored in project `certs/` (key/cert filenames discovered in skill steps).
|
||||
- Node.js 18+ and npm available.
|
||||
|
||||
## Setup Summary (see SKILL.md for exact commands)
|
||||
1) **Create project**: `npm create vite@latest frontend -- --template react --yes`; then `npm --prefix ./frontend install react-router-dom axios`.
|
||||
2) **Configure `vite.config.js`**: import `fs`/`path`; load mkcert key/cert from `certs/`; set `server.https`, `server.port=5173`, and proxy `/api` → `https://localhost:8000` with `secure:false`.
|
||||
3) **API config** `src/config/api.js`: export `API_BASE_URL` (empty to use proxy) and `ENDPOINTS` with `CSRF: '/api/csrf/'`.
|
||||
4) **CSRF service** `src/services/csrf.js`: fetch `/api/csrf/` once, read `csrftoken` cookie, cache/token helpers.
|
||||
5) **Axios instance** `src/services/api.js`: `withCredentials:true`, JSON headers; request interceptor adds `X-CSRFToken` via `getCsrfToken()` for POST/PUT/PATCH/DELETE.
|
||||
6) **UI scaffold**: navbar/footer components; `Root` layout with `Outlet`; `Home` page using `BackendStatus` component that pings CSRF then `/api/health/`.
|
||||
7) **Routing**: `src/router/AppRoutes.jsx` exports `createAppRouter()` config; `src/router/index.jsx` wraps `RouterProvider`; `App.jsx` renders `<Router />`.
|
||||
8) **Verify**: `npm --prefix ./frontend run build` to ensure config/imports succeed.
|
||||
|
||||
## Outputs/Artifacts
|
||||
- Vite React app under `frontend/` with HTTPS dev server ready to hit Django.
|
||||
- `vite.config.js` with mkcert HTTPS + API proxy.
|
||||
- Axios + CSRF utilities wired for Django session auth.
|
||||
- Minimal router/layout/pages demonstrating backend connectivity.
|
||||
|
||||
## Notes & Gotchas
|
||||
- Make sure `certs/` exists and filenames match what mkcert generated; update `vite.config.js` accordingly.
|
||||
- Backend must expose `/api/csrf/` to set the `csrftoken` cookie; otherwise Axios interceptor will fail.
|
||||
- If you change backend host/port, update the proxy target in `vite.config.js` and any hardcoded `FRONTEND_URL` on the Django side.
|
||||
268
skills/react-setup/SKILL.md
Normal file
268
skills/react-setup/SKILL.md
Normal file
@@ -0,0 +1,268 @@
|
||||
---
|
||||
name: react-setup
|
||||
description: Use when initializing a new React frontend with Vite to connect to a Django backend over HTTPS. Sets up routing, CSRF protection, Axios config, and validates the build. Not for existing React projects.
|
||||
allowed-tools: Bash, Write, Edit, Read, Glob, Grep, TodoWrite, Task
|
||||
---
|
||||
|
||||
## Overview
|
||||
Sets up a production-ready React + Vite frontend configured for Django backend integration with:
|
||||
- HTTPS local development (using mkcert certificates)
|
||||
- CSRF token handling for Django session auth
|
||||
- Axios interceptors for automatic CSRF injection
|
||||
- React Router with layout structure
|
||||
- Production build validation
|
||||
|
||||
**When to use:** New React projects that need Django backend connectivity
|
||||
**When NOT to use:** Existing React projects, non-Django backends, or projects not using session auth
|
||||
|
||||
## Prerequisites
|
||||
- Django backend configured with HTTPS
|
||||
- mkcert certificates in `/certs/` directory (use `mkcert-https-setup` skill)
|
||||
- Node.js 18+ installed
|
||||
|
||||
---
|
||||
|
||||
Copy this checklist and track your progress:
|
||||
|
||||
- [ ] 1. Initialize React + Vite Project
|
||||
- [ ] 2. Configure vite.config.js:
|
||||
- [ ] 3. Create API Configuration
|
||||
- [ ] 4. Create CSRF Service
|
||||
- [ ] 5. Create Axios Configuration
|
||||
- [ ] 6. Create Navbar and Footer Components
|
||||
- [ ] 7. Create `src/layouts/Root.jsx`:
|
||||
- [ ] 8. Create Page Components
|
||||
- [ ] 9. Create `src/router/AppRoutes.jsx`
|
||||
- [ ] 10. Create Router Wrapper
|
||||
- [ ] 11. Update App.jsx
|
||||
- [ ] 12. Verify Setup
|
||||
|
||||
---
|
||||
|
||||
1. Initialize React + Vite Project
|
||||
|
||||
- `npm create vite@latest frontend -- --template react --yes`
|
||||
- `npm --prefix ./frontend install react-router-dom axios`
|
||||
|
||||
---
|
||||
|
||||
2. Configure vite.config.js:
|
||||
|
||||
1. List cert files in `certs/` to find actual filenames
|
||||
2. Read existing `frontend/vite.config.js` (created by Vite)
|
||||
3. Add imports: `fs`, `path`
|
||||
4. Add server config:
|
||||
- server.https: Load key/cert from `certs/` using found filenames
|
||||
- server.port: 5173
|
||||
- server.proxy: `/api` → `https://localhost:8000` (changeOrigin: true, secure: false)
|
||||
|
||||
---
|
||||
|
||||
3. Create API Configuration
|
||||
|
||||
src/config/api.js:
|
||||
```javascript
|
||||
export const API_BASE_URL = ''; // Empty string uses Vite proxy
|
||||
|
||||
export const ENDPOINTS = {
|
||||
CSRF: '/api/csrf/',
|
||||
// Add your API endpoints here as you build features
|
||||
// Example: PRODUCTS: '/api/products/',
|
||||
};
|
||||
```
|
||||
---
|
||||
|
||||
4. Create CSRF Service
|
||||
|
||||
src/services/csrf.js:
|
||||
```javascript
|
||||
import { ENDPOINTS } from '../config/api';
|
||||
|
||||
let csrfToken = null;
|
||||
|
||||
/**
|
||||
* Fetches and caches the CSRF token from Django backend
|
||||
* The token is stored in a cookie by Django after the first request
|
||||
*/
|
||||
export async function getCsrfToken() {
|
||||
// Return cached token if available
|
||||
if (csrfToken) {
|
||||
return csrfToken;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(ENDPOINTS.CSRF, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch CSRF token: ${response.status}`);
|
||||
}
|
||||
|
||||
// Django sets the token in a cookie after this request
|
||||
const cookies = document.cookie.split(';');
|
||||
const csrfCookie = cookies.find(cookie => cookie.trim().startsWith('csrftoken='));
|
||||
|
||||
if (!csrfCookie) {
|
||||
throw new Error('CSRF token not found in cookies');
|
||||
}
|
||||
|
||||
csrfToken = csrfCookie.split('=')[1].trim();
|
||||
return csrfToken;
|
||||
} catch (error) {
|
||||
console.error('CSRF token fetch error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cached CSRF token (useful for logout or token refresh)
|
||||
*/
|
||||
export function clearCsrfToken() {
|
||||
csrfToken = null;
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
5. Create Axios Configuration
|
||||
|
||||
src/services/api.js:
|
||||
```javascript
|
||||
import axios from 'axios';
|
||||
import { API_BASE_URL } from '../config/api';
|
||||
import { getCsrfToken } from './csrf'; // ADD THIS
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
// REQUEST INTERCEPTOR
|
||||
axiosInstance.interceptors.request.use(
|
||||
async (config) => {
|
||||
if (['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase())) {
|
||||
const csrfToken = await getCsrfToken();
|
||||
config.headers['X-CSRFToken'] = csrfToken;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
console.error('API request failed:', error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
||||
```
|
||||
---
|
||||
|
||||
6. Create Navbar and Footer Components
|
||||
|
||||
Create `src/components/Navbar.jsx`:
|
||||
```javascript
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<nav>
|
||||
<h1>My App</h1>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Create `src/components/Footer.jsx`:
|
||||
```javascript
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer>
|
||||
<p>© {new Date().getFullYear()} My App. All rights reserved.</p>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
7. Create `src/layouts/Root.jsx`:
|
||||
|
||||
```javascript
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import Navbar from '../components/Navbar';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
export default function Root() {
|
||||
return (
|
||||
<div>
|
||||
<Navbar />
|
||||
<main>
|
||||
<Outlet />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
8. Create Page Components
|
||||
|
||||
Create `src/components/BackendStatus.jsx`:
|
||||
1. Import axios from `../services/api`, useState, useEffect
|
||||
2. Initialize state: `status = "checking..."`, `error = null`
|
||||
3. useEffect hook (runs on mount):
|
||||
- First: `axios.get('/api/csrf/')` to initialize cookies
|
||||
- Second: `axios.get('/api/health/')` using same axios instance
|
||||
- Success path: update status with backend message
|
||||
- Error path: set error state, display "Backend unreachable"
|
||||
4. Component is presentational only (no routing/layout logic)
|
||||
|
||||
Create `src/pages/Home.jsx`:
|
||||
1. Render `<BackendStatus />`
|
||||
2. Add heading + text explaining the backend connectivity check
|
||||
|
||||
---
|
||||
|
||||
9. Create `src/router/AppRoutes.jsx`:
|
||||
|
||||
1. Export `createAppRouter()` function
|
||||
2. Returns route config array: Root layout at "/" with Home page as nested "/" child route
|
||||
3. Imports: `Root` from `../layouts/Root`, `Home` from `../pages/Home`
|
||||
|
||||
---
|
||||
|
||||
10. Create `src/router/index.jsx`:
|
||||
|
||||
1. Export default `AppRouter` component
|
||||
2. Create browser router from `createAppRouter()` config
|
||||
3. Return `<RouterProvider router={router} />`
|
||||
4. Imports: `createBrowserRouter`, `RouterProvider` from react-router-dom, `createAppRouter` from `./AppRoutes`
|
||||
|
||||
---
|
||||
|
||||
11. Update `src/App.jsx`:
|
||||
- Remove: All useState, demo imports (reactLogo, viteLogo, App.css), counter UI
|
||||
- Add imports: `Router` from `./router`
|
||||
- Render: `<Router />`
|
||||
|
||||
---
|
||||
|
||||
12. Verify Setup
|
||||
|
||||
Run `npm --prefix ./frontend run build` to validate imports, dependencies, and syntax
|
||||
|
||||
**Common issues:**
|
||||
- Missing certs: Check `/certs/` directory exists with .pem files
|
||||
- Module not found: Verify all imports use correct paths
|
||||
- CSRF errors: Ensure Django backend is running at `https://localhost:8000`
|
||||
Reference in New Issue
Block a user