Nuxt.jsでxmlファイルを返す方法【動的にRSSフィードを作成する】

Nuxt.jsでxmlファイルを返す（動的にRSSフィードを作成する）方法になります。

[nuxt/sitemap](https://sitemap.nuxtjs.org/)や[nuxtjs/feed](https://github.com/nuxt-community/feed-module)などのサイトマップやRSSフィード用のライブラリはありますが、今回は使用しません。

nuxtjs/feedでもRSSフィードのxmlファイルを作成できますが、apiなどで動的に情報を書き換えるのは不向きだった為です。


## RSSフィードの生成をする
RSSフィードの生成には、[feed](https://www.npmjs.com/package/feed)というライブラリを使用しました。

```
npm install feed
yarn add feed
```

## ページのasyncDataやmiddlewareでxmlを返すとエラーになる

```typescript:page/demo
<script lang="ts">
import Vue from 'vue'
import { Feed } from 'feed'

export default Vue.extend({
  async asyncData({ res, $axios }) {
    if (process.server) {
      try {
        const feed: any = new Feed({
          title: 'タイトル',
          description: '説明文',
          id: 'http://example.com/',
          link: 'http://example.com/',
          language: 'ja',
          favicon: 'http://example.com/favicon.ico',
          copyright: 'All rights reserved 2021',
          updated: new Date(2021, 2, 14),
          generator: 'awesome'
        })

        const posts = await $axios.get('https://demo.jp')
        if (posts) {
          posts.data.forEach((post) => {
            feed.addItem({
              title: post.title,
              id: post.id,
              link: post.link,
              description: post.description,
              image: post.image
            })
          })
        }
        feed.addCategory('demo')
        feed.addContributor({
          name: 'demo',
          email: 'info@demo.jp',
          link: 'https://demo/'
        })
        res.setHeader('Content-Type', 'application/xml')
        return res.end(feed.rss2())
      } catch (error) {
        console.log(error)
      }
    }
  }
})
</script>
```

![nuxt_xml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/cf7edd64-8708-60a8-1664-744aab3c7187.png)

`/demo`にアクセスすると、xml（RSSフィード）が表示されます。
しかし、下記エラーが出てしまいます。

```
Cannot set headers after they are sent to the client 
```

詳しくは分からないのですが、
`node_modules/@nuxt/server/dist/server.js`で、クライアント側へ既にレスポンスを返却しているからだと思われます。

## serverMiddlewareを使ってxmlファイルを返すようにする

Nuxt.jsのserverMiddlewareを使ってxmlファイルを返すようにしました。

https://ja.nuxtjs.org/docs/2.x/configuration-glossary/configuration-servermiddleware


```nuxt.config.js
serverMiddleware: [{ path: '/api', handler: '@/server/index.ts' }]
```
`nuxt.config.js`に上記の設定を追加します。

`server`ディレクトリを作成して、index.tsファイルを追加します。

```typescript:server/index.ts
import express, { Response } from 'express'
import axios from 'axios'
import { Feed } from 'feed'

const app = express()

app.get('/feed', (res: Response) => {
  try {
    const feed: any = new Feed({
      title: 'タイトル',
      description: '説明文',
      // 同様なので省略
    })

    const posts = await axios.get('https://demo')
    if (posts) {
      posts.data.forEach((post) => {
        feed.addItem({
          title: post.title,
          // 同様なので省略
        })
      })
    }

    feed.addCategory('demo')
    feed.addContributor({
      name: 'demo',
      // 同様なので省略
    })

    res.setHeader('Content-Type', 'application/xml')
    return res.end(feed.rss2())
  } catch (error) {
    res.status(400).json({
      status: 'failed',
      message: error.message
    })
  }
})
export default app
```
`/api/feed`にアクセスすると、無事にエラー無くxml（RSSフィード）が表示されました。