code icon Code

Get Notion Database Schema

Fetch a database's property schema from Notion - property names, types, options for select/status fields

Source Code

const [databaseId] = process.argv.slice(2);

if (!databaseId) {
  console.error("Error: databaseId argument is required");
  process.exit(1);
}

const NOTION_API = "https://api.notion.com/v1";
const headers = {
  Authorization: "Bearer PLACEHOLDER_TOKEN",
  "Content-Type": "application/json",
  "Notion-Version": "2022-06-28",
};

/**
 * Extract options from select/multi_select/status properties
 */
function extractOptions(propertyConfig) {
  if (propertyConfig.options) {
    return propertyConfig.options.map((opt) => ({
      name: opt.name,
      color: opt.color,
    }));
  }
  // Status properties have options in a nested structure
  if (propertyConfig.status?.options) {
    return propertyConfig.status.options.map((opt) => ({
      name: opt.name,
      color: opt.color,
    }));
  }
  return null;
}

/**
 * Extract relevant config from a property based on its type
 */
function extractPropertyConfig(name, config) {
  const prop = {
    name,
    type: config.type,
  };

  // Add type-specific details
  switch (config.type) {
    case "select":
    case "multi_select":
      prop.options = extractOptions(config[config.type]);
      break;
    case "status":
      prop.options = extractOptions(config);
      // Status also has groups
      if (config.status?.groups) {
        prop.groups = config.status.groups.map((g) => ({
          name: g.name,
          color: g.color,
          optionIds: g.option_ids,
        }));
      }
      break;
    case "number":
      if (config.number?.format) {
        prop.format = config.number.format;
      }
      break;
    case "formula":
      if (config.formula?.expression) {
        prop.expression = config.formula.expression;
      }
      break;
    case "relation":
      if (config.relation?.database_id) {
        prop.relatedDatabaseId = config.relation.database_id;
      }
      break;
    case "rollup":
      if (config.rollup) {
        prop.rollupConfig = {
          relationPropertyName: config.rollup.relation_property_name,
          rollupPropertyName: config.rollup.rollup_property_name,
          function: config.rollup.function,
        };
      }
      break;
  }

  return prop;
}

console.log(`Fetching schema for database ${databaseId}...`);

try {
  const response = await fetch(`${NOTION_API}/databases/${databaseId}`, {
    method: "GET",
    headers,
  });

  if (!response.ok) {
    const errorText = await response.text();
    if (response.status === 404) {
      console.error(
        `Database not found. Make sure the database is shared with the Notion integration.`
      );
    } else if (response.status === 401) {
      console.error(`Unauthorized. Check your Notion API token.`);
    } else {
      console.error(`API error ${response.status}: ${errorText.slice(0, 200)}`);
    }
    process.exit(1);
  }

  const database = await response.json();

  // Extract database title
  const title = database.title?.[0]?.plain_text || "Untitled Database";

  // Extract and organize properties
  const properties = {};
  const dateProperties = [];
  const selectProperties = [];
  const statusProperties = [];

  for (const [propName, propConfig] of Object.entries(
    database.properties || {}
  )) {
    const prop = extractPropertyConfig(propName, propConfig);
    properties[propName] = prop;

    // Track properties by type for quick reference
    if (prop.type === "date") {
      dateProperties.push(propName);
    } else if (prop.type === "select") {
      selectProperties.push(propName);
    } else if (prop.type === "status") {
      statusProperties.push(propName);
    }
  }

  const schema = {
    id: databaseId,
    title,
    url: database.url,
    properties,
    // Quick reference for view-critical properties
    summary: {
      dateProperties,
      selectProperties,
      statusProperties,
      propertyCount: Object.keys(properties).length,
    },
  };

  console.log(`\n✓ Database: ${title}`);
  console.log(`  Properties: ${schema.summary.propertyCount}`);

  if (dateProperties.length > 0) {
    console.log(`  Date properties: ${dateProperties.join(", ")}`);
    console.log(`  → Use one of these for Calendar/Timeline views`);
  } else {
    console.log(`  ⚠ No date properties - cannot use Calendar/Timeline views`);
  }

  if (statusProperties.length > 0) {
    console.log(`  Status properties: ${statusProperties.join(", ")}`);
    console.log(`  → Use one of these for Board view grouping`);
  } else if (selectProperties.length > 0) {
    console.log(`  Select properties: ${selectProperties.join(", ")}`);
    console.log(`  → Use one of these for Board view grouping`);
  }

  // Output full schema as JSON
  console.log(`\nSchema:`);
  console.log(JSON.stringify(schema, null, 2));
} catch (error) {
  console.error(`Failed to fetch database schema: ${error.message}`);
  process.exit(1);
}