Blog RSS Feed
RSS 2.0 XML feed of the last 20 published blog posts. Content-Type application/rss+xml. Bot-detection skipped (feed readers should not be flagged). CDN 15min/30min SWR.
GET /api/v1/public/blog/feed โ ๐ Public ยท Rate limit: 30 req / minute
Returns the last 20 published blog posts as an RSS 2.0 XML feed for feed readers (NetNewsWire, Feedly, Reeder, etc). Bot-detection is skipped (@SkipBotDetection()) โ feed readers are bot-like and shouldn't be challenged. Cached 15min at the CDN with 30min stale-while-revalidate.
Different response shape from the rest of the portal. This endpoint returns raw XML with Content-Type: application/rss+xml; charset=utf-8 โ NOT the standard JSON { success, data } envelope. Consume with an XML parser (or just hand to your feed reader). The standard error envelope still applies for 4xx/5xx, but the success path is XML.
Cap is 20 (lower than the regular list). Feeds are designed for "what's new" โ the last 20 posts is plenty. The regular /public/blog endpoint serves up to 50 per page with full pagination if you need older posts.
Request
No body, no params, no headers required.
Response headers
| Header | Value |
|---|---|
Content-Type | application/rss+xml; charset=utf-8 |
Cache-Control | public, s-maxage=900, stale-while-revalidate=1800 |
Response
200 OK โ RSS 2.0 XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>BIO.RE Blog</title>
<link>https://bio.re/blog</link>
<description>Latest posts from the BIO.RE blog</description>
<language>en</language>
<lastBuildDate>Wed, 29 Apr 2026 20:00:00 GMT</lastBuildDate>
<atom:link href="https://bio.re/api/v1/public/blog/feed" rel="self" type="application/rss+xml"/>
<item>
<title>Introducing BIO.RE</title>
<link>https://bio.re/blog/introducing-biore</link>
<guid isPermaLink="true">https://bio.re/blog/introducing-biore</guid>
<description><![CDATA[A short excerpt of the post...]]></description>
<pubDate>Wed, 29 Apr 2026 20:00:00 GMT</pubDate>
<category>Product Updates</category>
</item>
</channel>
</rss>XML elements
| Element | Source | Notes |
|---|---|---|
channel.title | hardcoded | BIO.RE Blog |
channel.link | hardcoded | https://bio.re/blog |
channel.description | hardcoded | Latest posts from the BIO.RE blog |
channel.language | hardcoded | en |
channel.lastBuildDate | computed | The most recent publishedAt of any item, formatted as RFC 822 (UTC) |
channel.atom:link | hardcoded | Self-link to this feed |
item.title / item.link / item.guid / item.pubDate | per post | Individual post metadata. Title is XML-escaped server-side. |
item.description | per post | The post's excerpt (or empty), wrapped in CDATA[] |
item.category | per post category | One <category> element per associated BlogCategory.name, XML-escaped |
Errors
| HTTP | Reason |
|---|---|
429 | Rate limit exceeded (30 req/min โ tighter than the JSON list) |
Side effects
getBlogFeedItems()โprisma.blogPost.findMany({ where: status PUBLISHED, orderBy publishedAt desc, take: 20, select: slug+title+excerpt+publishedAt+categories }).- Build XML string by concatenation. Each user-controllable field passes through
xmlEscape()(replaces&,<,>,",'with entities). - Set
Content-Type: application/rss+xml; charset=utf-8+ cache header on the raw response (@Res()mode โ bypasses the standard interceptor). - Return the XML body. No mutations.
Code samples
curl https://api.bio.re/api/v1/public/blog/feedhttps://api.bio.re/api/v1/public/blog/feedPaste this URL into NetNewsWire / Feedly / Reeder / Inoreader. Most feed readers will auto-detect the format and start polling.
// Server-side โ fetch + parse with rss-parser
import Parser from 'rss-parser';
const parser = new Parser();
async function fetchBlogFeed() {
const feed = await parser.parseURL('https://api.bio.re/api/v1/public/blog/feed');
for (const item of feed.items) {
console.log(item.title, item.link, item.pubDate);
}
}Try it
curl -X GET "https://loading/api/v1/public/blog/feed"Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/content/public-content.controller.ts | 209โ222 (getBlogRssFeed), 145โ147 (xmlEscape), 149โ179 (buildRss2) |
| Service | apps/api-core/src/modules/content/content.service.ts | 578โ... (getBlogFeedItems โ slug+title+excerpt+publishedAt+categories slice) |
| Prisma model | packages/prisma/prisma/schema.prisma | BlogPost (filter status = PUBLISHED), BlogCategory (joined for <category> elements) |
| Bot detection skip | apps/api-core/src/common/decorators/skip-bot-detection.decorator.ts | @SkipBotDetection() |
List Blog Posts
Public paginated list of published blog posts. Optional category filter (by slug). Lightweight items (no body, just excerpt). Categories embedded as { name, slug } pairs. CDN 5min/10min SWR.
Blog Atom Feed
Atom XML feed of the last 20 published blog posts. Same data as the RSS feed in Atom format. Content-Type application/atom+xml. CDN 15min/30min SWR.