A secure Next.js 15 boilerplate for integrating Dify.ai with Firebase authentication and credit-based usage tracking. This project provides a complete foundation for building AI-powered applications with proper user management and cost tracking.
- π Features
- π Prerequisites
- π οΈ Setup Instructions
- π¨ Development Experience
- π― Usage
- π¬ Conversation Management
- ποΈ Project Structure
- π§ Configuration
- π§ͺ Testing
- π Adding More Languages
- π Deployment
- π Monitoring
- π Authentication System
- π‘οΈ Security Considerations
- π€ Contributing
- π License
- π Support
- π Release Management
- π Additional Documentation
- π§ Roadmap
- β Next.js 15 with App Router and Server Actions
- β Firebase Authentication with magic link (passwordless) login
- β Firestore Database with security rules and real-time updates
- β Credit Management System with token-based deduction (1 credit = 1000 tokens)
- β Secure Dify.ai Integration using server-side API calls only
- β TypeScript for type safety
- β Tailwind CSS 4 with shadcn/ui components
- β ESLint & Prettier for code quality
- β Google Analytics with Firebase Analytics integration
- π HTTP-only cookie authentication - Secure Firebase ID token handling
- π API keys never exposed to client-side code
- π Server-side validation for all Dify API calls
- π Credit pre-flight checks to prevent unauthorized usage
- π Atomic transactions for credit deduction
- π Firebase security rules protecting user data
- π Sentry error tracking with privacy-first configuration
- π± Responsive design for all screen sizes
- β‘ Real-time credit updates via Firestore listeners
- π¬ Custom chat interface with token usage tracking
- π Credit history and usage analytics
- π¨ Modern UI with loading states and error handling
- π Conversation management with React Query caching
- β‘ Optimistic updates for instant UI feedback
- π Real-time streaming chat with production-ready error handling and retry logic
- Node.js 18+ and npm/yarn
- Firebase project with Firestore and Authentication enabled
- Dify.ai account with API access
git clone https://github.com/mostafa-drz/nextjs-dify-firebase-starter.git
cd nextjs-dify-firebase-starter
npm install-
Create Firebase Project
- Visit Firebase Console
- Click "Create a project"
- Follow the setup wizard
-
Enable Authentication
- Go to Authentication β Sign-in method
- Enable Email/Password provider
- (Optional) Enable Google provider for OAuth
-
Setup Firestore Database
- Go to Firestore Database
- Click "Create database"
- Choose "Production mode"
- Select your preferred location
-
Generate Service Account Key
- Go to Project Settings β Service accounts
- Click "Generate new private key"
- Download the JSON file (keep it secure)
-
Enable Analytics (Optional)
- Go to Analytics β Dashboard
- Follow setup steps if not already enabled
- Copy the Measurement ID from Data Streams
Copy .env.example to .env.local and fill in your values:
cp .env.example .env.localRequired environment variables:
# Firebase Client Configuration
NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-XXXXXXXXXX
# Firebase Admin (Server-side)
FIREBASE_PROJECT_ID=your_project_id
FIREBASE_CLIENT_EMAIL=your_service_account_email
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# Dify AI Configuration
DIFY_API_KEY=your_dify_api_key
DIFY_BASE_URL=https://api.dify.ai/v1
# Application Configuration
NEXT_PUBLIC_SUPPORT_EMAIL=support@yourdomain.com
# Sentry Configuration (Optional)
NEXT_PUBLIC_SENTRY_DSN=https://your_public_key@o0.ingest.sentry.io/0
SENTRY_DSN=https://your_public_key@o0.ingest.sentry.io/0
SENTRY_AUTH_TOKEN=your_sentry_auth_token_here
SENTRY_ORG=your_sentry_org_slug
SENTRY_PROJECT=your_sentry_project_slug
# App Configuration
SUPPORT_EMAIL=support@yourdomain.com
NEXT_PUBLIC_SUPPORT_EMAIL=support@yourdomain.comDeploy the security rules to protect your data:
# Install Firebase CLI globally
npm install -g firebase-tools
# Login to Firebase
firebase login
# Initialize Firestore in your project
firebase init firestore
# - Select your existing Firebase project
# - Keep default firestore.rules file
# - Keep default firestore.indexes.json file
# Deploy the security rules
firebase deploy --only firestore:rules-
Get Dify API Key
- Visit Dify.ai and create account
- Create a new app or use existing one
- Copy the API key from app settings
-
Configure App Settings
- Ensure your Dify app supports the required endpoints
- Configure any necessary conversation settings
- Test the API key works with your app
npm run devOpen http://localhost:3000 to see the application.
This project includes a comprehensive development setup with automated code formatting, linting, and quality checks to ensure consistent code style and prevent common errors.
The project is configured with modern development tools for the best developer experience:
- Prettier: Simple, standard code formatting with Tailwind CSS class sorting
- ESLint: Minimal linting with Next.js and TypeScript defaults
- TypeScript: Type checking for build validation
- Husky: Git hooks for automated quality checks
Pre-configured VSCode/Cursor settings for optimal development:
- Format on save enabled
- Auto-fix ESLint issues on save
- Tailwind CSS IntelliSense support
- TypeScript IntelliSense and error highlighting
- Recommended extensions list
Automated quality checks run on every commit and push:
- Pre-commit: Runs linting and formatting on staged files
- Pre-push: Runs TypeScript checking and build verification
# Development
npm run dev # Start development server with hot reload
npm run build # Build for production
npm run start # Start production server
# Code Quality
npm run lint # Check code with ESLint
npm run lint:fix # Fix ESLint issues automatically
npm run format # Format code with Prettier
npm run format:check # Check if code is properly formatted
npm run typecheck # Type check with TypeScript compiler
# Git Hooks (handled automatically)
npm run prepare # Initialize Husky (runs on npm install)The project includes .vscode/ configuration with:
- Prettier: Code formatting
- ESLint: Code linting
- Tailwind CSS IntelliSense: CSS class completion
- Code Spell Checker: Spelling validation
- Error Lens: Inline error display
- Auto Rename Tag: Automatic HTML/JSX tag renaming
- Auto-format on save for consistent code style
- Auto-fix ESLint issues to prevent common errors
- Tailwind class sorting for better organization
- TypeScript hints for better development experience
-
Clone and install dependencies:
git clone https://github.com/mostafa-drz/nextjs-dify-firebase-starter.git cd nextjs-dify-firebase-starter npm install -
Open in VSCode/Cursor:
code . # or cursor .
-
Install recommended extensions when prompted
-
Configure environment (see Environment Configuration section)
-
Verify setup:
npm run typecheck # Should pass without errors npm run lint # Should pass without errors npm run build # Should build successfully
The project uses minimal, standard formatting conventions:
- Print width: 100 characters
- Single quotes for strings
- Semicolons required
- 2 spaces for indentation
- Tailwind classes automatically sorted
Before every commit, the following checks run automatically:
- ESLint: Checks for code quality issues
- Prettier: Ensures consistent formatting
- TypeScript: Validates type safety
Before every push:
- TypeScript compilation: Ensures no type errors
- Production build: Ensures the app builds successfully
Husky hooks not working?
npx husky installESLint/Prettier conflicts?
The project uses eslint-config-prettier to disable conflicting rules automatically.
TypeScript errors on build?
npm run typecheck # Check specific TypeScript issuesExtensions not working in Cursor? Cursor is compatible with VSCode extensions. Install the recommended extensions manually if auto-prompt doesn't appear.
Use the DifyChat component in your pages:
import { DifyChat } from '@/components/dify/DifyChat';
export default function MyPage() {
return (
<DifyChat
apiKey="app-your-dify-key" // Server-side only, never exposed
name="My Assistant"
placeholder="Ask me anything..."
welcomeMessage="Hello! How can I help you today?"
/>
);
}Enable real-time streaming for instant message updates:
import { DifyChat } from '@/components/dify/DifyChat';
export default function StreamingPage() {
return (
<DifyChat
name="Streaming Assistant"
enableStreaming={true}
streamingMode="auto"
placeholder="Ask me anything with real-time streaming..."
welcomeMessage="Hello! Watch my responses appear in real-time!"
/>
);
}Streaming Features:
- β Real-time message updates - See responses as they're generated
- β Production-ready error handling - Automatic retry with exponential backoff
- β Proper cleanup - AbortController prevents memory leaks
- β Stop functionality - Users can stop streaming at any time
- β Fallback support - Gracefully falls back to blocking mode on errors
- β Configurable - Choose between auto-streaming or manual control
Access credit information using the useCredits hook:
import { useCredits } from '@/lib/hooks/useCredits';
export function MyComponent() {
const { availableCredits, hasEnoughCredits, deductForTokens } = useCredits();
return (
<div>
<p>Available: {availableCredits} credits</p>
{!hasEnoughCredits(10) && <p>Insufficient credits!</p>}
</div>
);
}Use server actions for secure Dify API calls:
import { sendDifyMessage } from '@/lib/actions/dify';
// In a server action or API route
const result = await sendDifyMessage(userId, apiKey, {
query: 'Hello',
user: userId,
response_mode: 'blocking',
});
if (result.success) {
console.log('Response:', result.data?.answer);
console.log('Tokens used:', result.usage?.total_tokens);
}This boilerplate implements a client-side conversation management system using React Query for caching and optimistic updates. This approach prioritizes simplicity and performance while providing a solid foundation for developers to extend with their own persistence strategies.
Why React Query?
- Automatic caching with intelligent invalidation
- Optimistic updates for instant UI feedback
- Background refetching to keep data fresh
- Error handling with retry logic
- DevTools for debugging (development only)
Why Not Firebase/Firestore?
- Simplicity: Avoids additional database complexity
- Performance: Client-side caching is faster than database queries
- Flexibility: Developers can choose their own persistence layer
- Cost: Reduces Firebase read/write operations
User Action β Optimistic Update β API Call β Cache Update β UI Update
β β β β β
Click Send β Show Message β Call Dify β Update Cache β Show Response
import { useConversationMessages } from '@/lib/hooks/useConversationMessages';
export function ChatComponent() {
const { messages, isLoading, addMessageOptimistically, invalidate } = useConversationMessages(
conversationId,
userId,
apiKey
);
const handleSendMessage = async (content: string) => {
const tempMessage = { id: 'temp', content, role: 'user' };
addMessageOptimistically(tempMessage); // Instant UI update
const result = await sendDifyMessage(userId, apiKey, { query: content });
// Cache automatically updated with real data
};
return (
<div>
{isLoading && <div>Loading conversation...</div>}
{messages.map((message) => (
<div key={message.id}>{message.content}</div>
))}
</div>
);
}import { ConversationList } from '@/components/dify/ConversationList';
export function ChatPage() {
const [currentConversationId, setCurrentConversationId] = useState<string>();
return (
<div className="grid gap-8 lg:grid-cols-4">
<div className="lg:col-span-1">
<ConversationList
apiKey="app-demo-key"
userId={user.uid}
currentConversationId={currentConversationId}
onConversationSelect={setCurrentConversationId}
onCreateNew={() => setCurrentConversationId(undefined)}
/>
</div>
<div className="lg:col-span-3">
<DifyChat
apiKey="app-demo-key"
conversationId={currentConversationId}
// ... other props
/>
</div>
</div>
);
}- Smart Caching: 5-minute cache with 1-minute stale time
- Background Refetching: Keep data fresh without user interaction
- Prefetching: Preload messages on conversation hover
- Memory Management: Automatic garbage collection of unused data
- Bundle Size: React Query adds only ~13KB gzipped
// Add to your Firebase functions
export const syncConversationToFirestore = async (conversationId: string, messages: unknown[]) => {
await db.collection('conversations').doc(conversationId).set({
messages,
lastUpdated: new Date(),
userId: auth.currentUser?.uid,
});
};
// Call after successful message send
const result = await sendDifyMessage(userId, apiKey, request);
await syncConversationToFirestore(result.data.conversation_id, messages);// Add to useConversationMessages hook
useEffect(() => {
if (data) {
localStorage.setItem(`conversation-${conversationId}`, JSON.stringify(data));
}
}, [data, conversationId]);// Add WebSocket or Server-Sent Events
const useRealtimeMessages = (conversationId: string) => {
useEffect(() => {
const ws = new WebSocket(`/ws/conversations/${conversationId}`);
ws.onmessage = (event) => {
const newMessage = JSON.parse(event.data);
addMessageOptimistically(newMessage);
};
return () => ws.close();
}, [conversationId]);
};React Query DevTools are automatically enabled in development:
// Automatically included in development builds
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';Do's:
- β Use optimistic updates for better UX
- β Implement proper error boundaries
- β Add loading states for all async operations
- β Use TypeScript for type safety
- β Test with React Query DevTools
Don'ts:
- β Don't bypass the cache for real-time updates
- β Don't store sensitive data in client-side cache
- β Don't forget to handle offline scenarios
- β Don't over-fetch data (use pagination)
src/
βββ app/ # Next.js App Router pages
β βββ chat/ # Chat demo page with conversation management
β βββ conversations/ # Dedicated conversation management page
β βββ dashboard/ # User dashboard
β βββ login/ # Authentication page
β βββ test-credits/ # Credit testing utilities
βββ components/
β βββ auth/ # Authentication components
β βββ credits/ # Credit management UI
β βββ dify/ # Dify integration components
β β βββ DifyChat.tsx # Main chat interface
β β βββ ConversationList.tsx # Conversation management
β β βββ MessageFeedback.tsx # Message feedback system
β β βββ SuggestedQuestions.tsx # Suggested questions
β βββ providers/ # React context providers
β β βββ QueryProvider.tsx # React Query provider
β βββ ui/ # shadcn/ui components
βββ lib/
β βββ actions/ # Server actions
β βββ hooks/ # Custom React hooks
β β βββ useCredits.ts # Credit management
β β βββ useConversationMessages.ts # Conversation caching
β βββ services/ # Dify API services
β β βββ dify/ # Modular Dify service architecture
β βββ utils/ # Utility functions
β βββ config/ # Configuration constants
βββ types/ # TypeScript type definitions
Configure credit costs and limits in src/lib/config/constants.ts:
export const CREDIT_CONFIG = {
TOKENS_PER_CREDIT: 1000, // 1 credit = 1000 tokens
FREE_TIER_CREDITS: 100, // Free credits per month
MIN_CREDITS_WARNING: 10, // Show warning below this
CREDIT_PURCHASE_AMOUNTS: [10, 50, 100, 500],
} as const;For multiple Dify apps, create an app configuration:
const DIFY_APPS = {
chat: {
name: 'Chat Assistant',
apiKey: process.env.DIFY_CHAT_API_KEY!,
},
summarizer: {
name: 'Document Summarizer',
apiKey: process.env.DIFY_SUMMARIZER_API_KEY!,
},
};This project follows focused, self-descriptive testing principles that prioritize quality over quantity. Our tests focus on essential business logic and real-world scenarios rather than exhaustive coverage.
Core Principles:
- Self-Descriptive: Test names clearly describe what's being tested and expected outcomes
- Essential Focus: Test business-critical logic first (credit system, authentication, payment flows)
- Not Exhaustive: Skip trivial functions, third-party libraries, and implementation details
- Real-World Scenarios: Test user journeys and integration points where things can break
- Maintainable: Simple, focused tests that are easy to understand and modify
What We Test:
- β Business logic (credit calculations, user authentication flows)
- β Error handling and failure scenarios
- β Integration points between different parts of the system
- β User journeys and complete workflows
- β Edge cases and boundary conditions
What We Don't Test:
- β Third-party libraries (Firebase SDK, Dify API)
- β Simple utilities and data transformations
- β Configuration and environment variables
- β Styling and visual appearance
- β Trivial getters/setters without business logic
npm test # Run all tests
npm test:watch # Run tests in watch mode
npm test:coverage # Run tests with coverage report
npm test:ui # Run tests with UI interface
npm test:run # Run tests once (CI mode)
npm test:ci # Run tests with coverage and verbose output
npm test:firebase # Run tests with Firebase emulators
npm test:dify # Test Dify API connectionOur test suite covers:
- β Business Logic: Credit calculations, user authentication, rate limiting
- β Server Actions: Credit management, user initialization, Dify API integration
- β Utility Functions: Credit utilities, input validation, rate limiting
- β Dify Services: Chat service, conversation management, API integration
- β React Hooks: useCredits, useChatMessages, useDify integration
- β Error Handling: Database errors, API failures, network issues
- β Edge Cases: Insufficient credits, rate limit exceeded, invalid inputs
src/
βββ __tests__/
β βββ setup.ts # Test environment setup
β βββ mocks/ # Mock implementations
β β βββ handlers.ts # MSW API handlers
β β βββ firebase-admin.ts # Firebase Admin mocks
β β βββ firebase-client.ts# Firebase Client mocks
β βββ fixtures/ # Test data fixtures
β β βββ users.ts # User test data
β β βββ dify-responses.ts # Dify API response mocks
β βββ utils/ # Test utilities
β βββ render.tsx # Custom render function
β βββ auth.ts # Auth test helpers
βββ lib/
β βββ actions/__tests__/ # Server action tests
β βββ hooks/__tests__/ # React hook tests
β βββ services/dify/__tests__/ # Dify service tests
β βββ utils/__tests__/ # Utility function tests
Visit /test-credits to test credit deduction and management.
Visit /chat to test the Dify chat interface.
Visit /sentry-test to test error tracking and logging.
- Enable Analytics in your Firebase project console
- Copy the Measurement ID from Analytics settings
- Add
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-XXXXXXXXXXto.env.local
This boilerplate supports internationalization using next-intl. Currently configured for English only, but you can easily add more languages:
-
Add locale to config:
// src/i18n/config.ts export const locales = ['en', 'es'] as const;
-
Create translation file:
# Create src/i18n/messages/es.json { "common": { "loading": "Cargando...", "error": "OcurriΓ³ un error" }, "chat": { "title": "Asistente de Chat IA", "placeholder": "Escribe tu mensaje..." } }
-
Update middleware:
// middleware.ts export const config = { matcher: ['/', '/(en|es)/:path*'], };
-
Add language switcher component (optional)
The system automatically passes user language context to Dify AI for smarter responses:
// Language context is automatically included
const context = {
language: { code: 'es', locale: 'es-ES' },
timestamp: '2024-12-19T10:30:00Z',
timezone: 'Europe/Madrid',
};This helps the AI provide responses in the user's preferred language and cultural context.
- Push your code to GitHub
- Connect repository to Vercel
- Add environment variables in Vercel dashboard
- Deploy
This Next.js app can be deployed to any platform supporting Node.js:
- Railway
- Render
- Heroku
- DigitalOcean App Platform
- AWS Amplify
- Monitor authentication metrics
- View Firestore usage and costs
- Check security rule violations
This project includes production-ready Sentry integration with minimal but essential logging:
- Smart Error Filtering: Automatically filters non-critical errors (network timeouts, browser quirks)
- Privacy-First: Masks sensitive data, excludes cookies and IP addresses
- Performance Monitoring: Tracks slow operations and API performance
- Production Optimized: Lower sampling rates to reduce noise (10% traces, 1% sessions)
- Create a free Sentry account and project
- Copy your DSN and add to
.env.local:NEXT_PUBLIC_SENTRY_DSN=https://your_key@o0.ingest.sentry.io/0 SENTRY_DSN=https://your_key@o0.ingest.sentry.io/0
- Test with
/sentry-testpage
import { logError, logMessage, LogLevel } from '@/lib/sentry';
// Log critical errors
try {
await processPayment();
} catch (error) {
logError(error, { userId, amount, paymentMethod });
}
// Log important events
logMessage('User upgraded to premium', LogLevel.INFO);- β API errors (5xx responses)
- β Authentication failures
- β Payment processing errors
- β Slow operations (>3s)
- β Unhandled exceptions
- β Network timeouts (filtered)
- β Browser extension errors (filtered)
- β Expected auth errors (filtered)
- Credit usage patterns in Firestore
- Real-time error alerts and performance monitoring
- User activity via Firebase Analytics
This project includes privacy-first Google Analytics using Firebase Analytics with minimal data collection:
- Production Only: Analytics only tracks in production environment
- Essential Events: Tracks only business-critical events (auth, chat, credits)
- Privacy Focused: No personal data collection or tracking
- Client-Side Only: Simple Firebase Analytics integration
- β Page Views: User navigation patterns
- β Authentication: Login/logout events
- β Chat Usage: Message sending and conversation starts
- β Credit Usage: Purchase and deduction events
- β External Links: Outbound link clicks
- Enable Analytics in your Firebase Console
- Copy Measurement ID from Analytics β Data Streams
- Add to Environment:
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-XXXXXXXXXX - Deploy: Analytics will automatically start tracking in production
import { trackAuth, trackChat, trackCredits } from '@/lib/analytics';
// Track user authentication
trackAuth('login');
// Track chat interactions
trackChat('message_sent', messageLength);
// Track credit events
trackCredits('purchase', amount);- GDPR Compliant: No personal data collection
- Development Safe: No tracking in development environment
- Error Handling: Graceful failures without breaking user experience
This project uses HTTP-only cookie authentication for secure Firebase integration. No manual token handling required!
- User signs in β Firebase client gets ID token
- Token sent to server β
/api/auth/set-tokensets HTTP-only cookie - Automatic authentication β Cookie sent with every request
- User signs out β Cookie cleared via
/api/auth/clear-token
- β HTTP-only cookies - XSS protection
- β HTTPS only - Secure in production
- β SameSite=strict - CSRF protection
- β Proper JWT verification - Firebase Admin SDK
- β 7-day expiry - Reasonable session length
src/lib/config/auth-config.ts- Simple constantssrc/app/api/auth/set-token/route.ts- Set cookiesrc/app/api/auth/clear-token/route.ts- Clear cookiesrc/lib/utils/auth-cookie.ts- Cookie helperssrc/lib/auth/middleware-auth.ts- JWT verification
- Authentication: HTTP-only cookies with proper JWT verification
- API Keys: Never expose Dify API keys to client-side code
- Credit Limits: Implement proper credit limits to prevent abuse
- Rate Limiting: Consider adding rate limiting for API calls
- User Validation: Always validate user authentication server-side
- Firestore Rules: Keep security rules restrictive and test thoroughly
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in this repository
- Email: support@yourdomain.com
- Check the Dify Documentation
- Review Firebase Documentation
- Dify API Documentation - Complete API reference and integration guides
- Demo Applications - Example apps showcasing Dify capabilities