Adds FeedLand (feedland.com or self-hosted) as a new source type alongside OPML and Microsub. Syncs subscriptions via FeedLand's public OPML endpoint with optional category filtering and AJAX category discovery in the admin UI.
Blogroll Endpoint for Indiekit
An Indiekit plugin that provides a comprehensive blogroll management system with feed aggregation, admin UI, and public API.
Features
- Multiple Source Types: Import blogs from OPML files/URLs, Microsub subscriptions, or add manually
- Background Feed Fetching: Automatically syncs blogs and caches recent items
- Microsub Integration: Mirror your Microsub subscriptions as a blogroll (zero duplication)
- Admin UI: Manage sources, blogs, and view recent activity
- Public JSON API: Read-only endpoints for frontend integration
- OPML Export: Export your blogroll as OPML (all or by category)
- Feed Discovery: Auto-discover feeds from website URLs
- Item Retention: Automatic cleanup of old items (encourages fresh content discovery)
Installation
npm install @rmdes/indiekit-endpoint-blogroll
Configuration
Add to your indiekit.config.js:
import BlogrollEndpoint from "@rmdes/indiekit-endpoint-blogroll";
export default {
plugins: [
new BlogrollEndpoint({
mountPath: "/blogrollapi", // Admin UI and API base path
syncInterval: 3600000, // 1 hour (in milliseconds)
maxItemsPerBlog: 50, // Items to fetch per blog
maxItemAge: 7, // Days - older items auto-deleted
fetchTimeout: 15000 // 15 seconds per feed fetch
})
]
};
Requirements
- Indiekit:
>=1.0.0-beta.25 - MongoDB: Required for data storage
- Optional:
@rmdes/indiekit-endpoint-microsubfor Microsub integration
Usage
Admin UI
Navigate to /blogrollapi in your Indiekit instance to access:
- Dashboard: View sync status, blog counts, recent activity
- Sources: Manage OPML and Microsub sources
- Blogs: Add/edit/delete individual blogs, refresh feeds
- Manual Sync: Trigger immediate sync or clear and resync
Source Types
- OPML URL: Point to a public OPML file (e.g., your feed reader's export)
- OPML File: Paste OPML XML directly into the form
- Microsub: Import subscriptions from your Microsub channels
- Manual: Add individual blog feeds one at a time
Public API
All API endpoints return JSON (except OPML export which returns XML).
List Blogs
GET /blogrollapi/api/blogs?category=Tech&limit=100&offset=0
Get Blog with Recent Items
GET /blogrollapi/api/blogs/:id
List Items Across All Blogs
GET /blogrollapi/api/items?blog=<id>&category=Tech&limit=50&offset=0
List Categories
GET /blogrollapi/api/categories
Sync Status
GET /blogrollapi/api/status
Export OPML
GET /blogrollapi/api/opml (all blogs)
GET /blogrollapi/api/opml/:category (specific category)
Example Response
GET /blogrollapi/api/blogs
{
"items": [
{
"id": "507f1f77bcf86cd799439011",
"title": "Example Blog",
"description": "A great blog about tech",
"feedUrl": "https://example.com/feed",
"siteUrl": "https://example.com",
"feedType": "rss",
"category": "Tech",
"tags": ["programming", "web"],
"photo": "https://example.com/icon.png",
"status": "active",
"itemCount": 25,
"pinned": false,
"lastFetchAt": "2026-02-13T10:30:00.000Z"
}
],
"total": 42,
"hasMore": true
}
GET /blogrollapi/api/items
{
"items": [
{
"id": "507f1f77bcf86cd799439011",
"url": "https://example.com/post/hello",
"title": "Hello World",
"summary": "My first blog post...",
"published": "2026-02-13T10:00:00.000Z",
"isFuture": false,
"author": { "name": "Jane Doe" },
"photo": ["https://example.com/image.jpg"],
"categories": ["announcement"],
"blog": {
"id": "507f1f77bcf86cd799439011",
"title": "Example Blog",
"siteUrl": "https://example.com",
"category": "Tech",
"photo": "https://example.com/icon.png"
}
}
],
"hasMore": false
}
Microsub Integration
If you have @rmdes/indiekit-endpoint-microsub installed, the blogroll can mirror your subscriptions:
- Create a Microsub source in the admin UI
- Select specific channels or sync all channels
- Add a category prefix (optional) to distinguish Microsub blogs
- Blogs and items are referenced, not duplicated
Benefits:
- Zero data duplication - items are served directly from Microsub
- Automatic orphan cleanup when feeds are unsubscribed
- Webhook support for real-time updates
Background Sync
The plugin automatically syncs in the background:
- Initial Sync: Runs 15 seconds after server startup
- Periodic Sync: Runs every
syncIntervalmilliseconds (default 1 hour) - What it Does:
- Syncs enabled sources (OPML/Microsub)
- Fetches new items from active blogs
- Deletes items older than
maxItemAgedays - Updates sync statistics
Manual Sync:
- Trigger from the dashboard
- Use
POST /blogrollapi/sync(protected endpoint) - Use
POST /blogrollapi/clear-resyncto clear and resync all
Feed Discovery
The plugin includes auto-discovery for finding feeds from website URLs:
// In the admin UI, when adding a blog, paste a website URL
// The plugin will:
// 1. Check <link rel="alternate"> tags in HTML
// 2. Try common feed paths (/feed, /rss, /atom.xml, etc.)
// 3. Suggest discovered feeds
Item Retention
By default, items older than 7 days are automatically deleted during sync. This encourages discovery of fresh content rather than archiving everything.
To Change Retention:
new BlogrollEndpoint({
maxItemAge: 30 // Keep items for 30 days instead
})
Blog Status
- active: Blog is working, fetching items normally
- error: Last fetch failed (see
lastErrorfor details) - deleted: Soft-deleted, won't be recreated by sync
Navigation
The plugin adds itself to Indiekit's navigation:
- Menu Item: "Blogroll" (requires database)
- Shortcut: Bookmark icon in admin dashboard
Security
- Protected Routes: Admin UI and management endpoints require authentication
- Public Routes: Read-only API endpoints are publicly accessible
- XSS Prevention: Feed content is sanitized with
sanitize-html - Feed Discovery: Protected to prevent abuse (requires authentication)
Supported Feed Formats
- RSS 2.0
- Atom 1.0
- JSON Feed 1.0
Contributing
Report issues at: https://github.com/rmdes/indiekit-endpoint-blogroll/issues
License
MIT