4 - Add Summary
In this step, we’ll implement Summary, which provide a concise, AI-generated summary to user queries. This feature is useful for delivering immediate insights to users without requiring them to sift through multiple search results.
To fetch a summary, use the Raffle API’s /summary
endpoint. This requires a uid
and a query
parameter.
API Call
Here’s the function to fetch a summary from the Raffle API:
export const fetchSummary = async (query) => {
const response = await fetch(
`https://api.raffle.ai/v2/summary?uid=$YOUR_RAFFLE_UID&query=${query}`
);
const data = await response.json();
return data;
};
This function sends a GET
request to the summary
endpoint using the user’s query and returns the summary response.
Key Considerations
- HTML Content: The summary content from the API is returned as HTML, allowing for rich formatting. Use
dangerouslySetInnerHTML
to render it safely in React. - Loading State: Generating a summary might take some time, so it’s crucial to provide a clear loading state to improve user experience.
Integration with React
We’ll integrate the summary feature using React Query’s useMutation
. This will handle the fetching of a summary dynamically when a search is triggered.
import { useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { fetchSummary } from "./api";
export const Search = () => {
const [query, setQuery] = useState("");
const {
data: summary,
isPending: isLoadingSummary,
mutate: handleFetchSummary,
} = useMutation({
mutationKey: ["summary"],
mutationFn: fetchSummary,
});
const handleSearch = () => {
if (query.trim()) {
handleFetchSummary(query.trim());
}
};
return (
<div>
<h1>Search API - React Example</h1>
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Enter your query..."
/>
<button onClick={handleSearch}>Search</button>
</div>
<div>
<h2>Summary</h2>
{isLoadingSummary ? (
<p>Loading...</p>
) : summary ? (
<div>
<div dangerouslySetInnerHTML={{ __html: summary.summary }} />
{summary.references.length > 0 && (
<div>
<h3>References</h3>
<ul>
{summary.references.map((ref) => (
<li key={ref.url}>
<a
href={ref.url}
target="_blank"
rel="noopener noreferrer"
>
{ref.title}
</a>
</li>
))}
</ul>
</div>
)}
</div>
) : (
<p>No summary available for this query.</p>
)}
</div>
</div>
);
};
Explanation
fetchSummary
: Fetches the AI-generated summary for a given query from the API.useMutation
: Dynamically fetches the summary when the user initiates a search.- Debounced Suggestions: Ensures suggestions are fetched efficiently without overloading the API.
- HTML Content Rendering: Uses
dangerouslySetInnerHTML
to display HTML content safely in the UI. - Loading State: Displays a loading indicator while waiting for the summary to be generated.
- References: If references are included in the summary, they are displayed as clickable links for additional context.
This step integrates the summary seamlessly into the UI, providing users with instant insights while maintaining a smooth and efficient experience.
Total Code
At this stage, your full code should look like this:
import { useEffect, useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useDebounce } from "@uidotdev/usehooks";
import { fetchSuggestions, fetchSummary, fetchTopQuestions } from "./api";
export const Search = () => {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500); // Debounce with 500ms delay
const { data: topQuestions = [], isLoading } = useQuery({
queryKey: ["topQuestions"],
queryFn: fetchTopQuestions,
});
const { data: suggestions = [], mutate: handleFetchSuggestions } =
useMutation({
mutationKey: ["suggestions", debouncedQuery],
mutationFn: fetchSuggestions,
});
const {
data: summary,
isPending: isLoadingSummary,
mutate: handleFetchSummary,
} = useMutation({
mutationKey: ["summary"],
mutationFn: fetchSummary,
});
// Trigger suggestions when debouncedQuery is at least 3 characters long
useEffect(() => {
if (debouncedQuery.length >= 3) {
handleFetchSuggestions(debouncedQuery);
}
}, [debouncedQuery, handleFetchSuggestions]);
const handleSearch = () => {
if (query.trim()) {
handleFetchSummary(query.trim());
}
};
return (
<div>
<h1>Search API - React Example</h1>
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Enter your query..."
/>
<button onClick={handleSearch}>Search</button>
</div>
<div>
<h2>Top Questions</h2>
{isLoading ? (
<p>Loading top questions...</p>
) : (
<ul>
{topQuestions.map(({ question }) => (
<li key={question} onClick={() => setQuery(question)}>
{question}
</li>
))}
</ul>
)}
</div>
<div>
<h2>Suggestions</h2>
<ul>
{suggestions.map(({ suggestion }, index) => (
<li
key={index}
onClick={() => {
setQuery(suggestion);
handleSearch();
}}
>
{suggestion}
</li>
))}
</ul>
</div>
<div>
<h2>Summary</h2>
{isLoadingSummary ? (
<p>Loading...</p>
) : summary ? (
<div>
<div dangerouslySetInnerHTML={{ __html: summary.summary }} />
{summary.references.length > 0 && (
<div>
<h3>References</h3>
<ul>
{summary.references.map((ref) => (
<li key={ref.url}>
<a
href={ref.url}
target="_blank"
rel="noopener noreferrer"
>
{ref.title}
</a>
</li>
))}
</ul>
</div>
)}
</div>
) : (
<p>No summary available for this query.</p>
)}
</div>
<div>
<h2>Search Results</h2>
<p>Search results will appear here...</p>
</div>
</div>
);
};