TypeScript SDK
Complete guide to using the HOLE Foundation API TypeScript SDK.
Installation
$ npm install @hole-foundation/api
Or with pnpm:
$ pnpm add @hole-foundation/api
Quick Start
1 import { HoleFoundationClient } from '@hole-foundation/api'; 2 3 const client = new HoleFoundationClient({ 4 environment: 'https://api.theholefoundation.org', 5 token: process.env.AUTH0_TOKEN 6 }); 7 8 // Make your first API call 9 const health = await client.health.check(); 10 console.log(`API Status: ${health.status}`);
Client Configuration
Basic Configuration
1 const client = new HoleFoundationClient({ 2 environment: 'https://api.theholefoundation.org', 3 token: 'your-jwt-token' 4 });
Advanced Configuration
1 import { HoleFoundationClient } from '@hole-foundation/api'; 2 3 const client = new HoleFoundationClient({ 4 environment: 'https://api.theholefoundation.org', 5 token: process.env.AUTH0_TOKEN, 6 7 // Custom timeout (default: 60 seconds) 8 timeoutInSeconds: 30, 9 10 // Retry configuration (default: 2) 11 maxRetries: 3, 12 13 // Additional headers 14 headers: { 15 'X-Client-Version': '1.0.0', 16 'X-Request-ID': () => crypto.randomUUID() 17 }, 18 19 // Custom fetch implementation 20 fetch: customFetch, 21 22 // Logging configuration 23 logging: { 24 level: 'debug', 25 logger: console 26 } 27 });
Dynamic Token (Recommended)
For automatic token refresh:
1 import { HoleFoundationClient } from '@hole-foundation/api'; 2 3 class TokenManager { 4 private token: string | null = null; 5 private expiresAt: number = 0; 6 7 async getToken(): Promise<string> { 8 if (this.token && Date.now() < this.expiresAt) { 9 return this.token; 10 } 11 12 const response = await fetch('https://dev-4fszoklachwdh46m.us.auth0.com/oauth/token', { 13 method: 'POST', 14 headers: { 'Content-Type': 'application/json' }, 15 body: JSON.stringify({ 16 grant_type: 'client_credentials', 17 client_id: process.env.AUTH0_M2M_CLIENT_ID, 18 client_secret: process.env.AUTH0_M2M_CLIENT_SECRET, 19 audience: 'https://api.theholefoundation.org' 20 }) 21 }); 22 23 const { access_token, expires_in } = await response.json(); 24 this.token = access_token; 25 this.expiresAt = Date.now() + (expires_in * 1000) - 60000; 26 27 return this.token; 28 } 29 } 30 31 const tokenManager = new TokenManager(); 32 33 const client = new HoleFoundationClient({ 34 environment: 'https://api.theholefoundation.org', 35 token: async () => await tokenManager.getToken() 36 });
API Methods
Health
1 // Check API health 2 const health = await client.health.check(); 3 console.log(health.status); // "healthy" 4 console.log(health.database); // "connected" 5 console.log(health.version); // "2.0.0"
Vector Search
1 // Search legal documents 2 const results = await client.vectorSearch.search({ 3 query: "FOIA exemptions for national security", 4 limit: 10, 5 includeContext: true, 6 filters: { 7 jurisdiction: "federal", 8 documentType: "statute" 9 } 10 }); 11 12 // Access results 13 results.results?.forEach(result => { 14 console.log(result.title); 15 console.log(result.score); 16 console.log(result.excerpt); 17 }); 18 19 // Get specific document 20 const document = await client.vectorSearch.getDocument('doc-123'); 21 console.log(document.content);
Transparency
1 // List jurisdictions 2 const jurisdictions = await client.transparency.listJurisdictions({ 3 limit: 50, 4 type: 'state' // 'federal', 'state', or 'all' 5 }); 6 7 // Search rights of access 8 const rights = await client.transparency.searchRights({ 9 query: "public records", 10 jurisdiction: "california", 11 limit: 20 12 }); 13 14 // Search exemptions 15 const exemptions = await client.transparency.searchExemptions({ 16 query: "privacy", 17 jurisdiction: "federal", 18 category: "personal_information" 19 }); 20 21 // Get statute text 22 const statute = await client.transparency.getStatute('5-USC-552'); 23 console.log(statute.fullText);
FOIA Dashboard
1 // Create FOIA request (requires authentication) 2 const request = await client.foiaDashboard.createRequest({ 3 jurisdiction: 'federal', 4 agency: 'Department of Justice', 5 subject: 'Freedom of Information Request', 6 description: 'Request for records regarding...', 7 requesterName: 'John Doe', 8 requesterEmail: 'john@example.com', 9 deliveryMethod: 'email' 10 }); 11 12 // List your requests 13 const myRequests = await client.foiaDashboard.listRequests({ 14 status: 'pending', 15 limit: 10 16 }); 17 18 // Get request details 19 const requestDetails = await client.foiaDashboard.getRequest(request.id); 20 21 // Update request 22 const updated = await client.foiaDashboard.updateRequest(request.id, { 23 status: 'submitted', 24 notes: 'Request submitted to agency' 25 });
Chat
1 // Non-streaming chat 2 const response = await client.chat.sendMessage({ 3 message: "What is the Freedom of Information Act?", 4 stream: false 5 }); 6 console.log(response.message); 7 8 // Streaming chat 9 const streamResponse = await client.chat.sendMessage({ 10 message: "Explain FOIA exemption (b)(6)", 11 stream: true 12 }); 13 14 // Handle stream (Server-Sent Events) 15 const reader = streamResponse.body?.getReader(); 16 const decoder = new TextDecoder(); 17 18 while (true) { 19 const { done, value } = await reader.read(); 20 if (done) break; 21 22 const chunk = decoder.decode(value); 23 process.stdout.write(chunk); 24 }
Error Handling
1 import { TheholetruthApiError, TheholetruthApiTimeoutError } from '@hole-foundation/api'; 2 3 try { 4 const results = await client.vectorSearch.search({ 5 query: "test" 6 }); 7 } catch (error) { 8 if (error instanceof TheholetruthApiError) { 9 console.error('API Error:', error.statusCode); 10 console.error('Message:', error.message); 11 console.error('Body:', error.body); 12 } else if (error instanceof TheholetruthApiTimeoutError) { 13 console.error('Request timed out'); 14 } else { 15 console.error('Unknown error:', error); 16 } 17 }
TypeScript Types
The SDK is fully typed. Import types as needed:
1 import type { 2 ChatResponse, 3 SearchRequest, 4 SearchResponse, 5 Jurisdiction, 6 RightOfAccess, 7 Exemption, 8 FoiaRequest 9 } from '@hole-foundation/api'; 10 11 // Use types for type safety 12 const searchRequest: SearchRequest = { 13 query: "FOIA", 14 limit: 10, 15 includeContext: true 16 }; 17 18 const handleResults = (response: SearchResponse) => { 19 response.results?.forEach(result => { 20 console.log(result.title); 21 }); 22 };
Framework Integration
Next.js
1 // app/api/search/route.ts 2 import { HoleFoundationClient } from '@hole-foundation/api'; 3 import { NextRequest, NextResponse } from 'next/server'; 4 5 export async function POST(request: NextRequest) { 6 const { query } = await request.json(); 7 8 const client = new HoleFoundationClient({ 9 environment: process.env.HOLE_API_URL!, 10 token: process.env.AUTH0_M2M_TOKEN! 11 }); 12 13 const results = await client.vectorSearch.search({ query }); 14 15 return NextResponse.json(results); 16 }
Express.js
1 // server.ts 2 import express from 'express'; 3 import { HoleFoundationClient } from '@hole-foundation/api'; 4 5 const app = express(); 6 const client = new HoleFoundationClient({ 7 environment: process.env.HOLE_API_URL!, 8 token: process.env.AUTH0_M2M_TOKEN! 9 }); 10 11 app.post('/api/search', async (req, res) => { 12 const { query } = req.body; 13 const results = await client.vectorSearch.search({ query }); 14 res.json(results); 15 }); 16 17 app.listen(3000);
React (Client-side)
1 // hooks/useHoleApi.ts 2 import { HoleFoundationClient } from '@hole-foundation/api'; 3 import { useAuth0 } from '@auth0/auth0-react'; 4 import { useMemo } from 'react'; 5 6 export function useHoleApi() { 7 const { getAccessTokenSilently } = useAuth0(); 8 9 return useMemo( 10 () => new HoleFoundationClient({ 11 environment: 'https://api.theholefoundation.org', 12 token: async () => { 13 return await getAccessTokenSilently({ 14 audience: 'https://api.theholefoundation.org' 15 }); 16 } 17 }), 18 [getAccessTokenSilently] 19 ); 20 } 21 22 // components/Search.tsx 23 import { useHoleApi } from '../hooks/useHoleApi'; 24 25 export function Search() { 26 const client = useHoleApi(); 27 28 const handleSearch = async (query: string) => { 29 const results = await client.vectorSearch.search({ query }); 30 setResults(results.results); 31 }; 32 33 return <SearchUI onSearch={handleSearch} />; 34 }
Environment Variables
Create a .env file:
$ # Auth0 Configuration $ AUTH0_DOMAIN=dev-4fszoklachwdh46m.us.auth0.com $ AUTH0_M2M_CLIENT_ID=your-client-id $ AUTH0_M2M_CLIENT_SECRET=your-client-secret $ AUTH0_AUDIENCE=https://api.theholefoundation.org $ $ # API Configuration $ HOLE_API_URL=https://api.theholefoundation.org
Load with:
1 import 'dotenv/config';
Testing
1 // test/api.test.ts 2 import { HoleFoundationClient } from '@hole-foundation/api'; 3 import { describe, it, expect } from 'vitest'; 4 5 describe('HOLE Foundation API', () => { 6 const client = new HoleFoundationClient({ 7 environment: 'https://api.theholefoundation.org', 8 token: process.env.AUTH0_TOKEN! 9 }); 10 11 it('should check health', async () => { 12 const health = await client.health.check(); 13 expect(health.status).toBe('healthy'); 14 }); 15 16 it('should search documents', async () => { 17 const results = await client.vectorSearch.search({ 18 query: 'FOIA', 19 limit: 5 20 }); 21 expect(results.results).toBeDefined(); 22 expect(results.results!.length).toBeGreaterThan(0); 23 }); 24 25 it('should list jurisdictions', async () => { 26 const jurisdictions = await client.transparency.listJurisdictions(); 27 expect(jurisdictions.length).toBe(52); 28 }); 29 });
Best Practices
1. Singleton Pattern
Create one client instance and reuse it:
1 // lib/api-client.ts 2 import { HoleFoundationClient } from '@hole-foundation/api'; 3 4 export const apiClient = new HoleFoundationClient({ 5 environment: process.env.HOLE_API_URL!, 6 token: process.env.AUTH0_TOKEN! 7 }); 8 9 // Use throughout your app 10 import { apiClient } from './lib/api-client'; 11 const results = await apiClient.vectorSearch.search({ query: 'FOIA' });
2. Error Boundaries
Wrap API calls in try-catch:
1 async function searchDocuments(query: string) { 2 try { 3 return await client.vectorSearch.search({ query }); 4 } catch (error) { 5 if (error instanceof TheholetruthApiError) { 6 // Handle API errors 7 logger.error('API error', { statusCode: error.statusCode }); 8 } 9 throw error; 10 } 11 }
3. Request Deduplication
Use SWR or React Query for caching:
1 import useSWR from 'swr'; 2 3 function useSearch(query: string) { 4 return useSWR( 5 ['search', query], 6 async () => { 7 return await client.vectorSearch.search({ query }); 8 } 9 ); 10 }
Package Information
- Name:
@hole-foundation/api - Version: Auto-generated from OpenAPI
- License: See package.json
- Repository: https://github.com/The-HOLE-Foundation/hole-api
Support
- 📧 Email: api@theholefoundation.org
- 💬 Discord: Join our community
- 🐛 Issues: GitHub Issues
- 📚 Full Docs: API Documentation