# Folo Client SDK

The official JavaScript/TypeScript client for Folo - the modern RSS reader. This SDK makes it easy to build applications that interact with Folo's RSS management, reading history, and AI-powered features.

## Why Use the Folo SDK?

- **Cross-Platform**: Works in browsers, Node.js, React Native, and other JavaScript environments
- **Type-Safe**: Full TypeScript support with auto-completion and error prevention
- **Production Ready**: Used by Folo's official apps with comprehensive error handling

## Installation

```bash
pnpm add @follow-app/client-sdk
```

## Quick Start

```typescript
import { FollowClient } from '@follow-app/client-sdk'

// Connect to Folo
const follow = new FollowClient({
  baseURL: 'https://api.follow.is',
  credentials: 'include', // For web apps
})

// Subscribe to an RSS feed
await follow.subscriptions.create({
  url: 'https://example.com/rss',
  category: 'Technology',
})

// Get your latest unread articles
const articles = await follow.entries.list({
  read: false,
  limit: 20,
})

// Mark articles as read
await follow.entries.batch.read({
  entryIds: articles.data.map((entry) => entry.id),
})
```

## Core Features

### 📰 Feed Management

Subscribe to and manage RSS feeds with ease:

```typescript
// Subscribe to feeds
await follow.subscriptions.create({
  url: 'https://blog.example.com/rss',
  category: 'Tech Blogs',
})

// Get all your subscriptions
const subscriptions = await follow.subscriptions.get()

// Import OPML files
await follow.subscriptions.import({ opmlContent })
```

### 📖 Reading Experience

Track and manage your reading:

```typescript
// Get unread articles
const unread = await follow.entries.list({
  read: false,
  limit: 50,
})

// Mark articles as read
await follow.entries.batch.read({
  entryIds: ['article-1', 'article-2'],
})

// Save articles to inbox
await follow.entries.inbox.add({
  entryId: 'article-123',
  note: 'Read later',
})
```

### 🤖 AI-Powered Features

Enhance your reading with AI:

```typescript
// Get article summaries
const summary = await follow.ai.summary({
  id: 'article-123',
})

// Chat with AI about your articles
const response = await follow.ai.chat({
  messages: [
    { role: 'user', content: 'What are the key trends in this article?' },
  ],
})

// Get personalized recommendations
const recommendations = await follow.ai.recommendations()
```

### 📊 Analytics & Insights

Understand your reading habits:

```typescript
// Get reading statistics
const stats = await follow.reads.stats()

// Track feed performance
const feedAnalytics = await follow.feeds.analytics({
  feedId: 'feed-123',
})
```

## Setup & Configuration

### For Web Applications

```typescript
import { FollowClient } from '@follow-app/client-sdk'

const follow = new FollowClient({
  baseURL: 'https://api.follow.is',
  credentials: 'include', // For cookie-based authentication
})
```

### For Node.js Applications

```typescript
import { FollowClient } from '@follow-app/client-sdk'

const follow = new FollowClient({
  baseURL: 'https://api.follow.is',
  headers: {
    Authorization: 'Bearer your-api-token',
  },
})

// Or set the token later
follow.setAuthToken('your-api-token')
```

### For React Native

```typescript
import { FollowClient } from '@follow-app/client-sdk'

const follow = new FollowClient({
  baseURL: 'https://api.follow.is',
  credentials: 'include',
  headers: {
    'User-Agent': 'YourApp/1.0.0',
  },
})
```

## Error Handling

The SDK provides clear error handling for common scenarios:

```typescript
import { FollowAPIError, FollowAuthError } from '@follow-app/client-sdk'

try {
  const articles = await follow.entries.list()
} catch (error) {
  if (error instanceof FollowAuthError) {
    // User needs to log in
    console.log('Authentication required')
    redirectToLogin()
  } else if (error instanceof FollowAPIError) {
    // Handle API errors (network, server, etc.)
    console.log(`Error: ${error.message}`)
    showErrorMessage(error.message)
  }
}
```

### Specific Error Codes

```typescript
import { ExceptionCodeMap } from '@follow-app/client-sdk'

try {
  await follow.subscriptions.create({ url: 'invalid-url' })
} catch (error) {
  if (error.code === ExceptionCodeMap.feed.NOT_FOUND) {
    console.log('Feed not found at that URL')
  } else if (error.code === ExceptionCodeMap.subscription.ALREADY_EXISTS) {
    console.log('You are already subscribed to this feed')
  }
}
```

## Common Use Cases

### Building a Personal RSS Reader

```typescript
// Set up the client
const follow = new FollowClient({
  baseURL: 'https://api.follow.is',
  credentials: 'include',
})

// Subscribe to feeds
await follow.subscriptions.create({
  url: 'https://blog.openai.com/rss/',
  category: 'AI & Technology',
})

// Get latest articles
const latest = await follow.entries.list({
  read: false,
  limit: 20,
})

// Display articles to user, then mark as read
await follow.entries.batch.read({
  entryIds: latest.data.map((article) => article.id),
})
```

### Content Curation Tool

```typescript
// Import existing OPML subscription list
await follow.subscriptions.import({ opmlContent })

// Get articles from specific categories
const techNews = await follow.entries.list({
  category: 'Technology',
  limit: 100,
  read: false,
})

// Filter by keywords
const aiArticles = techNews.data.filter(
  (article) =>
    article.title.toLowerCase().includes('ai') ||
    article.content.toLowerCase().includes('artificial intelligence'),
)

// Save important articles to inbox
for (const article of aiArticles) {
  await follow.entries.inbox.add({
    entryId: article.id,
    note: 'AI trend research',
  })
}
```

### AI-Enhanced Reading App

```typescript
// Get user's reading recommendations
const recommendations = await follow.ai.chat({
  messages: [
    {
      role: 'user',
      content: 'What are the most important articles I should read today?',
    },
  ],
})

// Get summary of long articles
const longArticles = await follow.entries.list({
  read: false,
  limit: 10,
})

for (const article of longArticles.data) {
  const summary = await follow.ai.summary({
    id: article.id,
  })

  console.log(`${article.title}\nSummary: ${summary.summary}`)
}
```

## License

MIT License - see [LICENSE](./LICENSE) file for details.

## Contributing

We welcome contributions! Please see our [contributing guide](https://github.com/RSSNext/folo/blob/main/CONTRIBUTING.md) for details on:

- Setting up the development environment
- Running tests and linting
- Submitting pull requests
- Code style guidelines

---

Made with ❤️ by the [Folo](https://follow.is) team
