strapi+nuxt.jsでアドベントカレンダーを作ろう

## strapiを触ってみよう

巷でウワサのheadlessCMS「strapi」を触ってみました。

アドベントカレンダーの記事ということで、簡単なアドベントカレンダーを作ってみます。
記事表示の部分はnuxt.jsを利用します。

### strapiの構築

npxコマンド一発で簡単に初期構築を行ってくれます。

```
npx create-strapi-app advent_api --quickstart
```

ユーザ登録を済ませたら、記事用のコンテンツタイプ（データ定義）を作成します。
項目の名前とデータ型、その他設定（一意制約や必須など）を選択することで簡単にデータ項目を増やしていくことができます。

![スクリーンショット 2020-12-01 12.04.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287153/42ec2059-c107-bee7-2b28-25bf6c82dc5f.png)


コンテンツタイプを作成すると、下記の基本的なAPIが自動で生成されます。

 - 一覧
 - 件数
 - 詳細
 - 追加
 - 更新
 - 削除

この段階ではAPIのアクセスが許可されていないので、デフォルトで存在するpublicロールに、取得系APIのアクセス権限を設定してあげます。

![スクリーンショット 2020-12-01 13.03.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287153/132798a8-931e-0b9c-6049-65dca97f41df.png)

試しに記事を２件ほど書いてみます（投稿もAPI経由で行うことができますが、時間の都合上デフォルトの管理画面上で作成します）

![スクリーンショット 2020-12-01 12.07.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287153/65d44997-5e94-ec0d-f7c8-d75421a3a388.png)


この状態で、自動生成された記事一覧のAPIにアクセスしてみると...

```
http://localhost:1337/articles/
```

記事情報のjsonが取得できました。

```
[
  {
    "id": 1,
    "title": "シュトレンを作ってみた",
    "text": "## シュトレンとは\n\nシュトレンというお菓子を知っていますか？\nシュトレンとは・・・",
    "date": "2020-12-01",
    "published_at": "2020-11-30T12:16:18.523Z",
    "created_at": "2020-11-30T12:16:15.854Z",
    "updated_at": "2020-12-01T03:07:40.608Z"
  },
  {
    "id": 2,
    "title": "アドベントカレンダーを作ろう",
    "text": "## はじめに\n\n**アドベントカレンダー**作りたい... 作りたくない？\n",
    "date": "2020-12-02",
    "published_at": "2020-11-30T12:17:53.678Z",
    "created_at": "2020-11-30T12:17:51.696Z",
    "updated_at": "2020-12-01T03:06:19.338Z"
  }
]
```

### Viewの作成（Nuxt.js）

APIから取得したデータを表示する部分はnuxt.jsで作ります。
こちらもコマンド一発で初期構築。
対話式で利用するプラグインなどを設定できるので、
CSSフレームワークとして[Vuetify](https://vuetifyjs.com/) を導入します。

```
npx create-nuxt-app advent_front
```

/pages/配下に、記事一覧ページと記事詳細ページを作ります。

#### 記事一覧

Vuetifyにはカレンダー用のmoduleが用意されているのでこれを利用します。
apiから取得したデータを詰め替えて、カレンダーのイベントとして登録していきます。

```index.vue
<template>
  <v-container>
    <v-sheet height="80vh">
      <v-calendar light height="80vh" start="2020-12-01" end="2020-12-25" :events="events" @click:event="showEvent"></v-calendar>
    </v-sheet>
  </v-container>
</template>

<script>
import axios from 'axios'

export default {
  data() { return {
    events:[]
  }},
  methods: {
    showEvent ({ nativeEvent, event }) {
      this.$router.push(`/${event.id}`)
    }
  },
  async mounted() {
    const {data} = await axios.get("/api/articles/");
    this.events = data.map(el=>{
      return {
        id: el.id,
        start: new Date(el.date),
        name: el.title,
        timed: false,
      }
    })
  }
}
</script>

```

#### 記事詳細

nuxtはファイル名から自動でルーティングを生成してくれますが、ファイル名の先頭に_をつけた場合パスパラメータとして認識され、```$route.params```で取得することが可能です。
また、本文はmarkedライブラリを利用してmarkdownからhtmlに変換しています。

```_id.vue
<template>
  <v-container>
    <v-sheet height="80vh" class="pa-3">
      <div tag="h2" class="headline" v-html="article.title"></div>
      <div v-html="article.date" class="mb-3"></div>
      <v-sheet v-html="article.text">
      </v-sheet>
      <v-btn class="mt-5" to="/">カレンダーに戻る</v-btn>
    </v-sheet>
  </v-container>
</template>

<script>
import axios from 'axios'
import marked from 'marked'

export default {
  data() { return {
    article: {
      title: "hoge",
      date: "2020-12-01",
      text: "hogehoge",
    }
  }},
  props:["id"],
  methods: {
    md2Html(val) {
      return marked(val);
    }
  },
  async mounted() {
    const {data} = await axios.get("/api/articles/"+this.$route.params.id);
    this.article = {
      title: data.title,
      date : data.date,
      text : this.md2Html(data.text)
    }
  }
}
</script>

```

今回はstrapi,nuxtそれぞれlocalhostの別portで動かす（別ドメイン扱いになる）ため、nuxtでproxy設定を行うことでクロスオリジン制約を回避します。

```nuxt.config.js
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
  ],
  proxy: {
    '/api': {
    target: 'http://localhost:1337',
    pathRewrite: {
      '^/api' : '/'
      }
    }
  },
```

これでこのように表示されます。見た目ダサいのは気にしない

```
http://localhost:3000/
```

![スクリーンショット 2020-12-01 13.02.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287153/34ee165d-4ffd-2568-2714-a382e287ae45.png)

```
http://localhost:3000/1/
```

![スクリーンショット 2020-12-01 13.03.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287153/31664fab-d550-1884-ca85-5e3e775886f0.png)


### 完走した感想

実際に利用するには権限周りの設定などもう少し作り込む必要がありますが、それでもstrapi+nuxtの組み合わせはかなり簡単に初期構築を行えるなと感じました。
今回のような超小規模だとWordpressでよくね？となってしまう感はあるものの、

- Nuxtできちんとサーバサイドレンダリングをする
- 並行してアプリ化もする

など、headlessCMSの強みを活かせるような要件では強力だと思います。


---
[:christmas_tree: FORK Advent Calendar 2020](/advent-calendar/2020/fork)
:arrow_left: 1日目 [Google Cloud Dataproc でデータ解析基盤を構築](/numatch00/items/cfc599ae033dd67a5273) @numatch00
:arrow_right: 3日目 [WSL2環境でVSCode + Dockerで開発する為に](/sugar-engine/items/81f55fb7fa1f1bd12955) @sugar-engine