Custom routes lead to 404 for /rss/

I’ve created custom routes, which work, but now /rss/ throws a 404 error.

routes.yaml

routes:
  /:
    data:
      post:
        resource: pages
        type: read
        slug: home
    template:
      - home-page

  /clinic/:
    data:
      post:
        resource: posts
        type: read
        slug: clinic
      featured:
        resource: posts
        type: browse
        filter: 'featured:true+tag:clinic'
        include: tags,authors
    template:
      - section

  /farm/:
    data:
      post:
        resource: posts
        type: read
        slug: farm
      featured:
        resource: posts
        type: browse
        filter: 'featured:true+tag:farm'
        include: tags,authors
    template:
      - section

collections:
  /clinic/blog/:
    permalink: /clinic/blog/{slug}/
    filter: 'tag:clinic+page:false'
    template:
      - index

  /farm/blog/:
    permalink: /farm/blog/{slug}/
    filter: 'tag:farm+page:false'
    template:
      - index

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

Loading routes.yaml creates the following routes map:

[ { route: '/p/:uuid/:options?', from: 'PreviewRouter' },
  { route: '/', from: 'StaticRoutesRouter' },
  { route: '/clinic/', from: 'StaticRoutesRouter' },
  { route: '/farm/', from: 'StaticRoutesRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
  { route: '/tag/:slug/', from: 'Taxonomy' },
  { route: '/tag/:slug/', from: 'Taxonomy' },
  { route: '/tag/:slug/page/:page(\\d+)', from: 'Taxonomy' },
  { route: '/tag/:slug/edit', from: 'Taxonomy' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
  { route: '/author/:slug/', from: 'Taxonomy' },
  { route: '/author/:slug/', from: 'Taxonomy' },
  { route: '/author/:slug/page/:page(\\d+)', from: 'Taxonomy' },
  { route: '/author/:slug/edit', from: 'Taxonomy' },
  { route: '/clinic/blog/', from: 'CollectionRouter' },
  { route: '/clinic/blog/page/:page(\\d+)',
    from: 'CollectionRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
  { route: '/clinic/blog/', from: 'CollectionRouter' },
  { route: '/clinic/blog/:slug/:options(edit)?/',
    from: 'CollectionRouter' },
  { route: '/farm/blog/', from: 'CollectionRouter' },
  { route: '/farm/blog/page/:page(\\d+)',
    from: 'CollectionRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
  { route: '/farm/blog/', from: 'CollectionRouter' },
  { route: '/farm/blog/:slug/:options(edit)?/',
    from: 'CollectionRouter' },
  { route: '/:slug/:options(edit)?/', from: 'StaticPagesRouter' } ]

I don’t understand why accessing /rss/ does not invoke the RSSRouter:

Tue, 26 Mar 2019 16:24:29 GMT ghost:web:shared:mw:url-redirects getBlogRedirectUrl wholemedicine.net /rss/ https://wholemedicine.net/
  Tue, 26 Mar 2019 16:24:29 GMT ghost:web:shared:mw:url-redirects no url redirect
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled PreviewRouter /p/ pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled StaticRoutesRouter / pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled StaticRoutesRouter /clinic/ pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled StaticRoutesRouter /farm/ pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled Taxonomy undefined pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled Taxonomy undefined pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled CollectionRouter /clinic/blog/ pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled CollectionRouter /farm/blog/ pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled StaticPagesRouter undefined pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:ParentRouter isRedirectEnabled AppsRouter undefined pages rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:controllers:entry entryController { type: 'entry',
    filter: undefined,
    permalinks: '/:slug/:options(edit)?/',
    resourceType: 'pages',
    query: 
     { controller: 'pages',
       type: 'read',
       resource: 'pages',
       options: { slug: '%s' } },
    context: [ 'page' ] }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:helpers:entry-lookup /rss/
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:helpers:entry-lookup /rss/
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:helpers:entry-lookup { slug: 'rss' }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:services:routing:helpers:entry-lookup /:slug/:options(edit)?/
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:frame configure
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:frame original { body: undefined,
    options: { slug: 'rss', include: 'author,authors,tags' },
    context: {} }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:frame options { include: 'author,authors,tags', context: {} }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:frame data { slug: 'rss' }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:pipeline stages: validation
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:handle input
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:handle [ [Function: allShared], [Function: allShared] ]
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all validate all
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all include author,authors,tags
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all ctrl validation
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all context {}
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all validate read
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all validate browse
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all slug rss
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:validators:input:all global validation
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:pipeline stages: input serialisation
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:serializers:handle input
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:serializers:handle [ [Function: serializeAllShared],
    [Function: serializeOptionsShared] ]
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:serializers:input:all serialize all
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:serializers:input:all omit internal options
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:serializers:input:all { context: {}, withRelated: [ 'author', 'authors', 'tags' ] }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:v2:utils:serializers:input:pages read
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:v2:utils:serializers:input:pages { context: {}, withRelated: [ 'author', 'authors', 'tags' ] }
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:pipeline stages: permissions
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:v2:utils:permissions handle
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:v2:utils:permissions check content permissions
  Tue, 26 Mar 2019 16:24:29 GMT ghost:api:shared:pipeline stages: query
  Tue, 26 Mar 2019 16:24:29 GMT ghost:error-handler NotFoundError: Page not found
      at new NotFoundError (/var/lib/ghost/versions/2.15.0/node_modules/ghost-ignition/lib/errors/index.js:93:23)
      at errorHandler.pageNotFound (/var/lib/ghost/versions/2.15.0/core/server/web/shared/middlewares/error-handler.js:173:10)
      at Layer.handle [as handle_request] (/var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/layer.js:95:5)
      at trim_prefix (/var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/index.js:317:13)
      at /var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/index.js:284:7
      at Function.process_params (/var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/index.js:335:12)
      at Immediate.next (/var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/index.js:275:10)
      at Immediate.<anonymous> (/var/lib/ghost/versions/2.15.0/node_modules/express/lib/router/index.js:635:15)
      at runCallback (timers.js:812:20)
      at tryOnImmediate (timers.js:768:5)
      at processImmediate [as _immediateCallback] (timers.js:745:5)

My guess is that a change to routes.yaml could solve this issue. Returning to the default routes.yaml file does fix the RSS feed. The question is how to fix the RSS feed while keeping the custom routes. If only I knew where to start. I welcome suggestions.

The default /rss/ routes exist as subroutes for collections and channels. You’ll find that RSS feeds exist and are working under /clinic/blog/rss and /farm/blog/rss/.

The routes map is interesting, but the rss routes are actually relative :thinking:

2 Likes

Hannah, I understand that both collections have their own RSS endpoint. Yet, /rss/ is also registered as a route to the RSSRouter. Why does this route not trigger the RSS controller?

The RSSRouter is outputting its own relative path, not an absolute path. If you look closely you’ll see the rss routes are output twice, each after the collection router.

{ route: '/clinic/blog/', from: 'CollectionRouter' },
  { route: '/clinic/blog/page/:page(\\d+)',
    from: 'CollectionRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
  { route: '/clinic/blog/', from: 'CollectionRouter' },
  { route: '/clinic/blog/:slug/:options(edit)?/',
    from: 'CollectionRouter' },
  { route: '/farm/blog/', from: 'CollectionRouter' },
  { route: '/farm/blog/page/:page(\\d+)',
    from: 'CollectionRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },

The RSSRouter is always nested under a collection or channel router. It doesn’t exist as standalone.

1 Like

I saw that Hannah, those 2 are relative to the collection mount points.

But there is also a 3rd entry, before the 2 collections:

{ route: '/p/:uuid/:options?', from: 'PreviewRouter' },
  { route: '/', from: 'StaticRoutesRouter' },
  { route: '/clinic/', from: 'StaticRoutesRouter' },
  { route: '/farm/', from: 'StaticRoutesRouter' },
  { route: '/rss/', from: 'RSSRouter' },
  { route: '/rss/:page(\\d+)', from: 'RSSRouter' },
  { route: '/feed/', from: 'RSSRouter' },
....

This time, the RSSRouter is mounted relative to /. Why does accessing this path not trigger the RSS controller?

Those first two are the rss feeds for the two taxonomies (which are a kind of super-channel).

The RSSRouter is always nested under a collection or channel router. It doesn’t exist standalone.

If you want a top-level RSS route, you would need to create a custom one:

https://docs.ghost.org/integrations/custom-rss/

1 Like

Yes, that is the way forward to top-level RSS route. However, the question remains:

Why is there is no trace in the log that the registered route /rss/ is inert?

Could Ghost log this while registering RSSRouter at /rss/ when / is not a channel or collection? Alternatively, could Ghost only register the RSSRouter at /rss/ when root is a channel or collection?

As is, the logs show a route to the RSSRouter at /rss/ with no clues why the route doesn’t invoke the RSS controller.

Why is there is no trace in the log that the registered route /rss/ is inert?

There is no registered top-level /rss/ route. It’s not inert, it simply doesn’t exist.

Could Ghost log this while registering RSSRouter at /rss/ when / is not a channel or collection? Alternatively, could Ghost only register the RSSRouter at /rss/ when root is a channel or collection?

There is no special behaviour for root. The root is by default the only collection. Ghost doesn’t care where routes are registered.

As is, the logs show a route to the RSSRouter at /rss/ with no clues why the route doesn’t invoke the RSS controller.

Again, it’s a relative path, the RSSRouter is always a subrouter. It’s an abstraction over an express router that is always mounted on another router.

The route map you’re looking at is a bit of debug output used during development, if you want to look into making the debug output better, you’re more than welcome to submit a PR :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.