DEV Community

Apollo
Apollo

Posted on

How to integrate DeepSeek R1 into your React app

Integrating DeepSeek R1 into Your React Application: A Technical Deep Dive

DeepSeek R1 represents a significant leap in AI-powered search capabilities, offering developers powerful semantic search and retrieval functionalities. This tutorial will guide you through integrating DeepSeek R1 into a React application with proper TypeScript typing, error handling, and performance optimizations.

Prerequisites

Before beginning, ensure you have:

  • Node.js v18+ installed
  • A React project (we'll use Next.js 14 for this example)
  • DeepSeek R1 API credentials
  • Basic understanding of React hooks and async/await patterns

Step 1: Setting Up the Environment

First, install the required dependencies:

npm install @deepseek/sdk axios react-query
Enter fullscreen mode Exit fullscreen mode

Create a .env.local file for your environment variables:

NEXT_PUBLIC_DEEPSEEK_API_KEY=your_api_key_here
NEXT_PUBLIC_DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating the DeepSeek Service Layer

We'll implement a clean service layer to abstract the API interactions:

// lib/deepseek.ts
import axios from 'axios';

interface DeepSeekConfig {
  apiKey: string;
  baseUrl?: string;
}

interface SearchParams {
  query: string;
  documents: string[];
  top_k?: number;
  threshold?: number;
}

interface SearchResult {
  document: string;
  score: number;
  metadata?: Record<string, unknown>;
}

export class DeepSeekService {
  private readonly client: ReturnType<typeof axios.create>;

  constructor(config: DeepSeekConfig) {
    this.client = axios.create({
      baseURL: config.baseUrl || process.env.NEXT_PUBLIC_DEEPSEEK_BASE_URL,
      headers: {
        'Authorization': `Bearer ${config.apiKey}`,
        'Content-Type': 'application/json',
      },
      timeout: 10000,
    });
  }

  async search(params: SearchParams): Promise<SearchResult[]> {
    try {
      const response = await this.client.post('/search', {
        query: params.query,
        documents: params.documents,
        top_k: params.top_k || 5,
        threshold: params.threshold || 0.5,
      });

      return response.data.results.map((result: any) => ({
        document: result.document,
        score: result.score,
        metadata: result.metadata || {},
      }));
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(
          `DeepSeek API error: ${error.response?.data?.message || error.message}`
        );
      }
      throw error;
    }
  }

  async batchSearch(queries: SearchParams[]): Promise<SearchResult[][]> {
    const batchResponse = await Promise.all(
      queries.map(query => this.search(query))
    );
    return batchResponse;
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Creating React Hooks for DeepSeek

We'll use React Query for efficient data fetching and caching:

// hooks/useDeepSeek.ts
import { useMutation, useQueryClient } from 'react-query';
import { DeepSeekService } from '../lib/deepseek';

const deepSeekService = new DeepSeekService({
  apiKey: process.env.NEXT_PUBLIC_DEEPSEEK_API_KEY!,
});

export const useDeepSeekSearch = () => {
  const queryClient = useQueryClient();

  return useMutation(
    async (params: {
      query: string;
      documents: string[];
      top_k?: number;
      threshold?: number;
    }) => {
      return await deepSeekService.search(params);
    },
    {
      onSuccess: (data, variables) => {
        queryClient.setQueryData(
          ['deepseek', variables.query],
          data
        );
      },
    }
  );
};

export const useDeepSeekBatchSearch = () => {
  return useMutation(
    (queries: {
      query: string;
      documents: string[];
      top_k?: number;
      threshold?: number;
    }[]) => {
      return deepSeekService.batchSearch(queries);
    }
  );
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Implementing the Search Component

Here's a complete search component with loading states and error handling:

// components/DeepSeekSearch.tsx
import React, { useState } from 'react';
import { useDeepSeekSearch } from '../hooks/useDeepSeek';

interface DocumentInput {
  id: string;
  content: string;
}

export const DeepSeekSearch: React.FC = () => {
  const [query, setQuery] = useState('');
  const [documents, setDocuments] = useState<DocumentInput[]>([
    { id: '1', content: '' },
  ]);
  const [threshold, setThreshold] = useState(0.5);
  const [topK, setTopK] = useState(5);

  const {
    mutate: search,
    data: results,
    isLoading,
    error,
  } = useDeepSeekSearch();

  const handleSearch = () => {
    search({
      query,
      documents: documents.map(d => d.content),
      threshold,
      top_k: topK,
    });
  };

  const addDocument = () => {
    setDocuments([...documents, { id: Date.now().toString(), content: '' }]);
  };

  const updateDocument = (id: string, content: string) => {
    setDocuments(
      documents.map(doc => (doc.id === id ? { ...doc, content } : doc))
    );
  };

  return (
    <div className="deepseek-container">
      <div className="search-params">
        <div>
          <label>Search Query:</label>
          <input
            type="text"
            value={query}
            onChange={e => setQuery(e.target.value)}
          />
        </div>

        <div>
          <label>Threshold:</label>
          <input
            type="number"
            min="0"
            max="1"
            step="0.1"
            value={threshold}
            onChange={e => setThreshold(Number(e.target.value))}
          />
        </div>

        <div>
          <label>Top K Results:</label>
          <input
            type="number"
            min="1"
            max="10"
            value={topK}
            onChange={e => setTopK(Number(e.target.value))}
          />
        </div>

        <button onClick={handleSearch} disabled={isLoading}>
          {isLoading ? 'Searching...' : 'Search'}
        </button>
      </div>

      <div className="documents-section">
        <h3>Documents to Search</h3>
        {documents.map(doc => (
          <textarea
            key={doc.id}
            value={doc.content}
            onChange={e => updateDocument(doc.id, e.target.value)}
            placeholder="Enter document content..."
          />
        ))}
        <button onClick={addDocument}>Add Document</button>
      </div>

      {error && (
        <div className="error">
          Error: {error instanceof Error ? error.message : 'Unknown error'}
        </div>
      )}

      {results && (
        <div className="results">
          <h3>Search Results</h3>
          <ul>
            {results.map((result, index) => (
              <li key={index}>
                <div className="result-score">Score: {result.score.toFixed(3)}</div>
                <div className="result-content">
                  {result.document.substring(0, 200)}...
                </div>
                {result.metadata && (
                  <div className="result-meta">
                    {JSON.stringify(result.metadata)}
                  </div>
                )}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Step 5: Performance Optimizations

To enhance performance, implement the following techniques:

  1. Debounce Search Input:
import { useDebounce } from 'use-debounce';

// Inside your component
const [query] = useDebounce(rawQuery, 500);

useEffect(() => {
  if (query.trim()) {
    handleSearch();
  }
}, [query]);
Enter fullscreen mode Exit fullscreen mode
  1. Document Chunking: For large documents, split them into chunks before sending to DeepSeek:
function chunkDocument(content: string, chunkSize = 1000): string[] {
  const chunks: string[] = [];
  for (let i = 0; i < content.length; i += chunkSize) {
    chunks.push(content.substring(i, i + chunkSize));
  }
  return chunks;
}
Enter fullscreen mode Exit fullscreen mode
  1. Cache Optimization: Configure React Query's cache behavior:
// In your hooks
{
  staleTime: 1000 * 60 * 5, // 5 minutes
  cacheTime: 1000 * 60 * 30, // 30 minutes
}
Enter fullscreen mode Exit fullscreen mode

Advanced Integration: Real-time Search

For real-time search applications, consider using WebSockets:

// lib/deepseek-realtime.ts
export class DeepSeekRealtimeService {
  private socket: WebSocket | null = null;
  private listeners: ((results: SearchResult[]) => void)[] = [];

  constructor(private apiKey: string) {}

  connect() {
    this.socket = new WebSocket(`wss://api.deepseek.com/realtime?api_key=${this.apiKey}`);

    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.listeners.forEach(listener => listener(data.results));
    };
  }

  subscribe(callback: (results: SearchResult[]) => void) {
    this.listeners.push(callback);
    return () => {
      this.listeners = this.listeners.filter(l => l !== callback);
    };
  }

  sendSearch(query: string, documents: string[]) {
    this.socket?.send(JSON.stringify({
      type: 'search',
      query,
      documents,
    }));
  }
}
Enter fullscreen mode Exit fullscreen mode

Error Handling and Monitoring

Implement comprehensive error tracking:

// lib/errorTracking.ts
export function trackDeepSeekError(error: unknown, context: Record<string, unknown> = {}) {
  const errorData = {
    timestamp: new Date().toISOString(),
    error: error instanceof Error ? {
      name: error.name,
      message: error.message,
      stack: error.stack,
    } : String(error),
    context,
  };

  // Send to your error tracking service (Sentry, etc.)
  console.error('DeepSeek Error:', errorData);

  // Or send to your backend
  fetch('/api/error-log', {
    method: 'POST',
    body: JSON.stringify(errorData),
  }).catch(e => console.error('Failed to log error:', e));
}

// Usage in your hooks
try {
  // ... API calls
} catch (error) {
  trackDeepSeekError(error, { query, documentCount: documents.length });
  throw error;
}
Enter fullscreen mode Exit fullscreen mode

Testing Your Integration

Create comprehensive unit tests using Jest:

// __tests__/deepseek.test.ts
import { DeepSeekService } from '../lib/deepseek';
import axios from 'axios';

jest.mock('axios');

describe('DeepSeekService', () => {
  const mockApiKey = 'test-api-key';
  let service: DeepSeekService;

  beforeEach(() => {
    service = new DeepSeekService({ apiKey: mockApiKey });
    (axios.create as jest.Mock).mockReturnValue({
      post: jest.fn(),
    });
  });

  it('should initialize with correct headers', () => {
    expect(axios.create).toHaveBeenCalledWith({
      headers: {
        Authorization: `Bearer ${mockApiKey}`,
        'Content-Type': 'application/json',
      },
      timeout: 10000,
    });
  });

  describe('search', () => {
    it('should make a POST request with correct parameters', async () => {
      const mockResponse = {
        data: {
          results: [
            { document: 'test doc', score: 0.9 },
          ],
        },
      };
      (service as any).client.post.mockResolvedValue(mockResponse);

      const results = await service.search({
        query: 'test query',
        documents: ['doc1', 'doc2'],
        top_k: 3,
        threshold: 0.7,
      });

      expect((service as any).client.post).toHaveBeenCalledWith('/search', {
        query: 'test query',
        documents: ['doc1', 'doc2'],
        top_k: 3,
        threshold: 0.7,
      });
      expect(results).toEqual([
        { document: 'test doc', score: 0.9, metadata: {} },
      ]);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

This comprehensive integration of DeepSeek R1 into a React application demonstrates proper architectural patterns including:

  1. Clean service layer separation
  2. Type-safe implementations
  3. Efficient data fetching with React Query
  4. Comprehensive error handling
  5. Performance optimizations
  6. Test coverage

By following these patterns, you've created a robust integration that can scale with your application's needs while maintaining reliability and performance. The implementation can be further extended with features like:

  • User authentication context for API calls
  • Advanced document preprocessing
  • Custom ranking algorithms on top of DeepSeek results
  • Analytics integration to track search performance

Remember to monitor your API usage and optimize document preprocessing to maximize the effectiveness of DeepSeek R1's powerful search capabilities.


🚀 Stop Writing Boilerplate Prompts

If you want to skip the setup and code 10x faster with complete AI architecture patterns, grab my Senior React Developer AI Cookbook ($19). It includes Server Action prompt libraries, UI component generation loops, and hydration debugging strategies.

Browse all 10+ developer products at the Apollo AI Store | Or snipe Solana tokens free via @ApolloSniper_Bot.

Top comments (0)