herohoroブログ

2つ目のNotionDBをeasy-notion-blogに導入してブログ以外の情報を管理しよう



🔄   2023-03-14

ブログ用にNotionDBを使って記事を管理するeasy-notion-blog。

「記事以外もNotionDBで管理したい」と思ったことはありませんか??

実を言うとだいぶ前からその願望があり…..

複数のNotionAPIを使いこなす方法が分からず

苦し紛れに別サーバーで用意したものを埋め込む….といったことをしていました。

image block

見るだけなら不自由ないのですが、

クリックするとエラーになってしまう….

という問題を抱えていました。

思い切って再チャレンジしてみるか…..

学んだことを出し切れば….

なんとかなるのでは….????

と数カ月ぶりにやる気になり、

改造してみたら上手くいきました⭐

解説します\(^o^)/

トレーニング

Next.jsのサンプルページからNotionAPIを使って

1ページで実装する【流れ】がよく分かる動画です。

そこから表示させたいNotionDBのプロパティを追加して….

image block

表示させたい行は….この2行!!!

image block

Filter処理して…..

実装してみます。

image block

コードにするとこんな感じです。

import type { NextPage } from 'next'
import Link from 'next/link'
import {Client}from"@notionhq/client"
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Pstyles from '../styles/posting.module.css'

type Help={
  title:string;
  description:string;
  date:string;
  tags:string;
  last_edit:string;
  URL:string;
  site:string;
  siteCollor:string;
}

export async function getStaticProps(){
  const herotoken="***** NOTION_API_SECRET ******";
  const herodatabaseId="******* DATABASE_ID *******";
  const heronotion = new Client({ auth: herotoken });

  const data = await heronotion.databases.query({
    database_id: herodatabaseId,
    filter: {  
          property: 'tags',
          select: {
            equals: "posting",
          }
      },
    sorts:[{
      property: 'date',
      direction: 'descending',
    },],
  });

  const helpList: Help[]=data.results.map((_:any)=>_.properties).map(_=>{
    return{
      date: _.date.date.start,
      title: _.title.title[0].plain_text,
      description: _.description.rich_text[0].plain_text,
      tags: _.tags.select.name,
      last_edit: _.last_edit.last_edited_time,
      URL: _.URL.url,
      site: _.site.select.name,
      siteCollor: _.site.select.color
    };
  });

  return {props:{helpList},revalidate:30};
}

type Props={
  helpList:Help[];
};
const Home = ({helpList}:Props) => {

  return (
    <div className={styles.container}>
      <Head>
        <title>Posting App</title>
        <meta name="description" content="Generated by Posting app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <p>▼  こちらはherohoroブログ以外で投稿した時に更新します。</p>
        <h1 className={styles.title}>
            External Posting
        </h1>

        <p className={styles.description}>
          From NotionDB{' '}
          <code className={styles.code}>pages/posting.tsx</code>
        </p>
        <h3>気になるcardをクリックすると記事へ飛べるよー</h3>
{/* gridのクラス名が効かない....siteCollorも効かない */}
        <div className={Pstyles.grid}>
          {helpList.map((_)=>(
          <div className={Pstyles.card} key={_.title}>
            <div className={styles.siteCollor} >
              <div className={`${_.siteCollor}`}>
                <p>{_.site}</p>
              </div>
            </div>
            <h3>{_.date}</h3>
            <Link href={_.URL} passHref><p>📝 {_.title}</p></Link>
            <p>&#128537; {(_.description)?(_.description):null}</p>
            <hr/>
            {/* <p>last edit : {_.last_edit}</p> */}
          </div>
          ))}
        </div>
      </main>
    </div>
  )
}

export default Home
pages/index.tsx

これをeasy-notion-blogに導入するには…..

libやgetStaticPropsなどの組み方に調整する必要があります。(ここでずっと詰まってました)

以前投稿した

Notion APIの公式リファレンスを徹底的に根拠付けしながらブログにtitle,text,dateを一覧にして表示させる方法_Blog learn03

の流れに沿って実装したら上手くいったので、

今回はその流れに沿って解説していきます。\(^o^)/

image block
Blog learn03より

環境変数に文字列を登録

ブログ用のDBを設定した時と同じく2つの文字列を設定します。

※ 今回は2つ目の文字列…ということで文頭に「SEC_ 」と付けてます

  • SEC_NOTION_API_SECRET
  • SEC_DATABASE_ID

この2つの文字列を….

開発環境:

SEC_NOTION_API_SECRET=
SEC_DATABASE_ID=
.env.localに追加

本番環境:

image block
Vercelに追加

追加します\(^o^)/

追加した環境変数を取り出しやすくするためにsever-containts.js をいじります。

const path = require('path')

const NOTION_API_SECRET = process.env.NOTION_API_SECRET
const DATABASE_ID = process.env.DATABASE_ID
const NEXT_PUBLIC_URL = process.env.NEXT_PUBLIC_URL
const NEXT_PUBLIC_GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID
const BLOG_INDEX_CACHE = path.resolve('.blog_index_data')
const NUMBER_OF_POSTS_PER_PAGE = 10
const SEC_NOTION_API_SECRET = process.env.SEC_NOTION_API_SECRET 👉 追加
const SEC_DATABASE_ID = process.env.SEC_DATABASE_ID  👉 追加

module.exports = {
  NOTION_API_SECRET,
  DATABASE_ID,
  NEXT_PUBLIC_URL,
  NEXT_PUBLIC_GA_TRACKING_ID,
  BLOG_INDEX_CACHE,
  NUMBER_OF_POSTS_PER_PAGE,
  SEC_NOTION_API_SECRET,   👉 追加
  SEC_DATABASE_ID    👉 追加
}

これで他のファイルから呼び出せるようになりました🎶

読み込むデータの型を定義

トレーニングの段階で記述した….

type Help={
  title:string;
  description:string;
  date:string;
  tags:string;
  last_edit:string;
  URL:string;
  site:string;
  siteCollor:string;
}

この部分を定位置に収納します。

export interface Post {
    title: string;
    description: string;
    date: string;
    tags: string;
    last_edit: string;
    URL: string;
    site: string;
    siteCollor: string;
  }
src/lib/sec-notion/interfaces.ts

ブログ用と区別するために

libディレクトリ内に【sec-notion】というディレクトリを

用意することにしました(´・ω・`)

読み込むデータを整頓

トレーニングの段階で記述した…..

export async function getStaticProps(){
  const herotoken="***** NOTION_API_SECRET ******";
  const herodatabaseId="******* DATABASE_ID *******";
  const heronotion = new Client({ auth: herotoken });

  const data = await heronotion.databases.query({
    database_id: herodatabaseId,
    filter: {  
          property: 'tags',
          select: {
            equals: "posting",
          }
      },
    sorts:[{
      property: 'date',
      direction: 'descending',
    },],
  });

  const helpList: Help[]=data.results.map((_:any)=>_.properties).map(_=>{
    return{
      date: _.date.date.start,
      title: _.title.title[0].plain_text,
      description: _.description.rich_text[0].plain_text,
      tags: _.tags.select.name,
      last_edit: _.last_edit.last_edited_time,
      URL: _.URL.url,
      site: _.site.select.name,
      siteCollor: _.site.select.color
    };
  });
  console.dir(helpList,{depth:null});

  return {props:{helpList},revalidate:30};
}

この部分を定位置に収納します。

client.tsというファイルです。

これもinterfaces.tsと同様、

ブログ用と区別するために

【sec-notion】ディレクトリに

入れることにしました。(´・ω・`)

import { SEC_NOTION_API_SECRET, SEC_DATABASE_ID } from '../notion/server-constants'
import { Post } from './interfaces'

const { Client } = require('@notionhq/client')
const client = new Client({
    auth: SEC_NOTION_API_SECRET,
  })
export async function getAllSecPosts() {
    let results = []
  
    
    const params = {
        database_id: SEC_DATABASE_ID,
        filter: {  
            property: 'tags',
            select: {
                equals: "posting",
            }
        },
        sorts: [
            {
            property: 'date',
            direction: 'descending',
            },
        ]
    }    
	  const data = await client.databases.query(params)
    results = results.concat(data.results)
    params['start_cursor'] = data.next_cursor
  
    return results.map(item => _buildPost(item))
  }

function _buildPost(data) {
    const prop = data.properties
    const post: Post = {
        title: prop.title.title[0].plain_text,
        date: prop.date.date.start,
        tags: prop.tags.select.name,
        description:
        prop.description.rich_text.length > 0
            ? prop.description.rich_text[0].plain_text
            : '',
        last_edit: prop.last_edit.last_edited_time,
        URL:prop.URL.url,
        site: prop.site.select.name,
        siteCollor: prop.site.select.color
    }
  
    return post
}
src/lib/sec-notion/client.ts

これで他のファイルからgetAllSecPostsをimportすれば使えるようになりました。

整頓したデータを活用して表示

トレーニングの記述で残るは後半!!

const Home = ({helpList}:Props) => {

  return (
    <div className={styles.container}>
      <Head>
        <title>Posting App</title>
        <meta name="description" content="Generated by Posting app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <p>▼  こちらはherohoroブログ以外で投稿した時に更新します。</p>
        <h1 className={styles.title}>
            External Posting
        </h1>

        <p className={styles.description}>
          From NotionDB{' '}
          <code className={styles.code}>pages/posting.tsx</code>
        </p>
        <h3>気になるcardをクリックすると記事へ飛べるよー</h3>
{/* gridのクラス名が効かない....siteCollorも効かない */}
        <div className={Pstyles.grid}>
          {helpList.map((_)=>(
          <div className={Pstyles.card} key={_.title}>
            <div className={styles.siteCollor} >
              <div className={`${_.siteCollor}`}>
                <p>{_.site}</p>
              </div>
            </div>
            <h3>{_.date}</h3>
            <Link href={_.URL} passHref><p>📝 {_.title}</p></Link>
            <p>&#128537; {(_.description)?(_.description):null}</p>
            <hr/>
            {/* <p>last edit : {_.last_edit}</p> */}
          </div>
          ))}
        </div>
      </main>
    </div>
  )
}

export default Home

これをeasy-notion-blogに表示させたいページへ調合します。

今回はブログのトップページに入れたいのでsrc/pages/index.tsx に追記していきましょう。

import { getEditTimeStr } from '../lib/blog-helpers'
import { getAllSecPosts } from '../lib/sec-notion/client'
// 他のimportはそのまま使ってね

export async function getStaticProps() {
  const posts = await getPosts()
  const rankedPosts = await getRankedPosts()
  const tags = await getAllTags()
  const secPosts = await getAllSecPosts() 👉 追加
  return {
    props: {
      posts,
      rankedPosts,
      tags,
      secPosts  👉 追加
    },
    revalidate: 60,
  }
}
const RenderPage = ({ rankedPosts = [], tags = [],secPosts=[] }) => (
//引数にsecPosts=[]を追加
	<div className={styles.container}>
    <DocumentHead />
    <div className={styles.mainContent}>

{/* 以下、追加したい場所にコピペ */}

			<div className={SecStyles.grid}>
        <h3>
          \ 他のサイトへの記事投稿 /
        </h3>
        <p>
          たまに気が向くと投稿することがあるので良かったらのぞいてみてください\(^o^)</p>
        {secPosts.map(secPost => {
          return(
            <div className={SecStyles.card} key={secPost.title}>
            <div >
              <div className={`${secPost.siteCollor}`}>
                <p>{secPost.site}</p>
              </div>
            </div>
            <h3>{secPost.date}</h3>
            <Link href={secPost.URL} passHref><p>📝 {secPost.title}</p></Link>
            <p>&#128537; {(secPost.description)?(secPost.description):null}</p>
            <hr/>
            <p>last edit : {getEditTimeStr(secPost.last_edit)}</p>
          </div>
          )
        })}
      </div>

src/pages/index.tsxの一部

引数に入れたsecPostsを1つずつ取り出したのがsecPost

secPostが抱えているプロパティはclient.tsで追記したメンバーで…

  • title
  • date
  • tags
  • description
  • last_edit
  • URL
  • site
  • siteCollor

これらを使って表示させると…….

image block

こんな感じになりました\(^o^)/

cssは省略していますが、

簡単な記述なのでみなさんならちょちょいのちょいでできるかと思います⭐

まとめ

Blog learn恐るべし!

他の改造でも手順の雛形として活用できそうな予感がするので

改造作業を進める中で

「あ!ここも使えるじゃん💡」

といった発見があり次第記事にして投稿してみようと思います\(^o^)/

ブログとしても使いたいし、

日々の振り返りをCardにして並べるのも面白そうだし、

bookmarkのように使うのも良さそうですね〜〜。

このブログのSpaceページにもiframeで埋め込んでいるNotionDBがあり…..

image block

https://herohoro.com/herospace

実は今回取り込んだDBと同じDBだったりします。

image block

Filterを上手く使いこなせば1つのDBからいろんな表現ができそうです…..(*´∀`*)(*´∀`*)

今回、iframeで埋め込むとリンクを埋め込んでも使えない…といった問題を解決するべく結構な気合で改造したものの、普通にiframeが使えたら改造しなかったかもしれません。。。。(;O;)

不自由って改造意欲を沸き立たす要因だなと思い

余韻に浸っているへろほろでした 🧘‍♀️🧘‍♀️🧘‍♀️🧘‍♀️🧘‍♀️

(*´ω`*)🍺

Twitterでは更新のお知らせを随時行っています

興味ある方はLet'sフォロー★

▼ この記事に興味があったら同じタグから関連記事をのぞいてみてね

Buy Me A Coffee

新着記事を通知したい??


RSSリーダーにatomのリンクを登録すると通知が行くよ🐌

https://herohoro.com/atom

やってみてね(*´ω`*)(*´ω`*)

Twitter Timeline


フォロー大歓迎\(^o^)/