API Integration Guide
Learn how to integrate Infactory APIs into your applications
API Integration Guide
This guide provides comprehensive instructions for integrating Infactory’s APIs into your applications, enabling you to add intelligent data query capabilities with minimal effort.
API Integration Overview
When integrating with Infactory, your application will:
- Send questions or direct queries to Infactory’s API endpoints
- Receive structured data responses
- Present the information to your users or use it for further processing
Key Integration Decisions
Before diving into code, consider these important decisions:
Unified vs Direct Endpoints
Choose between the flexibility of the unified endpoint or the precision of direct endpoints
Authentication Method
Decide how to securely handle API keys in your application architecture
Error Handling Strategy
Plan how your application will respond to different API errors
Response Processing
Determine how to transform and display the structured data responses
Authentication
All Infactory API requests require authentication using API keys.
Securing Your API Keys
Never expose your API keys in client-side code. This could allow unauthorized access to your Infactory resources.
For web applications, use these approaches to keep your keys secure:
// Client code sends request to your backend
async function askQuestion(question) {
const response = await fetch('/api/data-query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question })
});
return await response.json();
}
// Your backend server proxies the request to Infactory with the API key
// Express.js example
app.post('/api/data-query', async (req, res) => {
try {
const { question } = req.body;
const infactoryResponse = await fetch('https://api.infactory.ai/v1/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.INFACTORY_API_KEY}`
},
body: JSON.stringify({
query: question,
project_id: process.env.INFACTORY_PROJECT_ID
})
});
const data = await infactoryResponse.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: 'Error querying Infactory' });
}
});
// Client code sends request to your backend
async function askQuestion(question) {
const response = await fetch('/api/data-query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question })
});
return await response.json();
}
// Your backend server proxies the request to Infactory with the API key
// Express.js example
app.post('/api/data-query', async (req, res) => {
try {
const { question } = req.body;
const infactoryResponse = await fetch('https://api.infactory.ai/v1/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.INFACTORY_API_KEY}`
},
body: JSON.stringify({
query: question,
project_id: process.env.INFACTORY_PROJECT_ID
})
});
const data = await infactoryResponse.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: 'Error querying Infactory' });
}
});
// AWS Lambda example
exports.handler = async (event) => {
try {
const { question } = JSON.parse(event.body);
const response = await fetch('https://api.infactory.ai/v1/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.INFACTORY_API_KEY}`
},
body: JSON.stringify({
query: question,
project_id: process.env.INFACTORY_PROJECT_ID
})
});
const data = await response.json();
return {
statusCode: 200,
body: JSON.stringify(data)
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: 'Error querying Infactory' })
};
}
};
// Next.js API route example
export default async function handler(req, res) {
try {
const { question } = req.body;
const response = await fetch('https://api.infactory.ai/v1/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.INFACTORY_API_KEY}`
},
body: JSON.stringify({
query: question,
project_id: process.env.INFACTORY_PROJECT_ID
})
});
const data = await response.json();
res.status(200).json(data);
} catch (error) {
res.status(500).json({ error: 'Error querying Infactory' });
}
}
Integration Patterns
Chatbot or Conversational Interface
import { useState } from 'react';
function ChatInterface() {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
// Don't submit empty messages
if (!inputValue.trim()) return;
// Add user message to chat
const userMessage = { type: 'user', text: inputValue };
setMessages(prev => [...prev, userMessage]);
// Clear input and show loading state
setInputValue('');
setIsLoading(true);
try {
// Send question to your backend proxy
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: userMessage.text })
});
const data = await response.json();
// Add bot response to chat
setMessages(prev => [
...prev,
{
type: 'bot',
text: 'Here\'s what I found:',
data: data.data
}
]);
} catch (error) {
// Handle errors
setMessages(prev => [
...prev,
{
type: 'bot',
text: 'Sorry, I encountered an error. Please try again.'
}
]);
} finally {
setIsLoading(false);
}
}
return (
<div className="chat-container">
<div className="chat-messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.type}`}>
<div className="message-text">{message.text}</div>
{message.data && (
<div className="message-data">
<pre>{JSON.stringify(message.data, null, 2)}</pre>
</div>
)}
</div>
))}
{isLoading && (
<div className="message bot loading">
<div className="loading-indicator">...</div>
</div>
)}
</div>
<form onSubmit={handleSubmit} className="chat-input-form">
<input
type="text"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Ask a question about your data..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}
import { useState } from 'react';
function ChatInterface() {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
// Don't submit empty messages
if (!inputValue.trim()) return;
// Add user message to chat
const userMessage = { type: 'user', text: inputValue };
setMessages(prev => [...prev, userMessage]);
// Clear input and show loading state
setInputValue('');
setIsLoading(true);
try {
// Send question to your backend proxy
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: userMessage.text })
});
const data = await response.json();
// Add bot response to chat
setMessages(prev => [
...prev,
{
type: 'bot',
text: 'Here\'s what I found:',
data: data.data
}
]);
} catch (error) {
// Handle errors
setMessages(prev => [
...prev,
{
type: 'bot',
text: 'Sorry, I encountered an error. Please try again.'
}
]);
} finally {
setIsLoading(false);
}
}
return (
<div className="chat-container">
<div className="chat-messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.type}`}>
<div className="message-text">{message.text}</div>
{message.data && (
<div className="message-data">
<pre>{JSON.stringify(message.data, null, 2)}</pre>
</div>
)}
</div>
))}
{isLoading && (
<div className="message bot loading">
<div className="loading-indicator">...</div>
</div>
)}
</div>
<form onSubmit={handleSubmit} className="chat-input-form">
<input
type="text"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Ask a question about your data..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}
<template>
<div class="chat-container">
<div class="chat-messages">
<div
v-for="(message, index) in messages"
:key="index"
:class="['message', message.type]"
>
<div class="message-text">{{ message.text }}</div>
<div v-if="message.data" class="message-data">
<pre>{{ JSON.stringify(message.data, null, 2) }}</pre>
</div>
</div>
<div v-if="isLoading" class="message bot loading">
<div class="loading-indicator">...</div>
</div>
</div>
<form @submit.prevent="handleSubmit" class="chat-input-form">
<input
v-model="inputValue"
type="text"
placeholder="Ask a question about your data..."
:disabled="isLoading"
/>
<button type="submit" :disabled="isLoading">
Send
</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
messages: [],
inputValue: '',
isLoading: false
}
},
methods: {
async handleSubmit() {
// Don't submit empty messages
if (!this.inputValue.trim()) return;
// Add user message to chat
const userMessage = { type: 'user', text: this.inputValue };
this.messages.push(userMessage);
// Clear input and show loading state
this.inputValue = '';
this.isLoading = true;
try {
// Send question to your backend proxy
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: userMessage.text })
});
const data = await response.json();
// Add bot response to chat
this.messages.push({
type: 'bot',
text: 'Here\'s what I found:',
data: data.data
});
} catch (error) {
// Handle errors
this.messages.push({
type: 'bot',
text: 'Sorry, I encountered an error. Please try again.'
});
} finally {
this.isLoading = false;
}
}
}
}
</script>
Search Interface
import { useState } from 'react';
function SearchInterface() {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSearch(e) {
e.preventDefault();
if (!query.trim()) return;
setIsLoading(true);
setError(null);
try {
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: query })
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
setResults(data);
} catch (error) {
setError('An error occurred while searching. Please try again.');
setResults(null);
} finally {
setIsLoading(false);
}
}
return (
<div className="search-container">
<form onSubmit={handleSearch} className="search-form">
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search your data..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Searching...' : 'Search'}
</button>
</form>
{error && <div className="error-message">{error}</div>}
{results && (
<div className="search-results">
<h2>Results</h2>
<div className="results-info">
<p>Query used: {results.query_used}</p>
<p>Execution time: {results.execution_time_ms}ms</p>
</div>
<div className="data-results">
{Array.isArray(results.data) ? (
<table>
<thead>
<tr>
{Object.keys(results.data[0] || {}).map(key => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{results.data.map((item, index) => (
<tr key={index}>
{Object.values(item).map((value, i) => (
<td key={i}>{typeof value === 'object' ? JSON.stringify(value) : value}</td>
))}
</tr>
))}
</tbody>
</table>
) : (
<pre>{JSON.stringify(results.data, null, 2)}</pre>
)}
</div>
</div>
)}
</div>
);
}
import { useState } from 'react';
function SearchInterface() {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSearch(e) {
e.preventDefault();
if (!query.trim()) return;
setIsLoading(true);
setError(null);
try {
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: query })
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
setResults(data);
} catch (error) {
setError('An error occurred while searching. Please try again.');
setResults(null);
} finally {
setIsLoading(false);
}
}
return (
<div className="search-container">
<form onSubmit={handleSearch} className="search-form">
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search your data..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Searching...' : 'Search'}
</button>
</form>
{error && <div className="error-message">{error}</div>}
{results && (
<div className="search-results">
<h2>Results</h2>
<div className="results-info">
<p>Query used: {results.query_used}</p>
<p>Execution time: {results.execution_time_ms}ms</p>
</div>
<div className="data-results">
{Array.isArray(results.data) ? (
<table>
<thead>
<tr>
{Object.keys(results.data[0] || {}).map(key => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{results.data.map((item, index) => (
<tr key={index}>
{Object.values(item).map((value, i) => (
<td key={i}>{typeof value === 'object' ? JSON.stringify(value) : value}</td>
))}
</tr>
))}
</tbody>
</table>
) : (
<pre>{JSON.stringify(results.data, null, 2)}</pre>
)}
</div>
</div>
)}
</div>
);
}
<template>
<div class="search-container">
<form @submit.prevent="handleSearch" class="search-form">
<input
v-model="query"
type="text"
placeholder="Search your data..."
:disabled="isLoading"
/>
<button type="submit" :disabled="isLoading">
{{ isLoading ? 'Searching...' : 'Search' }}
</button>
</form>
<div v-if="error" class="error-message">{{ error }}</div>
<div v-if="results" class="search-results">
<h2>Results</h2>
<div class="results-info">
<p>Query used: {{ results.query_used }}</p>
<p>Execution time: {{ results.execution_time_ms }}ms</p>
</div>
<div class="data-results">
<table v-if="Array.isArray(results.data) && results.data.length > 0">
<thead>
<tr>
<th v-for="(_, key) in results.data[0]" :key="key">{{ key }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in results.data" :key="index">
<td v-for="(value, i) in Object.values(item)" :key="i">
{{ typeof value === 'object' ? JSON.stringify(value) : value }}
</td>
</tr>
</tbody>
</table>
<pre v-else>{{ JSON.stringify(results.data, null, 2) }}</pre>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
query: '',
results: null,
isLoading: false,
error: null
}
},
methods: {
async handleSearch() {
if (!this.query.trim()) return;
this.isLoading = true;
this.error = null;
try {
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question: this.query })
});
if (!response.ok) {
throw new Error('Search failed');
}
const data = await response.json();
this.results = data;
} catch (error) {
this.error = 'An error occurred while searching. Please try again.';
this.results = null;
} finally {
this.isLoading = false;
}
}
}
}
</script>
Dashboard Integration
For dashboards, you’ll often want to:
- Predefine the queries to use
- Call direct endpoints instead of the unified endpoint
- Use a charting library to visualize the results
import { useState, useEffect } from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { Bar } from 'react-chartjs-2';
// Register Chart.js components
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
function Dashboard() {
const [salesData, setSalesData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchDashboardData() {
try {
// Fetch sales by region data
const response = await fetch('/api/dashboard/sales-by-region');
if (!response.ok) {
throw new Error('Failed to fetch dashboard data');
}
const data = await response.json();
setSalesData(data);
} catch (error) {
setError('Failed to load dashboard data. Please try again later.');
} finally {
setIsLoading(false);
}
}
fetchDashboardData();
}, []);
// Format data for Chart.js
const chartData = salesData ? {
labels: salesData.data.map(item => item.region),
datasets: [
{
label: 'Sales by Region',
data: salesData.data.map(item => item.sales),
backgroundColor: 'rgba(53, 162, 235, 0.5)',
},
],
} : null;
const chartOptions = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Sales by Region',
},
},
};
return (
<div className="dashboard">
<h1>Sales Dashboard</h1>
{isLoading && <p>Loading dashboard data...</p>}
{error && <p className="error">{error}</p>}
{chartData && (
<div className="chart-container">
<Bar options={chartOptions} data={chartData} />
<p className="data-timestamp">
Last updated: {new Date().toLocaleString()}
</p>
</div>
)}
</div>
);
}
import { useState, useEffect } from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { Bar } from 'react-chartjs-2';
// Register Chart.js components
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
function Dashboard() {
const [salesData, setSalesData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchDashboardData() {
try {
// Fetch sales by region data
const response = await fetch('/api/dashboard/sales-by-region');
if (!response.ok) {
throw new Error('Failed to fetch dashboard data');
}
const data = await response.json();
setSalesData(data);
} catch (error) {
setError('Failed to load dashboard data. Please try again later.');
} finally {
setIsLoading(false);
}
}
fetchDashboardData();
}, []);
// Format data for Chart.js
const chartData = salesData ? {
labels: salesData.data.map(item => item.region),
datasets: [
{
label: 'Sales by Region',
data: salesData.data.map(item => item.sales),
backgroundColor: 'rgba(53, 162, 235, 0.5)',
},
],
} : null;
const chartOptions = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Sales by Region',
},
},
};
return (
<div className="dashboard">
<h1>Sales Dashboard</h1>
{isLoading && <p>Loading dashboard data...</p>}
{error && <p className="error">{error}</p>}
{chartData && (
<div className="chart-container">
<Bar options={chartOptions} data={chartData} />
<p className="data-timestamp">
Last updated: {new Date().toLocaleString()}
</p>
</div>
)}
</div>
);
}
<template>
<div class="dashboard">
<h1>Sales Dashboard</h1>
<p v-if="isLoading">Loading dashboard data...</p>
<p v-if="error" class="error">{{ error }}</p>
<div v-if="chartData" class="chart-container">
<Bar :options="chartOptions" :data="chartData" />
<p class="data-timestamp">
Last updated: {{ new Date().toLocaleString() }}
</p>
</div>
</div>
</template>
<script>
import { defineComponent, ref, onMounted, computed } from 'vue';
import { Bar } from 'vue-chartjs';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
// Register Chart.js components
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
export default defineComponent({
components: {
Bar
},
setup() {
const salesData = ref(null);
const isLoading = ref(true);
const error = ref(null);
const chartData = computed(() => {
if (!salesData.value) return null;
return {
labels: salesData.value.data.map(item => item.region),
datasets: [
{
label: 'Sales by Region',
data: salesData.value.data.map(item => item.sales),
backgroundColor: 'rgba(53, 162, 235, 0.5)',
},
],
};
});
const chartOptions = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Sales by Region',
},
},
};
onMounted(async () => {
try {
// Fetch sales by region data
const response = await fetch('/api/dashboard/sales-by-region');
if (!response.ok) {
throw new Error('Failed to fetch dashboard data');
}
const data = await response.json();
salesData.value = data;
} catch (e) {
error.value = 'Failed to load dashboard data. Please try again later.';
} finally {
isLoading.value = false;
}
});
return {
chartData,
chartOptions,
isLoading,
error
};
}
});
</script>
Backend Implementation Examples
Node.js (Express)
const express = require('express');
const cors = require('cors');
const axios = require('axios');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.json());
// Environment variables
const INFACTORY_API_KEY = process.env.INFACTORY_API_KEY;
const INFACTORY_PROJECT_ID = process.env.INFACTORY_PROJECT_ID;
// Unified endpoint proxy
app.post('/api/ask', async (req, res) => {
try {
const { question } = req.body;
const response = await axios.post(
'https://api.infactory.ai/v1/query',
{
query: question,
project_id: INFACTORY_PROJECT_ID
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${INFACTORY_API_KEY}`
}
}
);
res.json(response.data);
} catch (error) {
console.error('Error querying Infactory:', error.response?.data || error.message);
res.status(500).json({
error: 'Failed to query Infactory',
message: error.response?.data?.message || error.message
});
}
});
// Direct endpoint for dashboard
app.get('/api/dashboard/sales-by-region', async (req, res) => {
try {
const response = await axios.post(
'https://api.infactory.ai/v1/queries/sales_by_region',
{}, // No parameters needed for this example
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${INFACTORY_API_KEY}`
}
}
);
res.json(response.data);
} catch (error) {
console.error('Error fetching dashboard data:', error.response?.data || error.message);
res.status(500).json({
error: 'Failed to fetch dashboard data',
message: error.response?.data?.message || error.message
});
}
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Python (FastAPI)
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import httpx
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
INFACTORY_API_KEY = os.getenv("INFACTORY_API_KEY")
INFACTORY_PROJECT_ID = os.getenv("INFACTORY_PROJECT_ID")
app = FastAPI()
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, specify your frontend domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class Question(BaseModel):
question: str
@app.post("/api/ask")
async def ask_question(question_data: Question):
"""Proxy endpoint for the unified Infactory API"""
async with httpx.AsyncClient() as client:
try:
response = await client.post(
"https://api.infactory.ai/v1/query",
json={
"query": question_data.question,
"project_id": INFACTORY_PROJECT_ID
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {INFACTORY_API_KEY}"
}
)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=e.response.status_code,
detail=e.response.json()
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to query Infactory: {str(e)}"
)
@app.get("/api/dashboard/sales-by-region")
async def get_sales_by_region():
"""Endpoint for dashboard sales by region data"""
async with httpx.AsyncClient() as client:
try:
response = await client.post(
"https://api.infactory.ai/v1/queries/sales_by_region",
json={}, # No parameters needed for this example
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {INFACTORY_API_KEY}"
}
)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=e.response.status_code,
detail=e.response.json()
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to fetch dashboard data: {str(e)}"
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Error Handling
Implement comprehensive error handling to ensure a smooth user experience:
async function queryInfactory(question) {
try {
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question })
});
if (!response.ok) {
const errorData = await response.json();
// Handle specific error types
switch (errorData.error) {
case 'authentication_failed':
console.error('API key authentication failed');
return { error: 'Authentication failed. Please check your API key.' };
case 'invalid_query':
console.error('Invalid query format');
return { error: 'Your question could not be processed. Please try rephrasing it.' };
case 'no_matching_query':
console.error('No matching query found');
return { error: 'I don\'t know how to answer that question yet. Please try something else.' };
case 'rate_limit_exceeded':
console.error('Rate limit exceeded');
return { error: 'Too many requests. Please try again in a moment.' };
default:
console.error('API error:', errorData);
return { error: 'An error occurred. Please try again later.' };
}
}
return await response.json();
} catch (error) {
console.error('Network error:', error);
return { error: 'Could not connect to the server. Please check your network connection.' };
}
}
Testing Your Integration
Before deploying to production, thoroughly test your integration:
Test with simple questions
Start with straightforward questions that you know should work.
Example: “What is the average sales by region?”
Test edge cases
Try questions that might be challenging or at the boundaries of what your queries can handle.
Example: “What’s the correlation between customer age and purchase amount in the northeast region for Q2?”
Test error scenarios
Deliberately trigger errors to ensure your error handling works properly.
Examples:
- Use an invalid API key
- Ask questions your queries can’t answer
- Send malformed requests
Performance testing
Test with concurrent users and measure response times.
Tools to consider:
- Apache JMeter
- Locust
- k6
Advanced Integration Techniques
Caching Responses
Implement caching to improve performance and reduce API calls:
// Simple in-memory cache
const cache = new Map();
const CACHE_TTL = 1000 * 60 * 5; // 5 minutes
async function queryWithCache(question) {
// Generate a cache key from the question
const cacheKey = question.trim().toLowerCase();
// Check if we have a fresh cached response
const cachedItem = cache.get(cacheKey);
if (cachedItem && Date.now() - cachedItem.timestamp < CACHE_TTL) {
console.log('Cache hit for:', cacheKey);
return cachedItem.data;
}
// If not in cache or expired, query the API
const response = await queryInfactory(question);
// Cache the response if successful
if (!response.error) {
cache.set(cacheKey, {
timestamp: Date.now(),
data: response
});
}
return response;
}
Streaming Responses
For queries that might return large datasets, use streaming:
async function streamResults(question, onChunk, onComplete, onError) {
try {
const response = await fetch('/api/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify({ question })
});
if (!response.ok) {
const errorData = await response.json();
onError(errorData);
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) {
if (buffer.trim()) {
try {
const chunk = JSON.parse(buffer);
onChunk(chunk);
} catch (e) {
console.error('Error parsing final chunk:', buffer);
}
}
onComplete();
break;
}
// Decode and buffer the incoming data
buffer += decoder.decode(value, { stream: true });
// Process complete JSON objects
let newlineIndex;
while ((newlineIndex = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, newlineIndex).trim();
buffer = buffer.slice(newlineIndex + 1);
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6);
try {
const chunk = JSON.parse(jsonStr);
onChunk(chunk);
} catch (e) {
console.error('Error parsing chunk:', jsonStr);
}
}
}
}
} catch (error) {
onError({ error: 'Stream error', message: error.message });
}
}
// Example usage
streamResults(
"List all products and their details",
(chunk) => {
console.log('Received chunk:', chunk);
// Update UI with this chunk
},
() => {
console.log('Stream complete');
// Update UI to show completion
},
(error) => {
console.error('Stream error:', error);
// Show error in UI
}
);
Best Practices
Security First
Never expose API keys in client-side code. Always use a backend service to make API calls.
Graceful Degradation
Design your application to handle API outages gracefully, providing fallback experiences.
Rate Limit Handling
Implement exponential backoff strategies for handling rate limits.
Comprehensive Logging
Log API interactions for debugging and monitoring purposes, but be careful not to log sensitive data.
Smart Caching
Implement caching strategies to improve performance and reduce API calls.
Consistent Error Handling
Develop a consistent approach to handling and displaying errors across your application.
Next Steps
Now that you understand how to integrate with Infactory’s APIs, you might want to explore:
- Advanced Queries - Learn how to create more sophisticated queries
- Security Best Practices - Ensure your integration is secure
- Performance Optimization - Optimize your API usage
- API Reference - Detailed API documentation
Was this page helpful?
- API Integration Guide
- API Integration Overview
- Key Integration Decisions
- Authentication
- Securing Your API Keys
- Integration Patterns
- Chatbot or Conversational Interface
- Search Interface
- Dashboard Integration
- Backend Implementation Examples
- Node.js (Express)
- Python (FastAPI)
- Error Handling
- Testing Your Integration
- Advanced Integration Techniques
- Caching Responses
- Streaming Responses
- Best Practices
- Next Steps