RSS uz 66 sociālajiem tīkliem: Facebook, Instagram, X, LinkedIn, Telegram un citiem Blogs Partner programma Kontakti
Pieslēgties Sākt bez maksas
Updated: 2026-10-16
AdonisJS RSS Feed: Build It in TypeScript and Auto-Post Articles

Short answer: AdonisJS has no built-in RSS output, but a feed takes one route and one controller. Register router.get('/rss.xml', ...), load the newest published records with Lucid, build RSS 2.0 XML with escaped text, absolute links and RFC 822 dates, and return it with the content type application/rss+xml. Add a short cache header, validate the result once and give the address to an auto-poster, so that every new article you publish reaches your social channels without manual work.

AdonisJS is a full-featured Node.js framework written in TypeScript. It ships with an ORM called Lucid, a router, validation, authentication and a template engine, so a typical project already has a database table of articles, products or announcements. That makes a feed a natural addition: the data exists, the router exists, and all that is missing is an XML response. This guide uses AdonisJS 6 conventions and shows the model, the controller, the route, caching, a feed per category and the checks that keep the feed reliable. At the end you will see how to share each new item automatically.

Why build the feed inside the application

You could generate a static file with a script and upload it, but a route has real advantages. It is always in step with the database, it uses the same domain and certificate as the website, and it can reuse your own model scopes, for example the one that hides drafts. There is no extra service to run and no file that can go stale. If your content lives in a system without a native feed, the principle is the same as in auto-posting from a headless CMS without native RSS: produce the feed yourself and let the auto-poster read it.

The examples assume a Post model with the columns title, slug, summary, status, published_at un cover_url. Rename them to match your own schema.

Step 1: Create the controller and the route

Generate a controller with the Ace command line tool:

node ace make:controller feed

Then open app/controllers/feed_controller.ts and write the handler:

import type { HttpContext } from '@adonisjs/core/http'
import env from '#start/env'
import Post from '#models/post'

const escapeXml = (value: unknown) =>
  String(value ?? '')
    .replace(/&/g, '&')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')

export default class FeedController {
  async index({ response }: HttpContext) {
    const site = env.get('APP_URL')

    const posts = await Post.query()
      .where('status', 'published')
      .orderBy('published_at', 'desc')
      .limit(20)

    const items = posts.map((post) => {
      const link = `${site}/blog/${post.slug}`
      return [
        '<item>',
        `<title>${escapeXml(post.title)}</title>`,
        `<link>${escapeXml(link)}</link>`,
        `<guid isPermaLink="false">post-${post.id}</guid>`,
        `<pubDate>${post.publishedAt!.toUTC().toRFC2822()}</pubDate>`,
        `<description>${escapeXml(post.summary)}</description>`,
        post.coverUrl
          ? `<enclosure url="${escapeXml(post.coverUrl)}" type="image/jpeg" length="0"/>`
          : '',
        '</item>',
      ].join('')
    })

    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>'

    response.header('Content-Type', 'application/rss+xml; charset=utf-8')
    response.header('Cache-Control', 'public, max-age=300')
    return response.send(xml)
  }
}

Register the route in start/routes.ts with a lazy import, which is the standard AdonisJS pattern:

import router from '@adonisjs/core/services/router'

const FeedController = () => import('#controllers/feed_controller')

router.get('/rss.xml', [FeedController, 'index'])

The variable APP_URL must exist in .env and be declared in start/env.ts with APP_URL: Env.schema.string(), so that the application refuses to start if it is missing. Start the server and open /rss.xml. You should see the XML, and a browser will usually offer to download or display it as text.

Why each part matters

The helper escapeXml turns 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 query returns published records only, newest first, with a limit of 20. Sort by the publication date, not by the update date, so that an edited old article does not jump to the top. The GUID uses the record ID, which never changes. If you used the slug and later renamed an article, an auto-poster could treat it as new and share it twice; see why your RSS feed auto-posts the same article twice. Lucid returns Luxon date objects, and the toRFC2822() method produces the format RSS expects, as described in RSS date formats and pubDate errors.

Absolute links and images

Feed 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 feed reader or on a social network. In the controller the link is built from APP_URL. If you store only a file name for the cover image, add the public address of your storage or CDN in front of it. 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. The sizes each network prefers are listed in social media image sizes.

Caching without surprises

A feed is requested constantly by readers, crawlers and auto-posters, and each request runs a database query. The header in the example lets browsers and proxies reuse the response for five minutes. For busier sites, keep the generated XML in memory for a short time as well:

let cache: { xml: string; expires: number } | null = null

// inside index():
if (cache && cache.expires > Date.now()) {
  response.header('Content-Type', 'application/rss+xml; charset=utf-8')
  return response.send(cache.xml)
}
// ...build xml...
cache = { xml, expires: Date.now() + 5 * 60 * 1000 }

This simple cache lives in one process. If you run several instances, each has its own copy, which is fine for a feed because the lifetime is short. Keep it short in any case: a feed cached for a day delays every share by a day. A reverse proxy such as Nginx or Caddy can also compress the response; see RSS feed compression with gzip and Brotli.

One feed per category

Many sites publish different kinds of content that belong on different channels. Add a second route such as /rss/:category.xml, read the parameter with params.category, and add a where condition to the query. Look the category up first and return a 404 when it does not exist, so that a typing mistake does not produce an empty feed that looks valid. Move the XML builder into a function that takes a title and a list of posts, and call it from both routes. 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.

Summary or full content

The example puts a short summary into the description element, which is what most auto-posters use for the caption. If you also want the full article in the feed, add a content:encoded element wrapped in CDATA and declare the content namespace on the rss element. Full content makes the feed larger and slower, and it lets other sites copy your articles easily, so many publishers prefer summaries with a link back. The trade-offs are discussed in full text or excerpt in RSS feeds. Whatever you choose, write the summary as a real sentence or two. It becomes the text that people read next to your link, so avoid cutting it in the middle of a word and avoid repeating the title.

Make the feed discoverable

Add a link element to the head of your layout template so that browsers, readers and tools can find the feed without being told:

<link rel="alternate" type="application/rss+xml" title="Example Blog" href="https://www.example.com/rss.xml">

With this tag in place, pasting your home page address into many tools is enough, because they follow the link to the feed. The mechanism is described in RSS plūsmas automātiska atklāšana.

Deploying the feed in production

A feed that works on your laptop can still fail in production, so check three things after deployment. First, APP_URL must hold the real public address with https and without a trailing slash, because a feed that points to localhost is useless to a reader. Second, the reverse proxy must pass the request through without adding a login page, a bot challenge or a different content type. Third, the process must be able to reach the database at the moment the feed is requested, which matters when an auto-poster checks the feed often. If your server sits behind a service that blocks unknown visitors, see Cloudflare blocking your RSS feed. When you move the application to another host later, keep the feed address stable or leave a permanent redirect, because every reader that stored the old address will keep asking for it.

Common mistakes to avoid

  • Forgetting to filter drafts. Only the query decides what is public. Put the condition in a model scope and reuse it for the website and the feed.
  • Returning HTML or JSON by mistake. Set the content type to application/rss+xml; see the Content-Type guide.
  • Unescaped text. One stray ampersand in a title can break the whole feed.
  • Relative image paths. Build full addresses for files and covers.
  • Changing the GUID later. A new GUID makes an old post look new and can trigger a repeat share.
  • Too many items. Twenty is enough for most sites; see how many items should be in your RSS feed.

Test the feed before connecting it

  1. Request the feed with 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.
  2. Confirm that the newest published record is first and that no draft appears.
  3. Open several item links and image addresses; they must use your public https domain.
  4. Run the feed through the W3C Feed Validation Service.
  5. Publish a test record, wait for the cache time and check that it appears.

The framework is documented at docs.adonisjs.com.

How PostRSS works with an AdonisJS site

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.

Saistīta lasāmviela

The bottom line

An AdonisJS RSS feed is one controller and one route. Query the published records, escape every text value, use the record ID for the GUID, make links and images absolute, format dates with toRFC2822 and return the content type application/rss+xml with a short cache lifetime. Add the autodiscovery tag, test the feed with curl and a validator, and keep the address stable. After that, every article you publish can reach your social channels automatically.

FAQ

Does AdonisJS have a built-in RSS feed?

No. AdonisJS provides the router, the ORM and the response helpers, and you add the feed with a controller that builds RSS XML from your records.

Do I need a package to generate the XML?

No. A feed is plain text with a few elements, and the example builds it with simple string functions. A package can help if you need many extra fields, but it is not required.

How do I set the right content type?

Call response.header before sending the body and use application/rss+xml with the UTF-8 charset. Check the result with curl -i.

Which date format does RSS need?

RSS uses the RFC 822 format. Lucid returns Luxon dates, and the toRFC2822 method produces a valid value, preferably converted to UTC first.

Can I use the AdonisJS feed with PostRSS?

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 AdonisJS have a built-in RSS feed?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “No. AdonisJS provides the router, the ORM and the response helpers, and you add the feed with a controller that builds RSS XML from your records.”}}, {“@type”: “Question”, “name”: “Do I need a package to generate the XML?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “No. A feed is plain text with a few elements, and the example builds it with simple string functions. A package can help if you need many extra fields, but it is not required.”}}, {“@type”: “Question”, “name”: “How do I set the right content type?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Call response.header before sending the body and use application/rss+xml with the UTF-8 charset. Check the result with curl -i.”}}, {“@type”: “Question”, “name”: “Which date format does RSS need?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “RSS uses the RFC 822 format. Lucid returns Luxon dates, and the toRFC2822 method produces a valid value, preferably converted to UTC first.”}}, {“@type”: “Question”, “name”: “Can I use the AdonisJS 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.”}}]}

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.

Vairāk mūsu komandas rīku

Izstrādājusi Internet Solutions — PostRSS komanda. Katrs produkts ietaupa laiku savā veidā.