Nuxt.jsとmicroCMSとMapTilerとNetlifyとGitHubでJamstackなアクセスマップページをつくってみた

![try-052_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/d532138d-9de7-e063-1d07-1fee1159803f.gif)

![try-052_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/2cc00cca-87a5-1e7d-2f5e-a0beb19c891a.png)

<br>

この記事は、「[Jamstack Advent Calendar 2020](https://qiita.com/advent-calendar/2020/jamstack)」の12日目の記事です。

### Nuxt.jsとmicroCMSとMapTilerとNetlifyとGitHubでJamstackなアクセスマップページをつくってみました :tada:  

今回は、コンテンツ管理者が手軽にアクセスマップページを追加できるJamstackな仕組みをためしてみました！

Jamstackの構成についてはこちらの記事と書籍を参考にさせて頂きました :bow: 

- [microCMS + NuxtでJamstackブログを作ってみよう](https://microcms.io/blog/microcms-nuxt-jamstack-blog)
- [プロフェッショナルWebプログラミングVue.js](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%ABWeb%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-Vue-js-%E5%B1%B1%E7%94%B0-%E5%85%B8%E6%98%8E-ebook/dp/B08N4VYZX6)

<br>

## 1. 事前準備

- Nuxt.jsの環境準備  
    - [v2.14でJamstack用に環境構築](https://day-journal.com/memo/%08nuxt-js-011)
    - node v15.2.0
    - npm v7.0.8
    - Nuxt.js v2.14.6
    - bootstrap-vue v2.17.3

- 各サービスのユーザー登録
    - [microCMS](https://microcms.io)
    - [MapTiler](https://maptiler.jp)
    - [Netlify](https://www.netlify.com)
    - [GitHub](https://github.com)

<br>

## 2. microCMSのAPI設定

はじめに、[microCMS](https://microcms.io)のAPI設定をします。

今回は、API名を「マップ」・エンドポイントを「maps」に設定します。
![](https://day-journal.com/memo/images/try-052_02.png)

APIの型は、「リスト形式」を設定します。
![](https://day-journal.com/memo/images/try-052_03.png)

APIスキーマは、アクセスマップを動的に作成したいので「名称・経度・緯度・ズームレベル」を設定します。
![](https://day-journal.com/memo/images/try-052_04.png)

コンテンツを追加します。
![](https://day-journal.com/memo/images/try-052_05.png)

コンテンツに任意で「名称・経度・緯度・ズームレベル」を入力します。「コンテンツID」も忘れずに任意で設定します。
![](https://day-journal.com/memo/images/try-052_06.png)

サンプルで店舗位置みたいなイメージで3レコード登録しました。APIプレビューで表示してみます。
![](https://day-journal.com/memo/images/try-052_07.png)

JSONでレスポンスされるのが確認できればOKです。ちなみに、「コンテンツID」が各ページのURLパスになります。
![](https://day-journal.com/memo/images/try-052_08.png)

**これだけで更新情報のAPI設定は完了です** :thumbsup: 

<br>

## 3. MapTilerの設定

アクセスマップには、[MapTiler](https://maptiler.jp)のStatic Maps APIを利用して地図画像を取得してみました。

このようなURLを指定することで、手軽に1枚のマップ画像を取得できるので便利です。

```text
https://api.maptiler.com/maps/jp-mierune-streets/static/{lon},{lat},{zoom}/{width}x{height}.png?key=[API_KEY]
```
※MapTilerの基本機能はある程度は無料ですが、Static Maps APIについては有料オプションになります :pray: 

![](https://day-journal.com/memo/images/try-052_09.png)

マップアプリケーションとして表示してみたいかたは、こちらの記事を参考にしていただければと思います。
[色々なマップライブラリでMapTilerを表示してみた](https://qiita.com/dayjournal/items/8fe865169e89c8bad343)

<br>

## 4. Nuxt.jsでアプリケーション構築

次に、[Nuxt.js](https://ja.nuxtjs.org)で動的にルーティングするアクセスマップページを構築します。
さきほどのMapTilerについてもここで埋め込む処理を記述します。

今回は、ひな形からの改修は「nuxt.config.js」「_slug.vue」「.env」の3ファイルくらいです :bulb: 

#### package.json

```json
{
  "name": "sample_prj",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
    "lint": "npm run lint:js && npm run lint:style"
  },
  "dependencies": {
    "@nuxtjs/axios": "^5.12.2",
    "bootstrap": "^4.5.2",
    "bootstrap-vue": "^2.17.3",
    "core-js": "^3.6.5",
    "nuxt": "^2.14.6"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^3.1.0",
    "@nuxtjs/eslint-module": "^2.0.0",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^7.10.0",
    "eslint-config-prettier": "^6.12.0",
    "eslint-plugin-nuxt": "^1.0.0",
    "eslint-plugin-prettier": "^3.1.4",
    "prettier": "^2.1.2",
    "stylelint": "^13.7.2",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^20.0.0"
  }
}
```

<br>

#### .env

```text
BASE_URL=http://localhost:3000
MICROCMS_API_URL=https://xxxxxxxxx.microcms.io/api/v1/maps/
MICROCMS_API_KEY=xxxxxxxxxx
MAPTILER_API_URL=https://api.maptiler.com/maps/jp-mierune-streets/static/
MAPTILER_API_KEY=xxxxxxxxxx
```

microCMSのURLとAPIキーを設定します。

```text
MICROCMS_API_URL=https://xxxxxxxxx.microcms.io/api/v1/maps/
MICROCMS_API_KEY=xxxxxxxxxx
```

MapTilerのURLとAPIキーを設定します。

```text
MAPTILER_API_URL=https://api.maptiler.com/maps/jp-mierune-streets/static/
MAPTILER_API_KEY=xxxxxxxxxx
```

<br>

#### nuxt.config.js

```javascript
import axios from 'axios'
const { MICROCMS_API_URL, MICROCMS_API_KEY, MAPTILER_API_URL, MAPTILER_API_KEY } = process.env;

export default {
  // 環境変数設定
  privateRuntimeConfig: {
    MicroCmsApiKey: MICROCMS_API_KEY,
    MaptilerApiKey: MAPTILER_API_KEY,
    MicroCmsApiUrl: MICROCMS_API_URL,
    MaptilerApiUrl: MAPTILER_API_URL
  },
  publicRuntimeConfig: {
  },

  // 動的ルーティング出力設定
  generate: {
    async routes() {
      const pages = await axios
        .get(MICROCMS_API_URL, {
          headers: { 'X-API-KEY': MICROCMS_API_KEY }
        })
        .then((res) =>
          res.data.contents.map((content) => ({
            route: `/${content.id}`,
            payload: content
          }))
        )
      return pages
    }
  },

  // Target (https://go.nuxtjs.dev/config-target)
  target: 'static',

  // Global page headers (https://go.nuxtjs.dev/config-head)
  head: {
    title: 'sample_prj',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  // Global CSS (https://go.nuxtjs.dev/config-css)
  css: [],

  // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
  plugins: [],

  // Auto import components (https://go.nuxtjs.dev/config-components)
  components: true,

  // Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
  buildModules: [
    // https://go.nuxtjs.dev/eslint
    '@nuxtjs/eslint-module',
    // https://go.nuxtjs.dev/stylelint
    '@nuxtjs/stylelint-module',
  ],

  // Modules (https://go.nuxtjs.dev/config-modules)
  modules: [
    // https://go.nuxtjs.dev/bootstrap
    'bootstrap-vue/nuxt',
    // https://go.nuxtjs.dev/axios
    '@nuxtjs/axios',
  ],

  // Axios module configuration (https://go.nuxtjs.dev/config-axios)
  axios: {},

  // Build Configuration (https://go.nuxtjs.dev/config-build)
  build: {},
}
```

.envで設定した環境変数を読み込みます。

```javascript
// 環境変数設定
privateRuntimeConfig: {
  MicroCmsApiKey: MICROCMS_API_KEY,
  MaptilerApiKey: MAPTILER_API_KEY,
  MicroCmsApiUrl: MICROCMS_API_URL,
  MaptilerApiUrl: MAPTILER_API_URL
},
publicRuntimeConfig: {
},
```

動的ルーティングの静的ファイル出力の設定をします。

```javascript
// 動的ルーティング出力設定
generate: {
  async routes() {
    const pages = await axios
      .get(MICROCMS_API_URL, {
        headers: { 'X-API-KEY': MICROCMS_API_KEY }
      })
      .then((res) =>
        res.data.contents.map((content) => ({
          route: `/${content.id}`,
          payload: content
        }))
      )
    return pages
  }
},
```

<br>

/pages

#### _slug.vue

```vue
<template>
  <main class="container">
    <b-container>
      <b-row class="text-center">
        <b-col sm="12" class="mt-5 mb-3">
          <!-- 名称 -->
          <h4>{{ name }}</h4>
          <hr />
        </b-col>
        <b-col sm="12">
          <!-- 案内図 -->
          <b-img :src="url"></b-img>
        </b-col>
      </b-row>
    </b-container>
  </main>
</template>

<script>
import axios from 'axios'

export default {
  async asyncData({ params, $config }) {
    // API取得
    const { data } = await axios.get(
      `${$config.MicroCmsApiUrl}${params.slug}`,
      {
        headers: { 'X-API-KEY': $config.MicroCmsApiKey },
      }
    )
    // 案内図URL
    const url = `${$config.MaptilerApiUrl}${data.lng},${data.lat},${data.zoom}/750x750.png
?key=${$config.MaptilerApiKey}&markers=${data.lng},${data.lat}`
    data.url = url
    return data
  },
}
</script>
```

店舗名称を表示します。

```html
<!-- 名称 -->
<h4>{{ name }}</h4>
```

アクセスマップを表示します。

```html
<!-- 案内図 -->
<b-img :src="url"></b-img>
```

microCMSからデータを取得します。

```javascript
// API取得
const { data } = await axios.get(
  `${$config.MicroCmsApiUrl}${params.slug}`,
  {
    headers: { 'X-API-KEY': $config.MicroCmsApiKey },
  }
)
```

MapTilerのマップ画像を取得するURLを生成します。

```javascript
// 案内図URL
const url = `${$config.MaptilerApiUrl}${data.lng},${data.lat},${data.zoom}/750x750.png
?key=${$config.MaptilerApiKey}&markers=${data.lng},${data.lat}`
data.url = url
```

<br>

**これだけで表示用のプロジェクト構築は完了です** :thumbsup: 

<br>

## 5. GitHubにpush

次に、[GitHub](https://github.com)にリポジトリを作成してNuxt.jsのプロジェクト一式をプッシュします。
![](https://day-journal.com/memo/images/try-052_10.png)

<br>

## 6. NetlifyとGitHubの連携

次に、[Netlify](https://www.netlify.com)と[GitHub](https://github.com)を連携してプッシュ時に自動でビルドとデプロイをする設定をします。

NetlifyとGitHubとの連携設定をします。
![](https://day-journal.com/memo/images/try-052_11.png)
![](https://day-journal.com/memo/images/try-052_12.png)
![](https://day-journal.com/memo/images/try-052_13.png)
![](https://day-journal.com/memo/images/try-052_14.png)
![](https://day-journal.com/memo/images/try-052_15.png)

さきほどプッシュしたリポジトリを設定します。
![](https://day-journal.com/memo/images/try-052_16.png)
![](https://day-journal.com/memo/images/try-052_17.png)

ビルド対象の「ブランチ名」と「ビルドコマンド」と「出力先ディレクトリ」を設定します。
![](https://day-journal.com/memo/images/try-052_18.png)

.envファイルと同じ環境変数を設定後、初回デプロイをします。
![](https://day-journal.com/memo/images/try-052_19.png)
![](https://day-journal.com/memo/images/try-052_20.png)

初回デプロイ完了後に、サイトURLにアクセスできるようになります。
![](https://day-journal.com/memo/images/try-052_21.png)

「コンテンツID」を指定することで、アクセスページが切り替わることが確認できます。
![](https://day-journal.com/memo/images/try-052_22.png)

**これで、開発者がGithubでプッシュしたら自動でビルドとデプロイされ静的ファイルが配信される仕組みができあがりました** :thumbsup: 

<br>

## 7. NetlifyとmicroCMSの連携

次に、[Netlify](https://www.netlify.com)と[microCMS](https://microcms.io)を連携してコンテンツ更新時に自動でビルドとデプロイをする設定をします。

NetlifyでmicroCMSからWebhookするためのURLを取得します。
![](https://day-journal.com/memo/images/try-052_23.png)
![](https://day-journal.com/memo/images/try-052_24.png)
![](https://day-journal.com/memo/images/try-052_25.png)
![](https://day-journal.com/memo/images/try-052_26.png)
![](https://day-journal.com/memo/images/try-052_27.png)

Netlifyで取得したURLをmicroCMSで設定します。
![](https://day-journal.com/memo/images/try-052_28.png)
![](https://day-journal.com/memo/images/try-052_29.png)
![](https://day-journal.com/memo/images/try-052_30.png)

**これで、コンテンツ管理者がmicroCMSでコンテンツを更新したら自動でビルドとデプロイされ静的ファイルが配信される仕組みができあがりました** :thumbsup: 

<br>

仕組みができたので、最後にコンテンツをいくつか登録して自動でビルドとデプロイができるかをためしてみます。
![](https://day-journal.com/memo/images/try-052_31.png)

コンテンツを更新すると、自動でビルドとデプロイがされるステータスになります。
![](https://day-journal.com/memo/images/try-052_32.png)

<br>
       
デプロイ完了後にサイトにアクセスすると、追加した分のアクセスマップが適用されているのが確認できます。(小さな北海道色々ありますね :map: )
![](https://day-journal.com/memo/images/try-052_33.png)
![](https://day-journal.com/memo/images/try-052_34.png)

**すべて表示するとこんな感じです** :thumbsup: 

![](https://day-journal.com/memo/images/try-052_01.gif)

<br>

#### Nuxt.jsとmicroCMSとMapTilerとNetlifyとGitHubでJamstackなアクセスマップページを構築することができました :thumbsup: 

自分の[ブログ](https://day-journal.com/memo)では現在[Hexo](https://hexo.io)を利用してローカルでMarkdownを書いて本番環境にデプロイしているのですが、今後よりJamstackな構成で再構築してみたかったので色々とためせてよかったです :grinning: 

自動でビルドとデプロイをする環境を構築できたことで、手軽にコンテンツの更新やアプリケーションの更新ができるのが便利だと思いました。しかも、最終的に静的ファイルで配信されるので表示速度やセキュリティ的にも安心です。

今後の課題としては、画像がまだURL参照になってしまってるので、事前にビルドする仕組みにしたらすべて静的ファイルで配信できそうです :bulb:

<br>

Nuxt.js・MapTilerについて、他にも記事を書いています。よろしければぜひ :bow:
<a href="https://day-journal.com/memo/tags/Nuxt-js" target="_blank">tags - Nuxt.js</a>
<a href="https://qiita.com/dayjournal/items/8fe865169e89c8bad343" target="_blank">色々なマップライブラリでMapTilerを表示してみた</a>

やってみたシリーズ :grinning:
<a href="https://day-journal.com/memo/tags/Try" target="_blank">tags - Try</a>

<br>
<br>

<a href="https://dayjournal.booth.pm" target="_blank"><img src="https://day-journal.com/memo/images/etc/book.png" alt="book"></a>