RSS vers 66 réseaux sociaux : Facebook, Instagram, X, LinkedIn, Telegram et plus Blog Affiliation Contacts
Connexion Commencer gratuitement
Updated: 2026-11-04
MongoDB RSS Feed With Node: Queries, Indexes and Caching

Short answer: A MongoDB feed is a query, a template and a route. Store each article with a slug, a status and a publication date saved as a real date, query the newest published documents whose date has passed, project only the fields the feed needs, build the XML with escaped text and send it with the RSS content type, an ETag and a short cache. Add a compound index that matches the query, keep a last good copy for outages and refresh the cache with a change stream or a short timer. Test the live address, then connect it to an auto-poster.

MongoDB stores documents that look like the objects in your application, which makes it a natural source for a blog, a news section or a product update log. Nothing in the database knows about feeds, and that is an advantage, because you decide exactly what appears in the output. It is also a risk, because loose data produces loose feeds: dates saved as strings in two formats, documents without slugs, drafts that look like published articles. This guide shows a clean document shape, the index, the query and a short route written for Node and Express, and explains how to cache the result and how to test it. Driver details change with versions, so check the documentation of the one you use.

Shape the documents for a feed

Each article needs a few fields with strict types. The slug is a string that never changes and forms the address. The status says whether the article is public. The publication date must be stored as a date type and not as text, because only dates sort and compare correctly across years and time zones. The title and the summary are plain text without markup.

{
  slug: "mongodb-feed-example",
  title: "A short example article",
  summary: "One or two complete sentences that describe the article.",
  status: "published",
  publishedAt: ISODate("2026-11-04T09:00:00Z"),
  body: "..."
}

Do not use the identifier of a document, which contains the time of its creation, as the publication date. An article may be written on Monday and published on Friday, and a feed that follows the identifier would order it wrongly. Do not use a last-modified field either, because a correction would move the article to the top and could make an auto-poster share it again. The causes of repeated posts are explained in why a feed auto-posts the same article twice.

Feed elementSource in the documentRule
TitletitlePlain text, no markup
Link and identifierslug plus your site addressAbsolute, https, never changes
Publication datepublishedAtA date type, set once
DescriptionsummaryOne or two complete sentences

Create the index

The feed query filters on the status, compares the date and sorts by it. A compound index with the same order lets the database read the twenty newest entries without scanning the collection.

db.articles.createIndex({ status: 1, publishedAt: -1 })
db.articles.createIndex({ slug: 1 }, { unique: true })

The unique index on the slug protects you from two documents with the same address, which would give two items the same identifier. Run the explain command on the query once and check that it uses the index. A feed that is called every few minutes by several tools should never cause a full scan.

Write the route

The route below uses the official driver, Express and Node 18 or later. It reads the newest published documents, builds the XML and sends it with the right headers. If the database fails, it serves the last good copy.

import express from "express";
import { MongoClient } from "mongodb";
import { createHash } from "node:crypto";

const SITE = "https://example.com";
const client = new MongoClient(process.env.MONGODB_URI);
const articles = client.db("site").collection("articles");

const ENT = { "&": "&" + "amp;", "<": "&" + "lt;", ">": "&" + "gt;", '"': "&" + "quot;", "'": "&" + "apos;" };
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ENT[c]);

async function load() {
  return articles
    .find({ status: "published", publishedAt: { $lte: new Date() } })
    .project({ _id: 0, slug: 1, title: 1, summary: 1, publishedAt: 1 })
    .sort({ publishedAt: -1 })
    .limit(20)
    .toArray();
}

function render(docs) {
  const newest = docs.length ? docs[0].publishedAt : new Date(0);
  const items = docs.map((d) => {
    const url = SITE + "/blog/" + d.slug + "/";
    return `<item>
      <title>${esc(d.title)}</title>
      <link>${esc(url)}</link>
      <guid isPermaLink="true">${esc(url)}</guid>
      <pubDate>${d.publishedAt.toUTCString()}</pubDate>
      <description>${esc(d.summary)}</description>
    </item>`;
  }).join("\n");
  return `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel>
  <title>Example Journal</title>
  <link>${SITE}</link>
  <description>News and guides from Example Journal.</description>
  <lastBuildDate>${newest.toUTCString()}</lastBuildDate>
  ${items}
</channel></rss>`;
}

let cache = { xml: null, etag: null, at: 0 };
const app = express();

app.get("/feed.xml", async (req, res) => {
  try {
    if (!cache.xml || Date.now() - cache.at > 300000) {
      const xml = render(await load());
      cache = { xml, etag: '"' + createHash("sha1").update(xml).digest("hex") + '"', at: Date.now() };
    }
  } catch (err) {
    if (!cache.xml) return res.status(503).send("Feed temporarily unavailable");
  }
  res.set({
    "Content-Type": "application/rss+xml; charset=utf-8",
    "Cache-Control": "public, max-age=300",
    ETag: cache.etag,
  });
  if (req.get("If-None-Match") === cache.etag) return res.status(304).end();
  res.send(cache.xml);
});

await client.connect();
app.listen(3000);

The projection leaves out the body of each article, which can be large, and the query returns plain objects with real dates, so the conversion to the GMT form is exact. The cache is a single variable with a time stamp, which is enough for one process. If you run several, each keeps its own copy, and the validators are identical as long as the data is the same.

Keep the feed fresh

A cache of five minutes means that a new article appears in the feed within five minutes. For faster updates, use a change stream, which tells your application when documents change. It needs a replica set or a hosted cluster, which most managed services provide. Watch for inserts and updates that set the status to published, and clear the cache when one arrives. Keep the timer as well, because a stream can disconnect, and scheduled articles become visible when their date passes, which is not a database event. Background on delays is in why auto-posted content sometimes lags.

Schemas, validation and bad data

The flexibility of documents means that bad data gets in easily. Add a validation rule to the collection that requires a slug, a title, a summary, a status from a fixed list and a date type for the publication date. Reject empty strings. Run a one-time check for documents that already violate the rules, in particular articles with a date stored as text, which the query ignores silently or sorts wrongly. A feed that omits an article without any error is hard to debug, so test with a draft, a scheduled article, an article with a special character and an article with a long title.

Security and access

A feed route is public, but your database must not be. Give the application a database user that can only read the articles collection, keep the connection string in an environment variable and never pass request parameters into the query. If you offer filters by category or language, take the value from a fixed list and not from the address as it arrives, because a crafted parameter can change the meaning of a query. Restrict network access to the database so that only your servers can connect, and enable the authentication and encryption that your provider offers. The feed itself needs no more than a read permission, so a leak of its credentials should never allow changes.

Archives and long histories

An auto-poster needs only the newest items, so twenty entries are enough. Do not return the whole collection to be helpful. A large feed is slow, may time out and gives no benefit, because tools read the first entries and compare them with what they already know. If you want to offer an archive to people, build it as a normal page with pagination on the website and keep the feed short. If you publish several articles within a minute, for example after a bulk import, raise the limit for that day so that none is pushed out before a tool reads it. See how many items a feed should contain.

Languages and categories

If your collection covers several languages, store the language in each document and create one route per language, so that every language has its own feed and its own social channel. A feed that mixes translations makes an auto-poster share the same story several times, and followers see it as spam. Categories work the same way: add a category field, filter on it in the query and offer one address per topic. See category filtering for auto-posting et the guide to multilingual feeds.

Test the feed

curl -i https://example.com/feed.xml
curl -i -H 'If-None-Match: "paste-the-etag-here"' https://example.com/feed.xml
  • The first request returns 200, the RSS content type and a body that starts with an XML declaration.
  • The second returns 304 with no body when the value matches.
  • A draft and an article with a future date do not appear.
  • An article with an ampersand in its title appears, and the XML is valid.
  • The address passes the W3C Feed Validation Service without errors.

A longer routine is in the feed validation guide, and a shorter one in debugging a feed with curl.

Common mistakes to avoid

  • Dates saved as text. They sort wrongly and compare badly.
  • The document identifier as the date. It shows when the record was created, not published.
  • A last-modified date as the item date. A correction moves the article to the top.
  • No index. The query slows down as the collection grows.
  • Whole documents in the query. Project only the fields you need.
  • An empty feed on errors. Serve the last good copy or an error status.

How PostRSS works with a feed built from MongoDB

PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your feed, 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 entry, 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 and posting windows let you tune each channel. See the PostRSS features page and the plan limits on the pricing page. More developer guides are under the Technical Guide tag.

Lectures associées

The bottom line

A MongoDB feed depends on disciplined data. Store the slug, the status and a publication date as a real date, add a compound index, query only what the feed needs, escape the text, send the right headers and cache the result with a fallback. Validate documents so that bad data cannot slip in, refresh the cache with a change stream and a timer, test the live address from outside and then connect it to an auto-poster, so that every article you publish reaches your social channels without extra work.

FAQ

How do I create an RSS feed from MongoDB?

Query the newest published documents, build the XML with escaped text in a route, and send it with the RSS content type, an ETag and a short cache.

Which date should the feed use?

A publication date stored as a real date and set once when the article goes live. Do not use the identifier or a last-modified field.

How can the feed update as soon as an article is published?

Use a change stream to clear the cache when a document is published, and keep a short timer as a safety net for scheduled articles and dropped connections.

Why is an article missing from my feed?

Common causes are a status that does not match, a date stored as text, a date in the future or a missing slug. Check the document against the validation rules.

Can PostRSS read a feed built from MongoDB data?

Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login. Paste the address, connect your channels and new articles are shared as soon as they appear.

New guides, once a month

What changed in the networks, what broke, and how to fix it before it costs you reach.

We send a confirmation e-mail first. Unsubscribe any time.

D'autres outils signés par notre équipe

Conçus par Internet Solutions, l'équipe derrière PostRSS. Chaque produit vous fait gagner du temps à sa façon.