2

I'm trying to get a list of all the databases to which my app has access in the Notion API. All I actually need is at most this

{
  object: 'list',
  results: [
    {
      object: 'block',
      id: 'a87f6026-9f2b-43cc-943d-fc2479a605b4',
      created_time: '2022-01-11T17:17:00.000Z',
      last_edited_time: '2022-01-14T15:28:00.000Z',
      has_children: false,
      archived: false,
      type: 'child_database',
      child_database: { title: 'Source' }
    }
  ],
  next_cursor: null,
  has_more: false
}

and really I don't even need that much — just the ID and the title would be sufficient. I had this working ... until I discovered users exist :) Users do things like move content around, including indenting it, which makes it a child and the technique I was initially using of iterating through blocks no longer works.

Before getting into the meat of things, as an aside, I found this question and answer. Unfortunately, the answer doesn't work as advertised, even though the original questioner marked it as the correct answer. When I use the new /search API with just a filter as the payload it returns both the parent page and the database ... and entries for each row in the database:

const getDatabases = async () => {
  console.log('Getting databases.');
  const databases = await notion.search({
    fiilter: {
      value: 'database',
      property: 'object'
    } 
  });

  console.log('Got ...');
  console.dir(databases, { depth: null });
}

outputs

Getting databases.
Got ...
{
  object: 'list',
  results: [
    {
      object: 'page',
      id: 'c6453c3b-466c-4c09-8520-1ddbf50be85d',
      ... // this is not a database, it's the parent page that has a database somewhere as a child
      ...
    },
    {
      object: 'database',
      id: 'a87f6026-9f2b-43cc-943d-fc2479a605b4',
      cover: null,
      icon: null,
      created_time: '2022-01-11T17:17:00.000Z',
      last_edited_time: '2022-01-14T15:28:00.000Z',
      title: [
        {
          type: 'text',
          text: { content: 'Source', link: null },
          annotations: {
            bold: false,
            italic: false,
            strikethrough: false,
            underline: false,
            code: false,
            color: 'default'
          },
          plain_text: 'Source',
          href: null
        }
      ],
      properties: {
        '06/12/2021': {
          id: '%3AitN',
          name: '06/12/2021',
          type: 'rich_text',
          rich_text: {}
        },
        ...
        // I guess this is a database, but it also includes all of the column headings (conveniently in random order) and is way more than I need
        ...
        '24/12/2021': {
          id: '~%3AJo',
          name: '24/12/2021',
          type: 'number',
          number: { format: 'number' }
        },
        Source: { id: 'title', name: 'Source', type: 'title', title: {} }
      },
      parent: {
        type: 'page_id',
        page_id: '372f45db-48e4-4948-9b8b-0aabded59e7c'
      },
      url: 'https://www.notion.so/a87f60269f2b43cc943dfc2479a605b4'
    },
    {
      object: 'page',
      id: '05bf1693-5279-4a91-93fa-af3ee6eaf362',
      created_time: '2022-01-11T17:17:00.000Z',
      last_edited_time: '2022-01-11T17:17:00.000Z',
      cover: null,
      icon: null,
      parent: {
        type: 'database_id',
        database_id: 'a87f6026-9f2b-43cc-943d-fc2479a605b4'
      },
      archived: false,
      properties: {
        '06/12/2021': { id: '%3AitN', type: 'rich_text', rich_text: [] },
        '29/11/2021': { id: '%3ETiv', type: 'rich_text', rich_text: [] },
        ...
        // clearly not a database since the 'object' is a 'page' ... the first row of data?
      ...

In any case, I found that long after going down a different path. If someone has a way to make the above work, great! But if not, here's where I'm currently stuck. My target outcome is an array of database IDs: [db_id_1, db_id_2, ... ].

I was going to do a .forEach on all the blocks on the page: if they're a database, stuff their ID in an array; if they have children, recurse down into the children:

const getChildren = async (blockId) => {
  const children = await notion.blocks.children.list({
    block_id: blockId
  });
  return children;
}

const getDatabases = async (blockId) => {
  let databases = [];

  const children = await getChildren(blockId);

  children.results.forEach( async (child) => {
    if (child.has_children === true) {
      const childrenDatabases = await getDatabases(child.id);
      databases = [...databases, ...childrenDatabases];
    }
    if (['child_database', 'database'].includes(child.type)) {
      databases.push(child.id)
    }
  });

  return databases;
}

This ... almost works ... but it turns out, .forEach with async/await does not work as I expected it would :)

In diagnosing that problem, I came across this answer, which seemed both elegant and with the added advantage of doing a bunch of this in parallel. However, I'm now stumped with an array size issue:

const getDatabases = async (blockId) => {
  const children = await getChildren(blockId);
  const newDatabases = await Promise.all(children.results
    .filter( async (child) => {
      return (['child_database', 'database'].includes(child.type)
        || child.has_children === true);
    })
    .map( async (child) => { 
      if (['child_database', 'database'].includes(child.type)) {
        return child.id;
      } else {
        // it must have children, recurse getDatabases
      }
    })
    .filter( (child) => ['child_database', 'database'].includes(child.type))
  );

I stopped coding there, because I realised at this point the "// It must have children ..." comment was going to result in a problem, because if I recursively called getDatabases again there, the response could be more than one entry, and my .map would be messed up because it would be a different size than the .filtered array :(

So, how do I get a list/array of the database ID's to which my app has access?

Thanks!

philolegein
  • 1,099
  • 10
  • 28

0 Answers0