My Notion Note Taking Automations

After attending THAT conference a few weeks ago, I sat in a group that discussed ongoing journalling. I decided to really get back into it using my Notion Notes database after being inspired during that talk. I used to keep a daily note in my OneNote notebooks way back in the day but it’s been a while since I have. Since then, I’ve moved to have a weekly note, which I prefer since it’s just a running list of the things I want to jot down. Beyond that, I’ve decided to automate some things with code. Here’s a quick and dirty rundown.

One note per week

I have a database called Notes that I use to keep misc notes in. Every week, I create a note that spans the entire week on a calendar, using a Date property with start and end dates. This is actually an automated process since I have a script that runs on Monday around 4AM to set this up, but it’s not so difficult that I couldn’t do it myself.

Daily setup

I actually don’t use any templates here, but instead, I recently built an automation using serverless functions on Netlify that uses the Notion API to grab the latest page in that document and append my daily task list (which honestly needs to be expanded on) and adds an inspirational quote just to put me in a good space mentally.

So here is the serverless function in Netlify.

const { Client } = require("@notionhq/client")
const Quote = require('inspirational-quotes');

const dailyTodos = [
  "review email",
  "review github issues",
  "review todos"
]

exports.handler = async function (event, context) {

  notion = new Client({
    auth: process.env.NOTION_KEY
  })

  // Get the latest note in that db
  let params = {
    database_id: process.env.NOTES_DBID,
    sorts: [
      {
        property: 'Date',
        direction: 'descending'
      }
    ]
  }
  let res = await notion.databases.query(params)

  let latestNote = res.results[0]

  // this is a template to add the blocks I want
  params = {
    block_id: latestNote.id,
    children: [
      {
        type: "divider",
        divider: {}
      },
      {
        "heading_2": {
          "rich_text": [
            {
              "text": {
                "content": (new Date()).toLocaleDateString()
              }
            }
          ]
        }
      },
      {
        "type": "toggle",
        "toggle": {
          "rich_text": [
            {
              "type": "text",
              "text": {
                "content": "daily tasks",
              }
            }
          ],
          "color": "default",
          "children":[]
        }
      }
    ]
  }

  // this block adds my daily todos from an array
  dailyTodos.forEach(todo => {
    params.children[2].toggle.children.push({
      "type": "to_do",
      "to_do": {
        "rich_text": [{
          "type": "text",
          "text": {
            "content": todo,
            "link": null
          }
        }],
        "checked": false,
        "color": "default"
      }
    })
  })

  // and add the inspirational quote
  let quote = Quote.getQuote()
  params.children.push({
    "type": "quote",
    "quote": {
      "rich_text": [{
        "type": "text",
        "text": {
          "content": quote.text + "\n•" + quote.author,
        },
      }],
      "color": "default"
    }
  })

  await notion.blocks.children.append(params)

  return {
    statusCode: 200,
  }
}

And I’m using a GitHub Action workflow with a cron trigger to do this once a day.

on:
  workflow_dispatch:
  schedule:
    - cron:  '0 4 * * 1-5'

jobs:
  execute:
    runs-on: ubuntu-latest
    steps:
    - name: Execute
      uses: fjogeleit/http-request-action@v1
      with:
        url: '${{ secrets.NETLIFY_URL }}/.netlify/functions/add-daily-note-section'
        method: 'GET'

Import tasks into a separate db

One thing I really wanted was a way to scan through the list of to dos at the top level and automatically create those entries in a separate Notion database for me to plan. I initially tried to do this in another serverless function, but it took longer than the allotted 10 seconds for it, so I instead decided to put it in a container on my Kubernetes server. In any case, using the node-cron library, I could essentially schedule it to happen once per day as well. This would help me setup for my daily review as soon as I sit down at the computer:

import * as dotenv from 'dotenv'
import * as cron from 'node-cron'
import { Client } from "@notionhq/client";

dotenv.config()

async function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getLatestNote() {
  let notion = new Client({
    auth: process.env.NOTION_KEY
  })

  let params = {
    database_id: process.env.NOTES_DBID,
    sorts: [
      {
        property: 'Date',
        direction: 'descending'
      }
    ]
  }
  let res = await notion.databases.query(params)

  return res.results[0]
}

export async function syncDailyNotes (event, context) {
  try {
    // get the latest note in the db
    let latestNote = await getLatestNote()

    let notion = new Client({
      auth: process.env.NOTION_KEY
    })

    // get all the blocks on that page
    let children = await notion.blocks.children.list({
      block_id: latestNote.id,
      page_size: 100
    })

    // flatten the list of blocks into only the to dos
    let todos = children.results.filter(c => c.type === 'to_do')
    let flattened = []
    todos.forEach(t => {
      if(t.to_do.rich_text[0].type !== "mention") {
        let f = {
          id: t.id,
          text: t.to_do.rich_text[0].plain_text
        }
        flattened.push(f)
      }
    })

    console.log(flattened.length)

    // loop over the list and create the entry in the tasks database
    if(flattened.length) {
      for (let i = 0; i < flattened.length; i++) {
        let t = flattened[i]
        console.log('processing', t.text)

        // Create task
        let page = await notion.pages.create({
          parent: {
            type: "database_id",
            database_id: process.env.TASKS_DBID
          },
          "properties": {
            "Name": {
              "title": [
                {
                  "text": {
                      "content": t.text
                  }
                }
              ]
            }
          }
        })

        // also, update the block in the notes page to be a backlink to the todo entry
        // this allows me to easily jump right to that task if I want to do anything about it.
        await notion.blocks.update({
          block_id: t.id,
          to_do: {
            type: "to_do",
            rich_text: [
                {
                  "type": "mention",
                  "mention": {
                      "type": "page",
                      "page": {
                          "id": page.id
                      }
                  },
                  "annotations": {
                      "bold": false,
                      "italic": false,
                      "strikethrough": false,
                      "underline": false,
                      "code": false,
                      "color": "default"
                  },
                  "href": "https://www.notion.so/" + page.id.replace(/-/g, "")
              }
            ]
          }
        })
        await sleep(3000)
      }
    }
  } catch (err) {
    console.error('drstrange', err);
  }
}

cron.schedule('0 0 * * 1-5', syncDailyNotes)

The result is that each top-level todo in my notes page ends up having a backlink to the task in the Tasks database.

This project is open source, so feel free to use the code however you want for your use case. Next up I think I’m going to automatically update the “Done” property in the Tasks db entry if the to do on the notes page is checked. I love working with the Notion API because the options feel like they are endless!