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 .filter
ed array :(
So, how do I get a list/array of the database ID's to which my app has access?
Thanks!