DEVELOPMENT
目次
はじめに
GridsomeはReact製の静的サイトジェネレータのGatsbyを参考に作られた、Vue.jsの静的サイトジェネレータです。
これら静的サイトジェネレーターはHeadlessCMSからGraphQLにてデータを取得し、コード側とデータを切り離して運用することのできるJAMStackの構成を構築するのに不可欠です。
今回これら静的サイトジェネレーターにログイン認証機能が必要になったので、簡単に実装したいと思ったことろ、AWSのAmplifyのAuth(Cognito)を使うのがベストかと思い、導入してみましたが、意外と手こずりましたので、方法をご紹介したいと思います。
前提
- Gridsomeでプロジェクト開始済み
- AWSアカウントを持っている
Gridsome
- ▶ 参考 Gridsomeでプロジェクト作成
- ざっくり、インストール、プロジェクト作成だけです
Amplifyの設定
下記に詳細書いてますが、ざっくりここではコマンドだけ載せておきます。
- ▶ 参考 AWS Amplify とVue-CLIで始める
- ▶ 公式 AWS Amplify Docs
- インスコ
$ npm install -g @aws-amplify/cli
- ここでlocalから、AWSへアクセスするIAMの設定をします。
$ amplify configure
GridsomeへAmplifyを読ませる
ここが苦労しました、Try & Errorの末、入れられました
- はじめにGridsomeのDir構造の紹介
- src/
| - components/ // コンポーネント
| - layouts/
| - Default.vue // 全体のLayoutです
| - pages/ // 各ページのファイルを作成します。
| - Index.vue // トップページ
| - templates/ // ブログ等、localのmarkdownファイルからブログ詳細ページ等の元となるvue component
| - favicon.png
| - main.js // 最初に呼ばれるjsファイル
| - aws-exports.js // Amplify initしてpushすれば出来るファイル 主にCognito のpoolキーなど
- static/
- gridsome.config.js // configファイルで、pluginを読ませたり
- gridsome.server.js // サイトをジェネレートする時の内容等を書きます。contentfulなどのheadlessCMSからデータを取得し、記事分のページを作成する指示もここ
.
.
- main.js にAWSのDocument通り読ませてみる
~
import Amplify, { Auth } from 'aws-amplify';
import awsconfig from './aws-exports';
~
export default function (Vue, { router, head, isClient }) {
Amplify.configure(awsconfig);
~
~
- 一旦走らせてみる
ERROR Failed to compile with 175 errors 16:45:58
error in ./node_modules/@aws-amplify/pubsub/node_modules/graphql/index.mjs
Can't reexport the named export 'BREAK' from non EcmaScript module (only default export is available)
error in ./node_modules/@aws-amplify/pubsub/node_modules/graphql/index.mjs
Can't reexport the named export 'BreakingChangeType' from non EcmaScript module (only default export is available)
error in ./node_modules/@aws-amplify/pubsub/node_modules/graphql/index.mjs
Can't reexport the named export 'DEFAULT_DEPRECATION_REASON' from non EcmaScript module (only default export is available)
error in ./node_modules/@aws-amplify/pubsub/node_modules/graphql/index.mjs
Can't reexport the named export 'DangerousChangeType' from non EcmaScript module (only default export is available)
error in ./node_modules/@aws-amplify/pubsub/node_modules/graphql/index.mjs
- 鬼のエラーです。よく読むと、すべて、mjsファイルだし、graphqlがなんちゃらと。
原因追求
- ▶ 参考 Reactで「Can’t import the named export ‘***’ from non EcmaScript module 」エラーがでる時の対応
-
こちらの記事のお陰で助かりました。
ライブラリ同士が、含まれる.mjsファイルをデフォルトでロードしようとして、かぶっている
ということですので、外部ライブラリのロードを除外しましょう。
rules: [ // fixes https://github.com/graphql/graphql-js/issues/1272
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto'
}
]
これなんですが、Gridsomeの場合、webPackのConfigなんてどこにあるんあろう?
Gridsomeの場合どこに書く?
わからず、ちょっと途方にくれましたが公式に掲載がありました。
- ▶ 公式 Project configuration ConfigureWebpack
- gridsome.config.jsですね
~
~
module.exports = {
~
~
],
configureWebpack: {
module:{
rules: [ // fixes https://github.com/graphql/graphql-js/issues/1272
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto'
}
]
}
}
}
- これでAmplifyのロードが出来るようになりました。
とりあえずLogin画面の表示
どこか適当なページにAmplify提供のAuthのログイン画面を出してみる
- pages/Index.vue
<template>
~
<amplify-authenticator>
<h1>indexページ</h1>
</amplify-authenticator>
~
.
</template>
<script>
import '@aws-amplify/ui-vue'
</script>
- ログインページが表示されたかと思います。
- ログインするとindexページが表示されるという感じですね
Login認証の有無で、ルーティングする
例えば、ログインしていなくても見れるページと、していないと見れないページがあると思います。
この場合、ログイン状態をstore(vuex)でstateを保持して、それにより、ログイン認証が必要なページにアクセスした際、ログインしていなければLoginページに飛ばすなどの処理が必要になると思います。
でも認証が必要なページにひたすら、<amplify-authenticator />を入れるという手も有りにはありですが、ログイン有無で表示が変わるヘッダーのナビゲーションなどの対応もあるので、storeでちゃんと状態を管理し、routerで弾こうと思いました。
でもここで疑問、静的サイトジェネレーターってrouting管理どこでやってんの?
libraryの内部でやってます
厳しい…と思いきや、翌々main.jsを覗いてみると
~
export default function (Vue, { router, head, isClient }) {
~
~
- router って変数あるではないか
- これをいじればどうにか出来そうということでやってみます
- ファイルの中身全部のせます
// This is the main.js file. Import global CSS and scripts here.
// The Client API can be used here. Learn more: gridsome.org/docs/client-api
import DefaultLayout from '~/layouts/Default.vue'
import Buefy from 'buefy'
import 'buefy/dist/buefy.css'
import Amplify from 'aws-amplify'
import '@aws-amplify/ui-vue'
import aws_exports from './aws-exports'
import VueLazyload from 'vue-lazyload' // 画像のlazy load
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import Login from './pages/Login.vue'
import store from './store.js'
// Amplify読み込み
import { AmplifyEventBus, AmplifyPlugin } from 'aws-amplify-vue'
import * as AmplifyModules from 'aws-amplify'
function getUser() {
return Amplify.Auth.currentAuthenticatedUser().then((data) => {
if (data && data.signInUserSession) {
store.commit('setUser', data);
return data;
}
}).catch(() => {
store.commit('setUser', null);
return null;
})
}
export default function (Vue, { router, head, isClient }) {
let user
Amplify.configure(aws_exports)
Vue.use(AmplifyPlugin, AmplifyModules)
// ユーザー管理
getUser().then((user) => {
if (user) {
router.push({path: '/'});
}
})
// ログイン状態管理
AmplifyEventBus.$on('authState', async (state) => {
if (state === 'signedOut'){
user = null;
store.commit('setUser', null);
router.push({path: '/login/'});
}
else if (state === 'signedIn') {
user = await getUser();
router.push({path: '/'});
}
})
// リダイレクト設定
router.beforeResolve(async (to, from, next) => {
if (to.matched.some((record) => {
return record.path == "/cart"
})) {
user = await getUser();
if (!user) {
return next({
path: '/login/'
})
}
return next()
}
return next()
})
// load library.
library.add(fas)
Vue.use(Buefy, { // css framework
defaultIconComponent: 'vue-fontawesome',
defaultIconPack: 'fas',
})
Vue.use(VueLazyload) // 画像のlazy load
Vue.component('font-awesome-icon', FontAwesomeIcon) // font icon
// Set default layout as a global component
Vue.component('Layout', DefaultLayout)
}
-
ここですね
router.beforeResolve(async (to, from, next) => {
- router.beforeResolveでどのパスのときに、認証が必要かのジャッジをして、getUserでログインされてなければ、login/に飛ばすという処理です。
- routingのmetaデータにauthが必要かどうかみたいな処理を良くすると思いますが、静的サイトジェネレーターの場合、セットできないので、name か pathで処理をしようと思いましたが、debugした所、routingのnameキーも持っていなかったので、とりあえずpathで検証してます(今回はcartページが認証してないと見れない設定)
- 前後のコードは、loginの有無をチェックして、状態をstoreに保存してます。
- あと、cssにBuefyのロードと、fontのfont-awesomeのロードをしてます。
- src/store.js 追加
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
user: null
},
mutations: {
// ユーザー情報保存
setUser(state, user) {
state.user = user
},
},
actions: {
},
modules: {
}
})
Loginページを用意する
あとは、ログインページ用のcomponentを用意するだけです。 最悪、以下でもOKじゃないでしょうか
<template>
<amplify-authenticator />
</template>
- 自前のログインページを用意するなら、書いていきますが、ログインやサインアップに必要なメソッドはAmplifyのAuthに定義されてますので、以下を参考にしてください。
- ▶ 参考 Amplify Authenticate Sign up, Sign in & Sign out
- またラインやOpenIDによる認証は以下に書いてます
- ▶ OIDCでCognitoログイン参考 AWS Amplify Auth(Cognito)に Line(OIDC)でログインする方法
まとめ
いかがでしたでしょうか。
- Amplifyのロード
- 静的サイトジェネレーターでのroutingのカスタマイズ
この部分についての対応方法を記載しました。 おそらくGatsbyでも同じ感じで行けるかと思います。というかもう少し楽かもしれません。
明日も頑張りましょう
株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから