🚀 The easiest way to connect your Next.js app to Django REST Framework
A comprehensive, type-safe SDK that makes integrating Next.js applications with Django backends simple and powerful. Perfect for beginners and experts alike!
- 🎯 Beginner-Friendly: Get started in minutes with our step-by-step guide
- 🔒 Secure by Default: Built-in JWT authentication and CSRF protection
- 📝 Full TypeScript: Complete type safety for your API calls
- 🤖 Auto-Generated Code: Generate API clients from your OpenAPI specs
- ⚡ Modern React: Built for Next.js 15+ with React 18+ hooks
- 🧪 Well Tested: Comprehensive test suite with 95%+ coverage
npm install nextjs-django-client
# or
yarn add nextjs-django-client
# or
pnpm add nextjs-django-client
Choose the right bundle for your needs:
// Full bundle (258 KB) - All features included
import { createDjangoApiClient, useAuth, useRBAC } from 'nextjs-django-client';
// Core bundle (136 KB) - Basic features only (recommended for most users)
import { createDjangoApiClient, useAuth } from 'nextjs-django-client/core';
// Advanced bundle (171 KB) - RBAC, social login, advanced auth
import { useRBAC, useSocialLogin, AdvancedAuthProvider } from 'nextjs-django-client/advanced';
Create a new file lib/api.ts
in your Next.js project:
import { createDjangoApiClient } from 'nextjs-django-client';
// Replace with your Django API URL
export const apiClient = createDjangoApiClient('http://localhost:8000/api');
Wrap your app with the AuthProvider in app/layout.tsx
or pages/_app.tsx
:
import { AuthProvider } from 'nextjs-django-client';
import { apiClient } from '@/lib/api';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<AuthProvider apiClient={apiClient}>
{children}
</AuthProvider>
</body>
</html>
);
}
### Step 4: Start Making API Calls
Now you can use the API client anywhere in your app:
```tsx
'use client'; // For Next.js 13+ App Router
import { useState, useEffect } from 'react';
import { apiClient } from '@/lib/api';
interface User {
id: number;
email: string;
name: string;
}
export default function UsersPage() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUsers() {
try {
const response = await apiClient.get<User[]>('/users/');
setUsers(response);
} catch (error) {
console.error('Failed to fetch users:', error);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []);
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
</div>
);
}
'use client';
import { useAuth } from 'nextjs-django-client';
export default function LoginForm() {
const { login, isLoading, error } = useAuth();
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
await login({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
name="email"
type="email"
required
className="border rounded px-3 py-2"
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
id="password"
name="password"
type="password"
required
className="border rounded px-3 py-2"
/>
</div>
{error && <p className="text-red-500">{error.message}</p>}
<button
type="submit"
disabled={isLoading}
className="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Login'}
</button>
</form>
);
}
'use client';
import { useAuth } from 'nextjs-django-client';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function ProtectedPage() {
const { user, isLoading, error } = useAuth(); // error property available for error handling
const router = useRouter();
useEffect(() => {
if (!isLoading && !user) {
router.push('/login');
}
}, [user, isLoading, router]);
if (isLoading) return <div>Loading...</div>;
if (!user) return null;
return (
<div>
<h1>Welcome, {user.name}!</h1>
<p>This is a protected page.</p>
</div>
);
}
The easiest way to get started is by generating your API client from your Django OpenAPI schema. This gives you:
- ✅ 100% Type Safety: All your API endpoints are fully typed
- ✅ Auto-completion: Your IDE knows all available methods and parameters
- ✅ Always Up-to-date: Regenerate when your API changes
- ✅ Zero Boilerplate: No manual API client code to write
First, make sure your Django project exports an OpenAPI schema. Add this to your Django urls.py
:
# urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.schemas import get_schema_view
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('your_app.urls')),
# Add this line to export your schema
path('api/schema/', get_schema_view(
title="Your API",
description="API for your awesome project",
version="1.0.0"
), name='openapi-schema'),
]
# Download your schema (replace with your URL)
curl http://localhost:8000/api/schema/ > openapi.json
# Generate your API client
npx nextjs-django-codegen generate -i openapi.json -o src/generated
// src/generated/index.ts is automatically created
import { GeneratedApiClient, useGetUsers, useCreateUser } from '@/generated';
// Create your client instance
const client = new GeneratedApiClient('http://localhost:8000/api');
// Use in React components with generated hooks
export default function UsersPage() {
const { data: users, isLoading, error } = useGetUsers();
const createUser = useCreateUser();
const handleCreateUser = async () => {
await createUser.mutateAsync({
name: 'John Doe',
email: 'john@example.com'
});
};
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Users</h1>
<button onClick={handleCreateUser}>Add User</button>
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
Create openapi.config.js
for custom settings:
module.exports = {
input: './openapi.json',
output: './src/generated',
generateTypes: true,
generateClient: true,
generateHooks: true,
clientName: 'GeneratedApiClient', // Default changed to avoid naming conflicts
prettier: true,
};
If you prefer manual setup or need custom configuration:
import { configureAuth } from 'nextjs-django-client';
const authConfig = configureAuth({
loginEndpoint: '/api/auth/login/',
refreshEndpoint: '/api/auth/refresh/',
userEndpoint: '/api/auth/user/',
useEmailAsUsername: true,
secureTokens: true,
});
import { createApiClient } from 'nextjs-django-client';
const apiClient = createApiClient({
baseURL: 'https://api.example.com',
timeout: 30000,
defaultHeaders: {
'Content-Type': 'application/json',
},
retries: 3,
retryDelay: 1000,
});
The SDK is built with TypeScript-first approach:
interface CustomUser extends User {
customField: string;
}
// Type-safe authentication
const { user } = useAuth<CustomUser>();
// Type-safe API calls
const response = await apiClient.get<CustomUser[]>('/users/');
Choose the right bundle for your needs to minimize bundle size:
- Core Bundle (136 KB): Basic HTTP client, authentication, and OpenAPI generation
- Advanced Bundle (171 KB): RBAC, social login, and advanced authentication features
- Full Bundle (259 KB): All features included
- ✅ No Node.js dependencies in browser bundles
- ✅ Tree-shakeable exports for optimal bundle size
- ✅ ES2022+ target for modern browsers
- ✅ Separate CLI bundle with Node.js dependencies
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch
# Install dependencies
npm install
# Start development build
npm run dev
# Run linting
npm run lint
# Format code
npm run format
# Type checking
npm run type-check
src/
├── auth/ # Authentication system
│ ├── providers/ # React context providers
│ ├── hooks/ # Authentication hooks
│ ├── types/ # TypeScript types
│ └── utils/ # Auth utilities
├── client/ # HTTP client
│ ├── http/ # Core HTTP client
│ ├── interceptors/ # Request/response interceptors
│ └── types/ # HTTP client types
├── state/ # State management (coming soon)
├── hooks/ # React hooks
├── utils/ # Utility functions
└── types/ # Global TypeScript types
- JWT auto-refresh mechanism
- Enhanced
useAuth
hook with auto-refresh - Secure cookie handling improvements
- Customizable authentication endpoints
- Role-based access control (RBAC)
- Social login providers
- Advanced state management with optimistic updates
- Tag-based cache invalidation
- Batch mutations
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Ensure all tests pass and coverage is maintained
- Submit a pull request
# In your Django settings.py
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # Your Next.js dev server
"https://yourdomain.com", # Your production domain
]
CORS_ALLOW_CREDENTIALS = True
// Check if token is being sent
apiClient.addInterceptor({
request: (config) => {
console.log('Headers:', config.headers);
return config;
}
});
# Regenerate your API client
npx nextjs-django-codegen generate -i openapi.json -o src/generated
# Make sure your OpenAPI schema is valid
npx nextjs-django-codegen validate -i openapi.json
// Use the correct Django dev server URL
const apiClient = createDjangoApiClient('http://127.0.0.1:8000/api');
// Not localhost if you're having issues
- 📖 Documentation: Check our full documentation
- 🐛 Bug Reports: Open an issue
- 💬 Questions: Start a discussion
- 📧 Email: support@nextjs-django-client.com
We love contributions! Here's how you can help:
- Fork the repository
-
Create a feature branch:
git checkout -b feature/amazing-feature
- Make your changes and add tests
-
Run tests:
npm test
- Submit a pull request
See our Contributing Guide for detailed instructions.
MIT License - see LICENSE file for details.
Made with ❤️ for the Django + Next.js community