
Short answer: PocketBase has no built-in RSS output, but a few lines of JavaScript in the pb_hooks folder add one. Register a route with routerAdd('GET', '/rss.xml', ...), load the newest published records with $app.findRecordsByFilter, build RSS 2.0 XML with escaped text and absolute links, and return it with the content type application/rss+xml. Add a short cache header, validate the feed once and paste its address into an auto-poster so that every new published record is shared on social media automatically.
PocketBase is an open-source backend in a single executable: an embedded database, authentication, file storage, an admin dashboard and an extension system. It is popular for small apps, internal tools and personal sites, and many of them keep articles, products or announcements in a collection. Because PocketBase lets you extend it with JavaScript hooks, a feed is a small addition rather than a separate service. This guide shows the collection, the route, the points where hooks behave differently from ordinary Node.js code, caching and the checks that keep the feed reliable. At the end you will see how to share each item automatically.
A feed is an HTTP response containing XML. PocketBase already runs a web server, and its JavaScript extension layer lets you add custom routes that run inside the same process. There is no need for a second server or a separate deployment, and the feed shares the host name and the certificate of your PocketBase instance. If your content lives in a system without a native feed, the idea is the same as in auto-posting from a headless CMS without native RSS: build the feed yourself and let the auto-poster read it.
This guide targets recent PocketBase versions, from 0.23 onward, in which route handlers receive a single event object. Older versions used a different handler signature, so check the documentation for the version you run.
Create a collection named posts with the fields title (text), slug (text), summary (text), cover (file, optional), published_at (date) and status (text or select with values such as draft and published). Content editors can then work in the dashboard, and only records with the status published will appear in the feed.
Hooks run with full access to the application, so the API rules of the collection do not limit what the code can read. That is convenient, but it also means your code must do the filtering. Never rely on the collection rules to hide drafts from a feed that you build by hand.
Create the file pb_hooks/rss.pb.js next to the PocketBase executable:
routerAdd('GET', '/rss.xml', (e) => {
const SITE = 'https://www.example.com';
const PB = 'https://cms.example.com';
const esc = (value) => String(value || '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
const records = $app.findRecordsByFilter(
'posts',
"status = 'published'",
'-published_at',
20,
0
);
const items = records.map((post) => {
const link = SITE + '/blog/' + post.getString('slug');
const cover = post.getString('cover');
const image = cover ? PB + '/api/files/posts/' + post.id + '/' + cover : '';
const date = new Date(post.getString('published_at').replace(' ', 'T'));
return '<item>'
+ '<title>' + esc(post.getString('title')) + '</title>'
+ '<link>' + esc(link) + '</link>'
+ '<guid isPermaLink="false">post-' + post.id + '</guid>'
+ '<pubDate>' + date.toUTCString() + '</pubDate>'
+ '<description>' + esc(post.getString('summary')) + '</description>'
+ (image ? '<enclosure url="' + esc(image) + '" type="image/jpeg" length="0"/>' : '')
+ '</item>';
});
const xml = '<?xml version="1.0" encoding="UTF-8"?>'
+ '<rss version="2.0"><channel>'
+ '<title>Example Blog</title>'
+ '<link>' + SITE + '/blog</link>'
+ '<description>News and articles from Example</description>'
+ items.join('')
+ '</channel></rss>';
e.response.header().set('Content-Type', 'application/rss+xml; charset=utf-8');
e.response.header().set('Cache-Control', 'public, max-age=300');
return e.string(200, xml);
});Restart PocketBase after saving the file. The feed is then available at /rss.xml on the same address as the dashboard, for example https://cms.example.com/rss.xml.
PocketBase runs hooks in an embedded JavaScript engine, not in Node.js or a browser. Four differences matter when you write a feed:
esc helper are defined inside it. To share code between handlers, put it in a module and load it with require inside the handler.URL oppure fetch may not exist. Build addresses with simple string concatenation, as in the example.The helper esc converts ampersands and angle brackets into entities. Without it, a title such as “Tips & tricks” produces invalid XML and many readers stop at the first error. If your summaries contain HTML, escape them or wrap them in CDATA; the rules are in CDATA and HTML in RSS descriptions.
The filter keeps drafts out, and the sort puts the newest record first with a limit of 20. Sort by the publication date, not by the update date, so that an edited old record does not jump to the top. The GUID uses the record ID, which never changes. If you used the slug and later renamed a post, an auto-poster could share it twice; see why your RSS feed auto-posts the same article twice. Dates use toUTCString(), which produces the RFC 822 format that RSS expects, as described in RSS date formats and pubDate errors.
Items are read outside your website, so every address must be complete. A relative link or image path works in a browser on your site but fails in a reader or on a social network. In the example, the article link is built from the public address of the front end, and the image address from the public address of PocketBase. Problems of this kind are described in relative URLs in RSS feeds. Choose images about 1200 pixels wide, and set the type according to the real file. Sizes for each network are listed in social media image sizes.
PocketBase does not cache custom routes for you, and a feed is requested constantly by readers, crawlers and auto-posters. The header in the example lets browsers and proxies reuse the response for five minutes. In production, PocketBase usually sits behind a reverse proxy such as Caddy or Nginx that terminates https. You can let the proxy cache the feed for a few minutes, or compress it, without touching the hook; see RSS feed compression with gzip and Brotli. Keep the lifetime short: a feed cached for a day delays every share by a day.
The route can read query parameters with e.request.url.query().get('category'). If a value is present, add a condition to the filter using a parameter placeholder, for example category = {:cat}, and pass the value as an object in the extra argument. Never insert the value directly into the filter text. Move the XML builder into a module, and serve one feed for everything and one for each category. Separate feeds let you send news to one channel, tutorials to another and announcements to a team chat. The idea is explained in category filtering for auto-posting.
A feed that works on your laptop can still fail in production, so check three things after deployment. First, the hook file must be copied to the pb_hooks folder of the server, not only to your development folder, and the service must be restarted. Second, the public addresses in the code must match the real domains, with https and without a trailing slash, because a feed that points to localhost or to an internal host name is useless to a reader. Third, the reverse proxy must pass the request through to PocketBase without adding a login page or changing the content type. When you move the instance to another host later, keep the feed address stable or leave a permanent redirect, because every reader and auto-poster that stored the old address will keep asking for it.
curl -i and check that the status is 200 and the content type is application/rss+xml. The method is described in debugging an RSS feed with curl.The extension system is documented at pocketbase.io/docs.
PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your rss.xml route, connect your accounts and choose how each post is built from the title, the description or the page title. PostRSS checks the feed as often as every minute and shares each new post, with its image and link, to the channels you choose among 66 supported networks, messengers, team chats and blogs, including LinkedIn, X, Bluesky, Mastodon and Telegram. Keyword filters, UTM parameters, hashtags from categories and posting windows let you tune each channel, and the REST API and webhooks connect PostRSS to your own systems. See the PostRSS features page and the plan limits on the pricing page. More developer guides are under the Technical Guide tag.
A PocketBase RSS feed is one hook file. Register a GET route, load the newest published records, escape every text value, use the record ID for the GUID, make links and images absolute and return the content type application/rss+xml with a short cache lifetime. Remember that handlers are isolated, that dates arrive as text and that your code must filter drafts. After that, every record you publish can reach your social channels automatically.
No. PocketBase provides the database, the API and the extension system. You add the feed with a small JavaScript hook that builds RSS XML from your collection.
Put it in the pb_hooks folder next to the PocketBase executable, with a name that ends in .pb.js, and restart PocketBase after changing it.
Handlers run in isolation, so variables declared at the top of the file are not visible inside them. Define constants and helpers inside the handler or load them from a module with require.
Yes. Move the XML builder into a module that takes a title and a list of records, and register one route for each collection or category.
Yes. Paste the public feed address as a source in PostRSS, connect your social accounts and each new item is posted automatically. Make sure the address opens without a login first.
{“@context”: “https://schema.org”, “@type”: “FAQPage”, “mainEntity”: [{“@type”: “Question”, “name”: “Does PocketBase have a built-in RSS feed?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “No. PocketBase provides the database, the API and the extension system. You add the feed with a small JavaScript hook that builds RSS XML from your collection.”}}, {“@type”: “Question”, “name”: “Where do I put the hook file?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Put it in the pb_hooks folder next to the PocketBase executable, with a name that ends in .pb.js, and restart PocketBase after changing it.”}}, {“@type”: “Question”, “name”: “Why does my variable not exist inside the handler?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Handlers run in isolation, so variables declared at the top of the file are not visible inside them. Define constants and helpers inside the handler or load them from a module with require.”}}, {“@type”: “Question”, “name”: “Can I use the same code for several collections?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Yes. Move the XML builder into a module that takes a title and a list of records, and register one route for each collection or category.”}}, {“@type”: “Question”, “name”: “Can I use the PocketBase feed with PostRSS?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Yes. Paste the public feed address as a source in PostRSS, connect your social accounts and each new item is posted automatically. Make sure the address opens without a login first.”}}]}
What changed in the networks, what broke, and how to fix it before it costs you reach.
Realizzati da Internet Solutions, il team dietro PostRSS. Ognuno ti fa risparmiare tempo in modo diverso.