いよいよNotion APIを使ってみる回になりました\(^o^)/
Notion APIを使った実装はネットに沢山ありますが、
私はなかなか習得することができずにいました。
原因は、
「何を根拠にしたらそのコードになるのか.....?」
って部分。
公式リファレンスを参照しながら実装していくのはいいものの、
「このコードってリファレンスのどの部分を根拠にしてるの?」
と細かい部分で詰まっていました。
Blog learn用にeasy-notion-blogのコードをなるべくシンプルに実装していく過程で、
「あ、これがあそこに書いてあったサンプルか💡」
「これ、さっきのあの変数とつながってるやつじゃん😀」
といろいろ手探りながらつかめてきました。
今回は
公式リファレンスのどこを根拠に記述しているのか
といった部分にこだわって簡単なDBデータをブログに表示させる過程を解説していきます。
title・text・dateタイプの列のデータを表示させていきます\(^o^)/
実装の際に使ったコードはこちらに収納していますー。
No. | branch | description | リンク | PR |
---|---|---|---|---|
8 | 9cp6j7 | NotionAPIからDBのタイトル・テキスト・日付列を表示させる_他_lib/環境変数module/getStaticProps/props | https://github.com/herohoro/Blog_learn/pull/11 | #11 |
https://github.com/herohoro/Blog_learn/wiki
コードについて触れる前に、
Notion上で用意しておくデータベースやAPIを準備しておきますー。
Name列→RenameでTitleに変更
Tags列→Edit propertyから列名TagsをExcerpt→Typeをtextに変更
列追加→列名PropertyをDate→TypeをDateに変更
データベースの名前や各列に記入したテキストはお好みでOK(*´∀`*)
https://developers.notion.com/docs/getting-started#step-1-create-an-integration
https://developers.notion.com/docs/getting-started#step-2-share-a-database-with-your-integration
データベースID
DATABASE_ID
という環境変数に入れますー。(あとで触れます)API トークンキー
Internal Integration Token
NOTION_API_SECRET
という環境変数に入れますー。(これも後で触れます)用意したNotionデータベースを読み込む大まかな流れ
.env.local
ファイルをtop階層に設置
NOTION_API_SECRET=
DATABASE_ID=
👆ここに準備の段階で取得した文字列を記入(「”」でくくらずそのまま転記でOK)
setting/Environment Variables から登録
サイドバーServer Control Panel/Secret Keys から登録
const NOTION_API_SECRET = process.env.NOTION_API_SECRET;
const DATABASE_ID = process.env.DATABASE_ID;
module.exports = {
NOTION_API_SECRET,
DATABASE_ID
};
公式リファレンスではmoduleにせずprocess.env.NOTION_API_SECRET
を直接記述したコードだけど、
easy-notion-blogではmoduleにしていざ使う場面では NOTION_API_SECRET
と記述するのみ。
環境変数をよく理解できていない私でも
process.env
が無いってだけで「これはAPIのトークンキーのことかな」
ってなんとなく把握できたのですごく助かりましたm(_ _)m
※ 本家ではnotion関連のファイルをlib/notionの中に収納していますが、今回はそこまで複雑な実装はしないのでlibのフォルダ内に入れていきますー
export interface Post {
PageId: string;
Title: string;
Excerpt: string;
Date: string;
}
https://developers.notion.com/reference/database
公式リファレンスのDatabase objectにある表が手がかり!!!
Type列にある記述がデータの型として定義する文字。
必要なPropertyは......ひとまずpropertiesかな???
Type列に「object」とあるのでいろいろネストされてるっぽい。。。。
key
が列名に表示されている文字列
ってことは、データベースを用意する時に列名にしたTitle/Excerpt/Date のこと。
value
はProperty objectを参照せよ
ってことなので、リンクを踏むと......(踏めなかったので普通に開くと.....)
https://developers.notion.com/reference/property-object
Typeはstringってことを確認!!!
export interface Post {
PageId: string;
Title: string;👉納得
Excerpt: string;👉Textじゃないの??rich_textって初耳....
Date: string;👉納得
}
Text propertyについては....
Database propertiesの表の下にpropertyの種類ごとに説明がある。
export interface Post {
PageId: string;👉これはなんだ?
Title: string;👉納得
Excerpt: string;👉納得
Date: string;👉納得
}
PageIDはDatabaseのデータを取得する時に触れるのでひとまず保留。。。。。
//型定義
import { Post } from "./interfaces";
//環境変数
import { NOTION_API_SECRET, DATABASE_ID } from "./sever-containts";
const { Client } = require("@notionhq/client");
const client = new Client({
auth: NOTION_API_SECRET
});
export async function getPosts() {
const params = {
database_id: DATABASE_ID
};
const data = await client.databases.query(params);
return data.results.map((item) => _buildPost(item));
}
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
Title: prop.Title.title[0].plain_text,
Excerpt: prop.Excerpt.rich_text[0].plain_text,
Date: prop.Date.date.start
};
return post;
}
いきなり見ても辛いので、少しずつ解読していきます.....💃
▼
https://developers.notion.com/reference/authentication
予め環境変数をmoduleにしているのでサンプルよりシンプル!
import { NOTION_API_SECRET, DATABASE_ID } from "./sever-containts";
const { Client } = require("@notionhq/client");
const client = new Client({
auth: NOTION_API_SECRET
});
Gets a list of Pages contained in the database,
データベースはページ一覧を含んでる.....
データベースのタイトルをクリックすると表示されるページのことを意味している......😲
このpageについても取得できるように型定義しないとな.....
っていうのがさっき保留にしたPageIDの謎でした。
https://developers.notion.com/reference/page#all-pages
export interface Post {
PageId: string;👉解決!!
Title: string;👉納得
Excerpt: string;👉納得
Date: string;👉納得
}
今回APIの実装を理解するためになるべくシンプルな記述にすべく、
FilterやSortはひとまず外して実装していきますーーーー🕺🏻🕺🏻🕺🏻🕺🏻🕺🏻🕺🏻🕺🏻
const { Client } = require('@notionhq/client');
const notion = new Client({ auth: process.env.NOTION_API_KEY });
(async () => {
const databaseId = '897e5a76-ae52-4b48-9fdf-e71f5945d1af';
const response = await notion.databases.query({
database_id: databaseId,
});
console.log(response);
})();
こんな感じでシンプルになってしまいました.....😋
このサンプルを元に実装しようとすると.....
//環境変数
import { NOTION_API_SECRET, DATABASE_ID } from "./sever-containts";
const { Client } = require("@notionhq/client");
const client = new Client({
auth: NOTION_API_SECRET
});
export async function getPosts() {
const params = {
database_id: DATABASE_ID
};
const data = await client.databases.query(params);
return data.results.map((item) => _buildPost(item));
}
returnの部分は複数取得したデータベースの行を1行ずつ仕分けしていく操作をしています。
_buildPost
って何?easy-notion-blogのlib/client.tsを覗くと思考停止しそうなexport async function
がたくさんあり、
なかでも、export async functionで使うためのfunctionが_◯◯◯◯
という名前で末尾に登場します。
今回、データベースを取得するために必要な_buildPost
関数を追加して
1行ずつ仕分けができるようにしていこうと思います\(^o^)/
return data.results.map((item) => _buildPost(item));
}
//このあと.....
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
Title: prop.Title.title[0].plain_text,
Excerpt: prop.Excerpt.rich_text[0].plain_text,
Date: prop.Date.date.start
};
return post;
}
データベースのPropertyの一部
https://developers.notion.com/reference/database#all-databases
Property | |
---|---|
object | |
id | 👌PageID |
created_time | |
created_by | |
last_edited_time | |
last_edited_by | |
title | |
icon | |
properties | 👉これ |
parent | |
url | |
archived |
列名として用意したTitle/Excerpt/Dateに記入したデータを取得するには
propertiesの子要素に収納されている💨
https://developers.notion.com/reference/property-item-object#title-property-values
https://developers.notion.com/reference/rich-text
{
"Name": {👉これが列名に表示されている(サンプルではNameという列名)
"object": "list",
"results": [
{
"object": "property_item",
"id": "title",
// ここからrich text
"type": "title",👉これがpropertyの種類
"title": {
"type": "text",//ここはrich textのProperty type
"text": {
"content": "The title",
"link": null
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": "The title",👉これが列に記入したデータ
"href": null
// ここまでrich text
}
}
],
"next_cursor": null,
"has_more": false,
"type": "property_item",
"property_item": {
"id": "title",
"next_url": null,
"type": "title",
"title": {}
}
}
}
https://developers.notion.com/reference/property-item-object#title-property-values
列名.propertyの種類.plain_text
で取得return data.results.map((item) => _buildPost(item));
}
//このあと.....
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
Title: prop.Title.title[0].plain_text,//👉納得
Excerpt: prop.Excerpt.rich_text[0].plain_text,
Date: prop.Date.date.start
};
return post;
}
https://developers.notion.com/reference/property-item-object#rich-text-property-values
{
"Details": {👉これが列名に表示されている(サンプルではDetailsという列名)
"object": "list",
"results": [
{
"object": "property_item",
"id": "NVv%5E",
// ここからrich text
"type": "rich_text",👉これがpropertyの種類
"rich_text": {
"type": "text",//ここはrich textのProperty typeの種類
"text": {
"content": "Some more text with ",
"link": null
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": "Some more text with ",👉これが列に記入したデータ
"href": null
// ここまでがrich text
}
},
// 複数行記載があればリピート
],
"next_cursor": null,
"has_more": false,
"type": "property_item",
"property_item": {
"id": "NVv^",
"next_url": null,
"type": "rich_text",
"rich_text": {}
}
}
}
列名.propertyの種類.plain_text
で取得return data.results.map((item) => _buildPost(item));
}
//このあと.....
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
Title: prop.Title.title[0].plain_text,//👉納得
Excerpt: prop.Excerpt.rich_text[0].plain_text,//👉納得
Date: prop.Date.date.start
};
return post;
}
https://developers.notion.com/reference/property-item-object#date-property-values
{
"Shipment Time": {👉列名
"object": "property_item",
"id": "i%3Ahj",
"type": "date",👉propertyの種類
"date": {
"start": "2021-05-11T11:00:00.000-04:00",👉これを取得したい
"end": null,
"time_zone": null
}
}
}
列名.propertyの種類.start
で取得return data.results.map((item) => _buildPost(item));
}
//このあと.....
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
Title: prop.Title.title[0].plain_text,//👉納得
Excerpt: prop.Excerpt.rich_text[0].plain_text,//👉納得
Date: prop.Date.date.start//👉納得
};
return post;
}
2021-05-11T11:00:00.000-04:00
これじゃ見にくい......見やすくするために....
easy-notion-blogにはblog-helper.tsというファイル内に記述がありました(*´ω`*)(*´ω`*)
export const getDateStr = date => {
const dt = new Date(date)
const y = dt.getFullYear()
const m = ('00' + (dt.getMonth() + 1)).slice(-2)
const d = ('00' + dt.getDate()).slice(-2)
return y + '-' + m + '-' + d
}
今回これもlibに追加していざ呼び出す時に使います〜。
//型定義
import { Post } from "./interfaces";
//環境変数
import { NOTION_API_SECRET, DATABASE_ID } from "./sever-containts";
const { Client } = require("@notionhq/client");
const client = new Client({
auth: NOTION_API_SECRET
});
export async function getPosts() {
const params = {
database_id: DATABASE_ID
};
// dataにnotionDBの情報を丸っと放り投げる
const data = await client.databases.query(params);
// 列名ごと必要な情報を残していく
return data.results.map((item) => _buildPost(item));
}
function _buildPost(data) {
const prop = data.properties;
const post: Post = {
PageId: data.id,
// keyは列名 valueはproperty value object
// property value objectは「property item object」のリファレンス参照
Title: prop.Title.title[0].plain_text,
Excerpt: prop.Excerpt.rich_text[0].plain_text,
Date: prop.Date.date.start
};
return post;
}
componentで一旦作っていつでも貼り付けられるように仕込みます。
import * as interfaces from "../lib/interfaces";
import { getDateStr } from "../lib/blog-helpers";
//client.tsで用意したpostデータを取り出していく
// postの中にある.... っていった感じで引数にpostを入れる
export const PostDate = ({ post }) => <div>{getDateStr(post.Date)}</div>;
export const PostTitle = ({ post }) => {
const postTitle = post.Title ? post.Title : "";
return <h3>{postTitle}</h3>;
};
export const PostExcerpt = ({ post }) => (
<div>
<p>{post.Excerpt ? post.Excerpt : ""}</p>
</div>
);
client.tsで作っておいたpost
が引っ張りだこ🕸️
postには.....
が収納されていて、使いたい場面でpost.〇〇で指定すれば出てきてくれます🧞♂️
getDateStr
もここで仕事をするんだね~あとは三項演算子で「あるの?無いの?ハッキリして(# ゚Д゚)」と問いかける。
準備が整ったのでcomponentを使って表示させていきますー。
import { PostDate, PostTitle, PostExcerpt } from "../../components/blog-parts";
import styles from "../../styles/blog.module.css";
import { getPosts } from "../../lib/client";
export async function getStaticProps() {
// clientにある関数をPenderPostsの引数にして活用
const posts = await getPosts();
return {
props: { posts },
revalidate: 60
};
}
const RenderPosts = ({ posts = [] }) => {
return (
<div className={styles.container}>
<div className={styles.mainContent}>
<p>***main-content***</p>
<h2>BlogList page</h2>
<br />
{""}
<section>
<div>
{/* 変数postsを1つの塊ごと取り出していく */}
{posts.map((post) => {
return (
<div key={post.Date}>
<PostDate post={post} />
<PostTitle post={post} />
<PostExcerpt post={post} />
</div>
);
})}
</div>
</section>
<br />
</div>
<div className={styles.subContent}>
<p>***sub-content***</p>
</div>
</div>
);
};
export default RenderPosts;
できたーーーーー\(^o^)/
code sandboxのリンクや追記などはPRのコメントにありますー。
No. | branch | description | リンク | PR |
---|---|---|---|---|
8 | 9cp6j7 | NotionAPIからDBのタイトル・テキスト・日付列を表示させる_他_lib/環境変数module/getStaticProps/props | https://github.com/herohoro/Blog_learn/pull/11 | #11 |
https://github.com/herohoro/Blog_learn/wiki
長丁場になってしまったので要点を.....
以上!!!🕺
公式リファレンスをなかなか理解できなかった要因は.....
「ネストされまくりのデータを理解できなかったから」
idと言われてもあっちこっちidが出てくるし、
propertyと言われてもdatabaseでpage propertyがどうこう言い出すしで
表の見方が全然分かりませんでした。(Propertyとpropertiesが別物で説明されてるとかもう.....😭)
でも、
code sandboxで練習用にeasy-notion-blogのコードをピックアップしながら
実装していくうちに点と点がつながってきて、
公式リファレンスの内容も分かってきました。
次は、タイトルをクリックすると記事のページが表示されるように画面遷移を作ってみようと思います\(^o^)/
Twitterでは更新のお知らせを随時行っています
興味ある方はLet'sフォロー★▼ この記事に興味があったら同じタグから関連記事をのぞいてみてね
RSSリーダーにatomのリンクを登録すると通知が行くよ🐌
https://herohoro.com/atom
やってみてね(*´ω`*)(*´ω`*)
フォロー大歓迎\(^o^)/
フォロー大歓迎\(^o^)/