tag:crieit.net,2005:https://crieit.net/tags/JAMStack/feed
「JAMStack」の記事 - Crieit
Crieitでタグ「JAMStack」に投稿された最近の記事
2021-08-29T00:49:34+09:00
https://crieit.net/tags/JAMStack/feed
tag:crieit.net,2005:PublicArticle/17623
2021-08-29T00:49:34+09:00
2021-08-29T00:49:34+09:00
https://crieit.net/posts/gatsby-vercel-microcms-cooprate-20210829-2
Gatsby.js + Vercel + microCMS の JAMStack 環境を構築する
<p><a href="https://crieit.net/posts/gatsby-vercel-microcms-cooprate-20210829-1">microCMS の設定が完了した</a>ので、次は microCMS からデータを取得するように Gatsby.js の方に手を入れます。また、 microCMS と Vercel に設定を加えて連携させ、 Gatsby.js + Vercel + microCMS の JAMStack 環境を構築します。</p>
<h2 id="改修"><a href="#%E6%94%B9%E4%BF%AE">改修</a></h2>
<p>今回は<a target="_blank" rel="nofollow noopener" href="https://www.gatsbyjs.com/starters/renyuanz/leonids">leonids: Gatsby Starter | Gatsby</a>を使用しているので、その前提で。</p>
<p>テーマが変わったり、 microCMS のスキーマが変われば諸々の調整が必要となります。</p>
<h3 id="パッケージの追加"><a href="#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E8%BF%BD%E5%8A%A0">パッケージの追加</a></h3>
<pre><code class="bash">> yarn add yarn add gatsby-source-microcms
## 略
Done in 231.79s.
> yarn add marked
## 略
Done in 7.30s.
</code></pre>
<p>今回は microCMS を利用するので <code>gatsby-source-microcms</code> を追加します。また、本文コンテンツは Markdown で記述しているのですが Gatsby.js の自前の Markdownパーサ まで手が届かなかったので安直に <code>marked</code> を使いました。</p>
<p>それから、内部的には <code>dotenv</code> も使用していますが <code>yarn.lock</code> を見たら既に入っていたので追加はしていません。</p>
<h3 id="gatsby-config.js"><a href="#gatsby-config.js">gatsby-config.js</a></h3>
<p>さて、改修の最初は Gatsby.js の設定から。</p>
<pre><code class="javascript">module.exports = {
// 略
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/blog`,
name: `blog`,
},
},
// 略
},
`gatsby-plugin-feed`,
{
// 略
],
// 略
}
</code></pre>
<p>これを以下のように修正。</p>
<pre><code class="js">const activeEnv = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV
if(activeEnv === "development") {
require("dotenv").config({
path: `.env.${activeEnv}`,
})
}
module.exports = {
// 略
plugins: [
{
resolve: 'gatsby-source-microcms',
options: {
apiKey: process.env.API_KEY,
serviceId: process.env.SERVICE_ID,
apis: [{
endpoint: process.env.APIS_ENDPOINT,
}],
},
},
// 略
{
resolve: `gatsby-plugin-feed`,
options: {
query: `
{
site {
siteMetadata {
title
description
siteUrl
}
}
}
`,
feeds: [
{
serialize: ({ query: { site, allMicrocmsHogeHogeBlog } }) => {
return allMicrocmsHogeHogeBlog.edges.map(edge => {
return Object.assign({}, edge.node, {
description: edge.node.keywords,
date: edge.node.date,
url: site.siteMetadata.siteUrl + edge.node.slug,
guid: site.siteMetadata.siteUrl + edge.node.slug,
custom_elements: [{ "content:encoded": edge.node.body }],
})
})
},
query: `
{
allMicrocmsHogeHogeBlog(sort: {fields: [date], order: DESC}) {
totalCount
pageInfo {
perPage
pageCount
}
edges {
node {
body
createdAt
date
id
keywords
publishedAt
revisedAt
slug
title
updatedAt
}
}
}
}
`,
output: "/rss.xml",
title: "HogeHoge Blog's RSS Feed",
},
],
},
},
// 略
],
// 略
}
</code></pre>
<p>変更点は以下。</p>
<ul>
<li>ローカルのファイルをコンテンツのリソース参照先としていたのを microCMS をリソースとするように修正</li>
<li><code>gatsby develop</code> 時は問題なかったのですが、 <code>gatsby build</code> 時に RSSフィードを生成する <code>gatsby-plugin-feed</code>プラグイン が通常の Markdown の構成をデフォルトにしており、 microCMS に置き換えたことでデータスキーマが異なるとエラーになってしまいました (本番ビルド時に気付いた)。
<ul>
<li>そこで、 <code>gatsby-plugin-feed</code>プラグイン の GraphQL も今回定義した microCMS のスキーマに沿って変更しました。併せて <code>serialize</code>メソッド の中身もスキーマに合わせて調整。</li>
</ul></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.gatsbyjs.com/plugins/gatsby-source-microcms/">gatsby-source-microcms | Gatsby</a>のサンプルコードでは Git 管理下になる <code>gatsby-config.js</code> に APIキー 等の情報がそのままベタ書きになってしまうので、 <code>dotenv</code> で外部ファイルに取り出すことで隠蔽しました
<ul>
<li>後述しますが、 Vercel の環境変数も <code>process.env</code> で渡ってくるので、<code>development</code> の場合は <code>dotenv</code> 経由の外部ファイルをパラメータのリソースに、 <code>production</code> の場合は Vercel の環境変数をリソースとするように書き分けました</li>
</ul></li>
</ul>
<h3 id="gatby-node.js"><a href="#gatby-node.js">gatby-node.js</a></h3>
<pre><code class="javascript">const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogPost = path.resolve(`./src/templates/blog-post.js`)
const result = await graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
`
)
if (result.errors) {
throw result.errors
}
// Create blog posts pages.
const posts = result.data.allMarkdownRemark.edges
posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node
const next = index === 0 ? null : posts[index - 1].node
createPage({
path: post.node.fields.slug,
component: blogPost,
context: {
slug: post.node.fields.slug,
previous,
next,
},
})
})
// Create blog post list pages
const postsPerPage = 5
const numPages = Math.ceil(posts.length / postsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/` : `/${i + 1}`,
component: path.resolve("./src/templates/blog-list.tsx"),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
</code></pre>
<p>これを以下のように修正。</p>
<pre><code class="javascript">const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(
`
{
allMicrocmsHogeHogeBlog(sort: {fields: [date], order: DESC}) {
totalCount
pageInfo {
perPage
pageCount
}
edges {
node {
body
createdAt
date
id
keywords
publishedAt
revisedAt
slug
title
updatedAt
}
}
}
}
`
)
if (result.errors) {
throw result.errors
}
// Create blog posts pages.
const posts = result.data.allMicrocmsHogeHogeBlog.edges
result.data.allMicrocmsHogeHogeBlog.edges.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node
const next = index === 0 ? null : posts[index - 1].node
createPage({
path: post.node.slug,
component: path.resolve('./src/templates/blog-post.js'),
context: {
slug: post.node.slug,
previous,
next,
},
});
});
// Create blog post list pages
const postsPerPage = result.data.allMicrocmsHogeHogeBlog.pageInfo.limit || 10
const numPages = Math.ceil(result.data.allMicrocmsHogeHogeBlog.totalCount / postsPerPage)
console.log(result.data.allMicrocmsHogeHogeBlog.totalCount, postsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/` : `/${i + 1}`,
component: path.resolve("./src/templates/blog-list.tsx"),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `microcmsHogeHogeBlog`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
</code></pre>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.gatsbyjs.com/plugins/gatsby-source-microcms/">gatsby-source-microcms | Gatsby</a>のサンプルコードはシンプル過ぎて GraphQL初心者には分かりづらかったので、 <code>gatsby develop</code> で起動した <code>http://localhost:8000/___graphql</code> の GraphQL を試すGUIでひたすらAPIを叩いてパラメータを変えたりしてどういう GraphQL を作れば望んだレスポンスを得られるか確認しながら地道に調整していきました</li>
<li>ベースのコードをなるべく変えないように調整して、各種プロパティの階層等を調整しました</li>
<li><code>MarkdownRemark</code> や <code>allMarkdownRemark</code> が Markdownファイル をリソースとする場合のキーのような働きをしているのが分かったので、それを <code>http://localhost:8000/___graphql</code> で表示された microCMS のキーに変更したり</li>
</ul>
<p>GraphQL の構造やレスポンスに慣れるまで時間がかかりましたが、最終的には <code>http://localhost:8000/___graphql</code> であれこれ試したのが一番効果がありました。</p>
<h3 id="blog-list.tsx"><a href="#blog-list.tsx">blog-list.tsx</a></h3>
<p>記事一覧ページ。トップページもこのテンプレートから生成されています。</p>
<pre><code class="javascript">// Gatsby supports TypeScript natively!
import React from "react"
import { PageProps, Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"
type PageContext = {
currentPage: number
numPages: number
}
type Data = {
site: {
siteMetadata: {
title: string
}
}
allMarkdownRemark: {
edges: {
node: {
excerpt: string
frontmatter: {
title: string
date: string
description: string
}
fields: {
slug: string
}
}
}[]
}
}
const BlogIndex = ({
data,
location,
pageContext,
}: PageProps<Data, PageContext>) => {
const siteTitle = data.site.siteMetadata.title
const posts = data.allMarkdownRemark.edges
const { currentPage, numPages } = pageContext
const isFirst = currentPage === 1
const isLast = currentPage === numPages
const prevPage = currentPage - 1 === 1 ? "/" : `/${currentPage - 1}`
const nextPage = `/${currentPage + 1}`
return (
<Layout location={location} title={siteTitle}>
<SEO title="All posts" />
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug
return (
<article key={node.fields.slug}>
<header>
<h3
style=<span>{</span><span>{</span>
marginBottom: rhythm(1 / 4),
<span>}</span><span>}</span>
>
<Link style=<span>{</span><span>{</span> boxShadow: `none` <span>}</span><span>}</span> to={node.fields.slug}>
{title}
</Link>
</h3>
<small>{node.frontmatter.date}</small>
</header>
<section>
<p
dangerouslySetInnerHTML=<span>{</span><span>{</span>
__html: node.frontmatter.description || node.excerpt,
<span>}</span><span>}</span>
/>
</section>
</article>
)
})}
<nav>
<ul
style=<span>{</span><span>{</span>
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
<span>}</span><span>}</span>
>
<li>
{!isFirst && (
<Link to={prevPage} rel="prev">
← Previous Page
</Link>
)}
</li>
<li>
{!isLast && (
<Link to={nextPage} rel="next">
Next Page →
</Link>
)}
</li>
</ul>
</nav>
</Layout>
)
}
export default BlogIndex
export const pageQuery = graphql`
query blogPageQuery($skip: Int!, $limit: Int!) {
site {
siteMetadata {
title
}
}
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: $limit
skip: $skip
) {
edges {
node {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
}
}
}
}
}
`
</code></pre>
<p>これを以下のように改修。</p>
<pre><code class="javascript">// Gatsby supports TypeScript natively!
import React from "react"
import { PageProps, Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"
type PageContext = {
currentPage: number
numPages: number
}
type Data = {
site: {
siteMetadata: {
title: string
}
}
allMicrocmsHogeHogeBlog: {
edges: {
node: {
id: string
keywords: string
title: string
updatedAt: string
slug: string
revisedAt: string
publishedAt: string
date: string
createdAt: string
body: string
}
}[]
}
}
const BlogIndex = ({
data,
location,
pageContext,
}: PageProps<Data, PageContext>) => {
const siteTitle = data.site.siteMetadata.title
const posts = data.allMicrocmsHogeHogeBlog.edges
const { currentPage, numPages } = pageContext
const isFirst = currentPage === 1
const isLast = currentPage === numPages
const prevPage = currentPage - 1 === 1 ? "/" : `/${currentPage - 1}`
const nextPage = `/${currentPage + 1}`
return (
<Layout location={location} title={siteTitle}>
<SEO title="All posts" />
{posts.map(({ node }) => {
const title = node.title || node.slug
return (
<article key={node.slug}>
<header>
<h3
style=<span>{</span><span>{</span>
marginBottom: rhythm(1 / 4),
<span>}</span><span>}</span>
>
<Link style=<span>{</span><span>{</span> boxShadow: `none` <span>}</span><span>}</span> to={node.slug}>
{title}
</Link>
</h3>
<small>{node.date}</small>
</header>
<section>
<p
dangerouslySetInnerHTML=<span>{</span><span>{</span>
__html: node.keywords,
<span>}</span><span>}</span>
/>
</section>
</article>
)
})}
<nav>
<ul
style=<span>{</span><span>{</span>
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
<span>}</span><span>}</span>
>
<li>
{!isFirst && (
<Link to={prevPage} rel="prev">
← Previous Page
</Link>
)}
</li>
<li>
{!isLast && (
<Link to={nextPage} rel="next">
Next Page →
</Link>
)}
</li>
</ul>
</nav>
</Layout>
)
}
export default BlogIndex
export const pageQuery = graphql`
query blogPageQuery($skip: Int!, $limit: Int!) {
site {
siteMetadata {
title
}
}
allMicrocmsHogeHogeBlog(
sort: {fields: [date], order: DESC}
limit: $limit
skip: $skip
) {
edges {
node {
body
createdAt
date(formatString: "YYYY/MM/DD")
id
keywords
publishedAt
revisedAt
slug
title
updatedAt
}
}
}
}
`
</code></pre>
<p>ここは大々的な改修というよりは、 GraphQL の変更と、それに併せて変化したデータ構造に合わせてプロパティ名やチェーンを調整した感じです。</p>
<h3 id="blog-post.js"><a href="#blog-post.js">blog-post.js</a></h3>
<pre><code class="javascript">import React from "react"
import { Link, graphql } from "gatsby"
import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm, scale } from "../utils/typography"
const BlogPostTemplate = ({ data, pageContext, location }) => {
const post = data.markdownRemark
// const siteTitle = data.site.siteMetadata.title
const { previous, next } = pageContext
return (
<Layout location={location} title="Home">
<SEO
title={post.frontmatter.title}
description={post.frontmatter.description || post.excerpt}
/>
<article>
<header>
<h1
style=<span>{</span><span>{</span>
marginBottom: 0,
<span>}</span><span>}</span>
>
{post.frontmatter.title}
</h1>
<p
style=<span>{</span><span>{</span>
...scale(-1 / 5),
display: `block`,
marginBottom: rhythm(1),
<span>}</span><span>}</span>
>
{post.frontmatter.date}
</p>
</header>
<section dangerouslySetInnerHTML=<span>{</span><span>{</span> __html: post.html <span>}</span><span>}</span> />
<hr
style=<span>{</span><span>{</span>
marginBottom: rhythm(1),
<span>}</span><span>}</span>
/>
<footer>
<Bio />
</footer>
</article>
<nav>
<ul
style=<span>{</span><span>{</span>
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
<span>}</span><span>}</span>
>
<li>
{previous && (
<Link to={previous.fields.slug} rel="prev">
← {previous.frontmatter.title}
</Link>
)}
</li>
<li>
{next && (
<Link to={next.fields.slug} rel="next">
{next.frontmatter.title} →
</Link>
)}
</li>
</ul>
</nav>
</Layout>
)
}
export default BlogPostTemplate
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!) {
site {
siteMetadata {
title
}
}
markdownRemark(fields: { slug: { eq: $slug } }) {
id
excerpt(pruneLength: 160)
html
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
description
}
}
}
`
</code></pre>
<p>これを以下のように改修。</p>
<pre><code class="javascript">import React from "react"
import { Link, graphql } from "gatsby"
import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm, scale } from "../utils/typography"
import marked from "marked"
const BlogPostTemplate = ({ data, pageContext, location }) => {
const post = data.microcmsHogeHogeBlog
// const siteTitle = data.site.siteMetadata.title
const { previous, next } = pageContext
return (
<Layout location={location} title="Home">
<SEO
title={post.title}
description={post.keywords}
/>
<article>
<header>
<h1
style=<span>{</span><span>{</span>
marginBottom: 0,
<span>}</span><span>}</span>
>
{post.title}
</h1>
<p
style=<span>{</span><span>{</span>
...scale(-1 / 5),
display: `block`,
marginBottom: rhythm(1),
<span>}</span><span>}</span>
>
{post.date}
</p>
</header>
<section dangerouslySetInnerHTML=<span>{</span><span>{</span> __html: marked(post.body) <span>}</span><span>}</span> />
<hr
style=<span>{</span><span>{</span>
marginBottom: rhythm(1),
<span>}</span><span>}</span>
/>
<footer>
<Bio />
</footer>
</article>
<nav>
<ul
style=<span>{</span><span>{</span>
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
<span>}</span><span>}</span>
>
<li>
{previous && (
<Link to={'../'+previous.slug} rel="prev">
← {previous.title}
</Link>
)}
</li>
<li>
{next && (
<Link to={'../'+next.slug} rel="next">
{next.title} →
</Link>
)}
</li>
</ul>
</nav>
</Layout>
)
}
export default BlogPostTemplate
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!) {
site {
siteMetadata {
title
}
}
microcmsHogeHogeBlog(slug: { eq: $slug }) {
id
keywords
title
updatedAt
slug
revisedAt
publishedAt
date(formatString: "YYYY/MM/DD")
createdAt
body
}
}
`
</code></pre>
<p>こちらも <code>blog-list.tsx</code> と同様 GraphQL の変更とそれに伴うプロパティ名やチェーンの調整がメインです。本文の Markdown は冒頭の通り時短のため <code>marked</code> を使いました。</p>
<p>こうして見てみると、 GraphQL に慣れるまでかなり紆余曲折した気がするのですが、 <code>blog-list.tsx</code> や <code>blog-post.js</code> は特に出来上がったコードがあまり元から変化していませんね……。</p>
<h2 id="microCMS で APIキー を参照"><a href="#microCMS+%E3%81%A7+API%E3%82%AD%E3%83%BC+%E3%82%92%E5%8F%82%E7%85%A7">microCMS で APIキー を参照</a></h2>
<p>microCMS で APIキー を控えます。</p>
<p><a href="https://crieit.now.sh/upload_images/949bd68fc9e6b312266c6dd7d831ade5612a583ed1bd6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/949bd68fc9e6b312266c6dd7d831ade5612a583ed1bd6.jpg?mw=700" alt="APIキーの取得" /></a></p>
<p>「サービス設定」→「APIキー」と進みます。今回必要なのは「X-API-KEY」。これを控えておきます。</p>
<h2 id="Vercel の設定"><a href="#Vercel+%E3%81%AE%E8%A8%AD%E5%AE%9A">Vercel の設定</a></h2>
<p>続いて Vercel の設定へ。</p>
<h3 id="環境変数の設定"><a href="#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E8%A8%AD%E5%AE%9A">環境変数の設定</a></h3>
<p>まずは環境変数の設定。</p>
<p><a href="https://crieit.now.sh/upload_images/57a8cc2a08598bf78b631bbd9da2a024612a5842798b2.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/57a8cc2a08598bf78b631bbd9da2a024612a5842798b2.jpg?mw=700" alt="環境変数の設定" /></a></p>
<p>プロジェクトを選択後、「Settings」から「Enviroment Variables」へ。</p>
<p>「NAME」にコードに記載したキーの名前を付けて、実際の値を入力します。画面では APIキー なので上述の microCMS の管理画面で控えた APIキー を入力。</p>
<p><a href="https://crieit.now.sh/upload_images/3bbfd322b71fb32df23894afdd5853d5612a584583337.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3bbfd322b71fb32df23894afdd5853d5612a584583337.jpg?mw=700" alt="環境変数の設定2" /></a></p>
<p>他、必要な変数をセットします。</p>
<h3 id="Deploy Hooks を登録"><a href="#Deploy+Hooks+%E3%82%92%E7%99%BB%E9%8C%B2">Deploy Hooks を登録</a></h3>
<p>次に Deploy Hooks をひっかけます。</p>
<p>プロジェクトの「Settings」から今度は「Git」へ。</p>
<p><a href="https://crieit.now.sh/upload_images/4f89b78a73ad66c2fab07245a0b599ac612a58482a821.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4f89b78a73ad66c2fab07245a0b599ac612a58482a821.jpg?mw=700" alt="Deploy hooks" /></a></p>
<p>Deploy Hooks に名前を入力、ブランチは通常は <code>main</code> になるかと。</p>
<p><a href="https://crieit.now.sh/upload_images/792d34407a2e1061c785101139250868612a584aeed86.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/792d34407a2e1061c785101139250868612a584aeed86.jpg?mw=700" alt="Deploy Hooks を控える" /></a></p>
<p>登録されたら Hook の URL を控えます。</p>
<h2 id="microCMS の設定"><a href="#microCMS+%E3%81%AE%E8%A8%AD%E5%AE%9A">microCMS の設定</a></h2>
<p>お次は microCMS の画面へ。</p>
<p><a href="https://crieit.now.sh/upload_images/16478a5b69bd9cb593abea925cd2c925612a584d45b74.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/16478a5b69bd9cb593abea925cd2c925612a584d45b74.jpg?mw=700" alt="Webhook" /></a></p>
<p>サービスの管理画面から「API設定」→「Webhook」へ。</p>
<p><a href="https://crieit.now.sh/upload_images/192e10d26803bfa0dcf31470a2553f63612a58505802c.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/192e10d26803bfa0dcf31470a2553f63612a58505802c.jpg?mw=700" alt="カスタム通知" /></a></p>
<p>Vercel は一覧にはないので「カスタム通知」を選択。</p>
<p><a href="https://crieit.now.sh/upload_images/b93547fbc6ca4f11e11986ec1869d7d5612a5853d80e0.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b93547fbc6ca4f11e11986ec1869d7d5612a5853d80e0.jpg?mw=700" alt="Vercel の Hook を設定" /></a></p>
<p>名前は任意の名前を。</p>
<p>Hook の URL に先ほど控えた URL を入力します。</p>
<p><a href="https://crieit.now.sh/upload_images/39693fd99f7447504a37a4bfd38afea0612a58568376c.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/39693fd99f7447504a37a4bfd38afea0612a58568376c.jpg?mw=700" alt="Hook の権限" /></a></p>
<p>権限はこのような感じ。基本的に公開されているデータに変更が加わったら Hook が通知を行うものとします。</p>
<h2 id="テスト投稿"><a href="#%E3%83%86%E3%82%B9%E3%83%88%E6%8A%95%E7%A8%BF">テスト投稿</a></h2>
<p><a href="https://crieit.now.sh/upload_images/58704ee832c977a18796e1a3e05c2c8f612a5859db3dd.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/58704ee832c977a18796e1a3e05c2c8f612a5859db3dd.jpg?mw=700" alt="記事を試しに追加" /></a></p>
<p>ここまで設定できたら、試しに記事を新しく追加してみます。</p>
<p><a href="https://crieit.now.sh/upload_images/b79d4f2666aaa6c98f4c010011c12e6f612a585d5f4c4.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b79d4f2666aaa6c98f4c010011c12e6f612a585d5f4c4.jpg?mw=700" alt="Vercel 側が反応" /></a></p>
<p>すると、 Vercel 側でプロジェクトが反応してビルドが走り始めました。</p>
<p><a href="https://crieit.now.sh/upload_images/75c7890bf380c4b476fea57535096623612a586141166.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75c7890bf380c4b476fea57535096623612a586141166.jpg?mw=700" alt="デプロイ完了" /></a></p>
<p>microCMS に記事を追加すると、 Github のリポジトリに push することなく勝手にビルドが走ってサイトが更新されることが確認できました。</p>
<p>これで Gatsby.js + Vercel + microCMS で JAMStack なブログの仕組みが構築できました。実験成功です。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="改修"><a href="#%E6%94%B9%E4%BF%AE">改修</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/k1_style/scraps/07cce0dd3611e3">Gatsby + microcms + Vercel でブログを作って公開したい</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.gatsbyjs.com/plugins/gatsby-source-microcms/">gatsby-source-microcms | Gatsby</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/rokki188/articles/948d53199508c7">microCMS + Next.js でJamStackブログを作ってみた</a></li>
</ul>
<h3 id="コード、GraphQL、microCMSの設定、環境変数"><a href="#%E3%82%B3%E3%83%BC%E3%83%89%E3%80%81GraphQL%E3%80%81microCMS%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%80%81%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0">コード、GraphQL、microCMSの設定、環境変数</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://blog.microcms.io/microcms-next-jamstack-blog/">microCMS + Next.jsでJamstackブログを作ってみよう | microCMSブログ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://blog.microcms.io/gatsby-microcms-media/">GatsbyJS + microCMSでJamstackなオウンドメディアを作ろう | microCMSブログ</a></li>
</ul>
<h3 id="Vercel の環境変数"><a href="#Vercel+%E3%81%AE%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0">Vercel の環境変数</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://vercel.com/docs/environment-variables">Vercel – Environment Variables - Vercel Documentation</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.snorerelax.com/posts/tech-vercel-environment/">Vercelで環境変数を設定する | Next.js Blog Example with すのりら</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Slowhand0309/items/f954237520d343fa9e4c">Vercelで設定した環境変数をNext.jsで使用する - Qiita</a></li>
</ul>
<h4 id="環境変数の分かち書き"><a href="#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E5%88%86%E3%81%8B%E3%81%A1%E6%9B%B8%E3%81%8D">環境変数の分かち書き</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://blog.gaji.jp/2020/06/09/4030/">GatsbyJSで開発環境にのみページを存在させる方法 ++ Gaji-Laboブログ</a></li>
</ul>
<h3 id="gatsby-plugin-feed"><a href="#gatsby-plugin-feed">gatsby-plugin-feed</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.gatsbyjs.com/plugins/gatsby-plugin-feed/">gatsby-plugin-feed | Gatsby</a></li>
</ul>
<p>基本 Gatsby.js のプラグインのページはシンプルなサンプルなので <code>http://localhost:8000/___graphql</code> で GraphQL を試して感覚をつかむ方が早かった気がします。</p>
arm-band