code icon Code

Create/Update Notion Content

Create a page, database entry, inline database, or update existing content

Source Code

import fs from "fs";

const [parentType, parentOrPageId, propertiesJson, contentPath = ""] =
  process.argv.slice(2);

if (
  !parentType ||
  !["database", "page", "inline_database", "update"].includes(parentType)
) {
  console.error(
    "Error: parentType must be 'database', 'page', 'inline_database', or 'update'"
  );
  process.exit(1);
}

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

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

let properties;
try {
  properties = JSON.parse(propertiesJson);
} catch (e) {
  console.error("Error parsing properties JSON:", e.message);
  process.exit(1);
}

let blocks = [];
if (contentPath) {
  try {
    const contentData = fs.readFileSync(contentPath, "utf-8");
    blocks = JSON.parse(contentData);
    if (!Array.isArray(blocks)) {
      blocks = [blocks];
    }
  } catch (e) {
    console.error(`Error reading content file: ${e.message}`);
    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",
};

try {
  let result;

  if (parentType === "update") {
    // Update existing page
    console.log(`Updating page ${parentOrPageId}...`);

    const updateBody = { properties };

    // Handle icon if provided
    if (properties.icon) {
      updateBody.icon = { type: "emoji", emoji: properties.icon };
      delete updateBody.properties.icon;
    }

    const res = await fetch(`${NOTION_API}/pages/${parentOrPageId}`, {
      method: "PATCH",
      headers,
      body: JSON.stringify(updateBody),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Update failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Update failed: ${res.status}`);
    }

    result = await res.json();

    // Append blocks if provided
    if (blocks.length > 0) {
      console.log(`  Appending ${blocks.length} blocks...`);
      const appendRes = await fetch(
        `${NOTION_API}/blocks/${parentOrPageId}/children`,
        {
          method: "PATCH",
          headers,
          body: JSON.stringify({ children: blocks }),
        }
      );

      if (!appendRes.ok) {
        console.error(`Warning: Failed to append blocks: ${appendRes.status}`);
      }
    }

    console.log(`✓ Updated page`);
  } else if (parentType === "database") {
    // Create entry in database
    console.log(`Creating entry in database ${parentOrPageId}...`);

    const body = {
      parent: { database_id: parentOrPageId },
      properties,
    };

    if (blocks.length > 0) {
      body.children = blocks;
      console.log(`  With ${blocks.length} content blocks`);
    }

    const res = await fetch(`${NOTION_API}/pages`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created database entry`);
  } else if (parentType === "inline_database") {
    // Create inline database under a page
    const title = properties.title || "Untitled Database";
    console.log(
      `Creating inline database "${title}" under ${parentOrPageId}...`
    );

    // properties.schema defines the database columns
    // Format: { "Column Name": { type: "select", options: [...] }, ... }
    const schema = properties.schema || {};

    // Build database properties from schema
    const dbProperties = {
      // Title column is required
      Name: { title: {} },
    };

    for (const [colName, colDef] of Object.entries(schema)) {
      if (colName === "Name") continue; // Skip, already added

      switch (colDef.type) {
        case "text":
        case "rich_text":
          dbProperties[colName] = { rich_text: {} };
          break;
        case "number":
          dbProperties[colName] = {
            number: { format: colDef.format || "number" },
          };
          break;
        case "select":
          dbProperties[colName] = {
            select: {
              options: (colDef.options || []).map((opt) =>
                typeof opt === "string" ? { name: opt } : opt
              ),
            },
          };
          break;
        case "multi_select":
          dbProperties[colName] = {
            multi_select: {
              options: (colDef.options || []).map((opt) =>
                typeof opt === "string" ? { name: opt } : opt
              ),
            },
          };
          break;
        case "status":
          dbProperties[colName] = { status: {} };
          break;
        case "date":
          dbProperties[colName] = { date: {} };
          break;
        case "checkbox":
          dbProperties[colName] = { checkbox: {} };
          break;
        case "url":
          dbProperties[colName] = { url: {} };
          break;
        case "email":
          dbProperties[colName] = { email: {} };
          break;
        case "phone_number":
          dbProperties[colName] = { phone_number: {} };
          break;
        default:
          dbProperties[colName] = { rich_text: {} };
      }
    }

    const body = {
      parent: { page_id: parentOrPageId },
      title: [{ type: "text", text: { content: title } }],
      properties: dbProperties,
      is_inline: true,
    };

    if (properties.icon) {
      body.icon = { type: "emoji", emoji: properties.icon };
    }

    const res = await fetch(`${NOTION_API}/databases`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created inline database`);
  } else {
    // Create page under parent page
    const title = properties.title || "Untitled";
    console.log(`Creating page "${title}" under ${parentOrPageId}...`);

    const body = {
      parent: { page_id: parentOrPageId },
      properties: {
        title: {
          title: [{ text: { content: title } }],
        },
      },
    };

    if (properties.icon) {
      body.icon = { type: "emoji", emoji: properties.icon };
    }

    if (blocks.length > 0) {
      body.children = blocks;
      console.log(`  With ${blocks.length} content blocks`);
    }

    const res = await fetch(`${NOTION_API}/pages`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created page`);
  }

  // Extract title from result
  let title;
  if (parentType === "inline_database") {
    // Database title is in result.title array
    title = result.title?.[0]?.plain_text || "Untitled Database";
  } else {
    // Page title is in properties
    const titleProp = Object.values(result.properties || {}).find(
      (p) => p.type === "title"
    );
    title = titleProp?.title?.[0]?.plain_text || "Untitled";
  }

  console.log(`  Title: ${title}`);
  console.log(`  URL: ${result.url}`);
  console.log(`  ID: ${result.id}`);

  console.log(
    JSON.stringify({
      success: true,
      action: parentType === "update" ? "updated" : "created",
      parentType,
      id: result.id,
      url: result.url,
      title,
      blocksAdded: blocks.length,
    })
  );
} catch (error) {
  console.error("Error:", error.message);
  throw error;
}
import fs from "fs";

const [parentType, parentOrPageId, propertiesJson, contentPath = ""] =
  process.argv.slice(2);

if (
  !parentType ||
  !["database", "page", "inline_database", "update"].includes(parentType)
) {
  console.error(
    "Error: parentType must be 'database', 'page', 'inline_database', or 'update'"
  );
  process.exit(1);
}

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

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

let properties;
try {
  properties = JSON.parse(propertiesJson);
} catch (e) {
  console.error("Error parsing properties JSON:", e.message);
  process.exit(1);
}

let blocks = [];
if (contentPath) {
  try {
    const contentData = fs.readFileSync(contentPath, "utf-8");
    blocks = JSON.parse(contentData);
    if (!Array.isArray(blocks)) {
      blocks = [blocks];
    }
  } catch (e) {
    console.error(`Error reading content file: ${e.message}`);
    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",
};

try {
  let result;

  if (parentType === "update") {
    // Update existing page
    console.log(`Updating page ${parentOrPageId}...`);

    const updateBody = { properties };

    // Handle icon if provided
    if (properties.icon) {
      updateBody.icon = { type: "emoji", emoji: properties.icon };
      delete updateBody.properties.icon;
    }

    const res = await fetch(`${NOTION_API}/pages/${parentOrPageId}`, {
      method: "PATCH",
      headers,
      body: JSON.stringify(updateBody),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Update failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Update failed: ${res.status}`);
    }

    result = await res.json();

    // Append blocks if provided
    if (blocks.length > 0) {
      console.log(`  Appending ${blocks.length} blocks...`);
      const appendRes = await fetch(
        `${NOTION_API}/blocks/${parentOrPageId}/children`,
        {
          method: "PATCH",
          headers,
          body: JSON.stringify({ children: blocks }),
        }
      );

      if (!appendRes.ok) {
        console.error(`Warning: Failed to append blocks: ${appendRes.status}`);
      }
    }

    console.log(`✓ Updated page`);
  } else if (parentType === "database") {
    // Create entry in database
    console.log(`Creating entry in database ${parentOrPageId}...`);

    const body = {
      parent: { database_id: parentOrPageId },
      properties,
    };

    if (blocks.length > 0) {
      body.children = blocks;
      console.log(`  With ${blocks.length} content blocks`);
    }

    const res = await fetch(`${NOTION_API}/pages`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created database entry`);
  } else if (parentType === "inline_database") {
    // Create inline database under a page
    const title = properties.title || "Untitled Database";
    console.log(
      `Creating inline database "${title}" under ${parentOrPageId}...`
    );

    // properties.schema defines the database columns
    // Format: { "Column Name": { type: "select", options: [...] }, ... }
    const schema = properties.schema || {};

    // Build database properties from schema
    const dbProperties = {
      // Title column is required
      Name: { title: {} },
    };

    for (const [colName, colDef] of Object.entries(schema)) {
      if (colName === "Name") continue; // Skip, already added

      switch (colDef.type) {
        case "text":
        case "rich_text":
          dbProperties[colName] = { rich_text: {} };
          break;
        case "number":
          dbProperties[colName] = {
            number: { format: colDef.format || "number" },
          };
          break;
        case "select":
          dbProperties[colName] = {
            select: {
              options: (colDef.options || []).map((opt) =>
                typeof opt === "string" ? { name: opt } : opt
              ),
            },
          };
          break;
        case "multi_select":
          dbProperties[colName] = {
            multi_select: {
              options: (colDef.options || []).map((opt) =>
                typeof opt === "string" ? { name: opt } : opt
              ),
            },
          };
          break;
        case "status":
          dbProperties[colName] = { status: {} };
          break;
        case "date":
          dbProperties[colName] = { date: {} };
          break;
        case "checkbox":
          dbProperties[colName] = { checkbox: {} };
          break;
        case "url":
          dbProperties[colName] = { url: {} };
          break;
        case "email":
          dbProperties[colName] = { email: {} };
          break;
        case "phone_number":
          dbProperties[colName] = { phone_number: {} };
          break;
        default:
          dbProperties[colName] = { rich_text: {} };
      }
    }

    const body = {
      parent: { page_id: parentOrPageId },
      title: [{ type: "text", text: { content: title } }],
      properties: dbProperties,
      is_inline: true,
    };

    if (properties.icon) {
      body.icon = { type: "emoji", emoji: properties.icon };
    }

    const res = await fetch(`${NOTION_API}/databases`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created inline database`);
  } else {
    // Create page under parent page
    const title = properties.title || "Untitled";
    console.log(`Creating page "${title}" under ${parentOrPageId}...`);

    const body = {
      parent: { page_id: parentOrPageId },
      properties: {
        title: {
          title: [{ text: { content: title } }],
        },
      },
    };

    if (properties.icon) {
      body.icon = { type: "emoji", emoji: properties.icon };
    }

    if (blocks.length > 0) {
      body.children = blocks;
      console.log(`  With ${blocks.length} content blocks`);
    }

    const res = await fetch(`${NOTION_API}/pages`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errorText = await res.text();
      console.error(`Create failed: ${res.status}`);
      console.error(errorText);
      throw new Error(`Create failed: ${res.status}`);
    }

    result = await res.json();
    console.log(`✓ Created page`);
  }

  // Extract title from result
  let title;
  if (parentType === "inline_database") {
    // Database title is in result.title array
    title = result.title?.[0]?.plain_text || "Untitled Database";
  } else {
    // Page title is in properties
    const titleProp = Object.values(result.properties || {}).find(
      (p) => p.type === "title"
    );
    title = titleProp?.title?.[0]?.plain_text || "Untitled";
  }

  console.log(`  Title: ${title}`);
  console.log(`  URL: ${result.url}`);
  console.log(`  ID: ${result.id}`);

  console.log(
    JSON.stringify({
      success: true,
      action: parentType === "update" ? "updated" : "created",
      parentType,
      id: result.id,
      url: result.url,
      title,
      blocksAdded: blocks.length,
    })
  );
} catch (error) {
  console.error("Error:", error.message);
  throw error;
}