index.html•30.5 kB
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notion API 테스트</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
h1 {
color: #2e3a59;
}
.card {
background-color: #f7f9fc;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.form-group {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
input, select, textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
textarea {
height: 100px;
font-family: monospace;
}
button {
background-color: #2e75b5;
color: white;
border: none;
padding: 10px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background-color: #1d5a9f;
}
pre {
background-color: #f0f2f5;
padding: 12px;
border-radius: 4px;
overflow: auto;
font-size: 13px;
max-height: 300px;
}
.result {
margin-top: 20px;
}
.tabs {
display: flex;
border-bottom: 1px solid #ddd;
margin-bottom: 20px;
}
.tab {
padding: 10px 15px;
cursor: pointer;
background: #f0f2f5;
margin-right: 5px;
border-radius: 4px 4px 0 0;
}
.tab.active {
background: #2e75b5;
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.api-section {
margin-bottom: 30px;
}
.collapsible {
cursor: pointer;
background-color: #eaecf0;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.collapsible:hover {
background-color: #e0e3e9;
}
.collapsible-content {
display: none;
}
.collapsible-content.active {
display: block;
}
</style>
</head>
<body>
<h1>Notion API 테스트</h1>
<div class="tabs">
<div class="tab active" data-tab="search">검색</div>
<div class="tab" data-tab="pages">페이지</div>
<div class="tab" data-tab="databases">데이터베이스</div>
<div class="tab" data-tab="blocks">블록</div>
<div class="tab" data-tab="users">사용자</div>
<div class="tab" data-tab="comments">코멘트</div>
</div>
<!-- 검색 탭 -->
<div class="tab-content active" id="search-tab">
<div class="card">
<h2>검색 API</h2>
<div class="form-group">
<label for="searchQuery">검색어:</label>
<input type="text" id="searchQuery" placeholder="검색어를 입력하세요">
</div>
<div class="form-group">
<label for="searchFilter">필터 (선택사항):</label>
<select id="searchFilter">
<option value="">필터 없음</option>
<option value="page">페이지만</option>
<option value="database">데이터베이스만</option>
</select>
</div>
<button id="searchButton">검색</button>
<div id="searchResult" class="result">
<h3>검색 결과:</h3>
<pre id="searchOutput">검색 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
<!-- 페이지 탭 -->
<div class="tab-content" id="pages-tab">
<div class="api-section">
<div class="collapsible">
<h2>페이지 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="pageId">페이지 ID:</label>
<input type="text" id="pageId" placeholder="페이지 ID를 입력하세요">
</div>
<button id="pageButton">조회</button>
<div id="pageResult" class="result">
<h3>페이지 정보:</h3>
<pre id="pageOutput">페이지 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>페이지 생성</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="createPageParent">상위 페이지 ID:</label>
<input type="text" id="createPageParent" placeholder="페이지를 생성할 상위 페이지 ID">
</div>
<div class="form-group">
<label for="createPageTitle">페이지 제목:</label>
<input type="text" id="createPageTitle" placeholder="새 페이지 제목">
</div>
<div class="form-group">
<label for="createPageJson">페이지 속성 (JSON):</label>
<textarea id="createPageJson" placeholder='{
"parent": { "page_id": "페이지ID" },
"properties": {
"title": {
"title": [{ "text": { "content": "새 페이지 제목" } }]
}
}
}'></textarea>
</div>
<button id="createPageButton">페이지 생성</button>
<div id="createPageResult" class="result">
<h3>생성 결과:</h3>
<pre id="createPageOutput">페이지 생성 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>페이지 업데이트</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="updatePageId">페이지 ID:</label>
<input type="text" id="updatePageId" placeholder="업데이트할 페이지 ID">
</div>
<div class="form-group">
<label for="updatePageJson">업데이트 내용 (JSON):</label>
<textarea id="updatePageJson" placeholder='{
"properties": {
"title": {
"title": [{ "text": { "content": "업데이트된 제목" } }]
}
}
}'></textarea>
</div>
<button id="updatePageButton">페이지 업데이트</button>
<div id="updatePageResult" class="result">
<h3>업데이트 결과:</h3>
<pre id="updatePageOutput">페이지 업데이트 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>페이지 속성 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="propertyPageId">페이지 ID:</label>
<input type="text" id="propertyPageId" placeholder="페이지 ID를 입력하세요">
</div>
<div class="form-group">
<label for="propertyId">속성 ID:</label>
<input type="text" id="propertyId" placeholder="속성 ID를 입력하세요">
</div>
<button id="propertyButton">속성 조회</button>
<div id="propertyResult" class="result">
<h3>속성 정보:</h3>
<pre id="propertyOutput">속성 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
</div>
<!-- 데이터베이스 탭 -->
<div class="tab-content" id="databases-tab">
<div class="api-section">
<div class="collapsible">
<h2>데이터베이스 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="databaseId">데이터베이스 ID:</label>
<input type="text" id="databaseId" placeholder="데이터베이스 ID를 입력하세요">
</div>
<button id="databaseButton">조회</button>
<div id="databaseResult" class="result">
<h3>데이터베이스 정보:</h3>
<pre id="databaseOutput">데이터베이스 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>데이터베이스 생성</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="createDatabaseParent">상위 페이지 ID:</label>
<input type="text" id="createDatabaseParent" placeholder="데이터베이스를 생성할 상위 페이지 ID">
</div>
<div class="form-group">
<label for="createDatabaseTitle">데이터베이스 제목:</label>
<input type="text" id="createDatabaseTitle" placeholder="새 데이터베이스 제목">
</div>
<div class="form-group">
<label for="createDatabaseJson">데이터베이스 속성 (JSON):</label>
<textarea id="createDatabaseJson" placeholder='{
"parent": { "page_id": "페이지ID" },
"title": [{ "text": { "content": "새 데이터베이스" } }],
"properties": {
"이름": { "title": {} },
"설명": { "rich_text": {} },
"상태": { "select": { "options": [
{ "name": "진행중", "color": "blue" },
{ "name": "완료", "color": "green" }
] } }
}
}'></textarea>
</div>
<button id="createDatabaseButton">데이터베이스 생성</button>
<div id="createDatabaseResult" class="result">
<h3>생성 결과:</h3>
<pre id="createDatabaseOutput">데이터베이스 생성 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>데이터베이스 쿼리</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="queryDatabaseId">데이터베이스 ID:</label>
<input type="text" id="queryDatabaseId" placeholder="쿼리할 데이터베이스 ID">
</div>
<div class="form-group">
<label for="queryDatabaseJson">쿼리 내용 (JSON):</label>
<textarea id="queryDatabaseJson" placeholder='{
"filter": {
"property": "속성명",
"rich_text": {
"contains": "검색어"
}
},
"sorts": [
{
"property": "속성명",
"direction": "ascending"
}
]
}'></textarea>
</div>
<button id="queryDatabaseButton">쿼리 실행</button>
<div id="queryDatabaseResult" class="result">
<h3>쿼리 결과:</h3>
<pre id="queryDatabaseOutput">쿼리 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
</div>
<!-- 블록 탭 -->
<div class="tab-content" id="blocks-tab">
<div class="api-section">
<div class="collapsible">
<h2>블록 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="blockId">블록 ID:</label>
<input type="text" id="blockId" placeholder="블록 ID를 입력하세요">
</div>
<button id="blockButton">조회</button>
<div id="blockResult" class="result">
<h3>블록 정보:</h3>
<pre id="blockOutput">블록 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>블록 내용 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="blockChildrenId">블록 ID:</label>
<input type="text" id="blockChildrenId" placeholder="블록 ID를 입력하세요 (페이지 ID도 가능)">
</div>
<button id="blockChildrenButton">내용 조회</button>
<div id="blockChildrenResult" class="result">
<h3>블록 내용:</h3>
<pre id="blockChildrenOutput">블록 내용이 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>블록 추가</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="appendBlockId">블록 ID:</label>
<input type="text" id="appendBlockId" placeholder="블록을 추가할 상위 블록 ID (페이지 ID도 가능)">
</div>
<div class="form-group">
<label for="appendBlockJson">추가할 블록 (JSON):</label>
<textarea id="appendBlockJson" placeholder='{
"children": [
{
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Hello world"
}
}
]
}
}
]
}'></textarea>
</div>
<button id="appendBlockButton">블록 추가</button>
<div id="appendBlockResult" class="result">
<h3>추가 결과:</h3>
<pre id="appendBlockOutput">블록 추가 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>블록 삭제</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="deleteBlockId">블록 ID:</label>
<input type="text" id="deleteBlockId" placeholder="삭제할 블록 ID">
</div>
<button id="deleteBlockButton">블록 삭제</button>
<div id="deleteBlockResult" class="result">
<h3>삭제 결과:</h3>
<pre id="deleteBlockOutput">블록 삭제 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
</div>
<!-- 사용자 탭 -->
<div class="tab-content" id="users-tab">
<div class="api-section">
<div class="collapsible">
<h2>사용자 목록 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<button id="usersButton">사용자 목록 조회</button>
<div id="usersResult" class="result">
<h3>사용자 목록:</h3>
<pre id="usersOutput">사용자 목록이 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>사용자 정보 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="userId">사용자 ID:</label>
<input type="text" id="userId" placeholder="사용자 ID를 입력하세요">
</div>
<button id="userButton">사용자 조회</button>
<div id="userResult" class="result">
<h3>사용자 정보:</h3>
<pre id="userOutput">사용자 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>자신의 사용자 정보 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<button id="meButton">내 정보 조회</button>
<div id="meResult" class="result">
<h3>내 정보:</h3>
<pre id="meOutput">내 정보가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
</div>
<!-- 코멘트 탭 -->
<div class="tab-content" id="comments-tab">
<div class="api-section">
<div class="collapsible">
<h2>코멘트 조회</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="commentsBlockId">블록 ID:</label>
<input type="text" id="commentsBlockId" placeholder="코멘트를 조회할 블록 ID">
</div>
<button id="commentsButton">코멘트 조회</button>
<div id="commentsResult" class="result">
<h3>코멘트 목록:</h3>
<pre id="commentsOutput">코멘트 목록이 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
<div class="api-section">
<div class="collapsible">
<h2>코멘트 생성</h2>
</div>
<div class="collapsible-content">
<div class="card">
<div class="form-group">
<label for="createCommentParent">페이지 ID:</label>
<input type="text" id="createCommentParent" placeholder="코멘트를 생성할 페이지 ID">
</div>
<div class="form-group">
<label for="createCommentText">코멘트 내용:</label>
<textarea id="createCommentText" placeholder="코멘트 내용을 입력하세요"></textarea>
</div>
<button id="createCommentButton">코멘트 생성</button>
<div id="createCommentResult" class="result">
<h3>생성 결과:</h3>
<pre id="createCommentOutput">코멘트 생성 결과가 여기에 표시됩니다.</pre>
</div>
</div>
</div>
</div>
</div>
<script>
// API 기본 URL
const API_BASE_URL = 'http://localhost:3000/api';
// 탭 기능
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
this.classList.add('active');
document.getElementById(`${this.dataset.tab}-tab`).classList.add('active');
});
});
// 접을 수 있는 섹션
document.querySelectorAll('.collapsible').forEach(coll => {
coll.addEventListener('click', function() {
const content = this.nextElementSibling;
content.classList.toggle('active');
});
});
// API 통신 공통 함수
async function apiCall(endpoint, method = 'GET', body = null) {
try {
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
return await response.json();
} catch (error) {
return { error: error.message };
}
}
// ========= 검색 API =========
document.getElementById('searchButton').addEventListener('click', async function() {
const query = document.getElementById('searchQuery').value;
const filterValue = document.getElementById('searchFilter').value;
const output = document.getElementById('searchOutput');
output.textContent = '로딩 중...';
const body = { query };
if (filterValue) {
body.filter = {
property: 'object',
value: filterValue
};
}
const data = await apiCall('/search', 'POST', body);
output.textContent = JSON.stringify(data, null, 2);
});
// ========= 페이지 API =========
// 페이지 조회
document.getElementById('pageButton').addEventListener('click', async function() {
const pageId = document.getElementById('pageId').value;
const output = document.getElementById('pageOutput');
if (!pageId) {
output.textContent = '페이지 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/pages/${pageId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// 페이지 생성
document.getElementById('createPageButton').addEventListener('click', async function() {
const pageParent = document.getElementById('createPageParent').value;
const pageTitle = document.getElementById('createPageTitle').value;
const pageJson = document.getElementById('createPageJson').value;
const output = document.getElementById('createPageOutput');
output.textContent = '로딩 중...';
let body;
try {
if (pageJson.trim()) {
body = JSON.parse(pageJson);
} else {
body = {
parent: { page_id: pageParent },
properties: {
title: {
title: [{ text: { content: pageTitle } }]
}
}
};
}
const data = await apiCall('/pages', 'POST', body);
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = `오류 발생: ${error.message}`;
}
});
// 페이지 업데이트
document.getElementById('updatePageButton').addEventListener('click', async function() {
const pageId = document.getElementById('updatePageId').value;
const pageJson = document.getElementById('updatePageJson').value;
const output = document.getElementById('updatePageOutput');
if (!pageId) {
output.textContent = '페이지 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
try {
const body = pageJson.trim() ? JSON.parse(pageJson) : {};
const data = await apiCall(`/pages/${pageId}`, 'PATCH', body);
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = `오류 발생: ${error.message}`;
}
});
// 페이지 속성 조회
document.getElementById('propertyButton').addEventListener('click', async function() {
const pageId = document.getElementById('propertyPageId').value;
const propertyId = document.getElementById('propertyId').value;
const output = document.getElementById('propertyOutput');
if (!pageId || !propertyId) {
output.textContent = '페이지 ID와 속성 ID를 모두 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/pages/${pageId}/properties/${propertyId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// ========= 데이터베이스 API =========
// 데이터베이스 조회
document.getElementById('databaseButton').addEventListener('click', async function() {
const databaseId = document.getElementById('databaseId').value;
const output = document.getElementById('databaseOutput');
if (!databaseId) {
output.textContent = '데이터베이스 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/databases/${databaseId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// 데이터베이스 생성
document.getElementById('createDatabaseButton').addEventListener('click', async function() {
const parentId = document.getElementById('createDatabaseParent').value;
const title = document.getElementById('createDatabaseTitle').value;
const json = document.getElementById('createDatabaseJson').value;
const output = document.getElementById('createDatabaseOutput');
output.textContent = '로딩 중...';
try {
let body;
if (json.trim()) {
body = JSON.parse(json);
} else {
body = {
parent: { page_id: parentId },
title: [{ text: { content: title } }],
properties: { 이름: { title: {} } }
};
}
const data = await apiCall('/databases', 'POST', body);
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = `오류 발생: ${error.message}`;
}
});
// 데이터베이스 쿼리
document.getElementById('queryDatabaseButton').addEventListener('click', async function() {
const databaseId = document.getElementById('queryDatabaseId').value;
const queryJson = document.getElementById('queryDatabaseJson').value;
const output = document.getElementById('queryDatabaseOutput');
if (!databaseId) {
output.textContent = '데이터베이스 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
try {
const body = queryJson.trim() ? JSON.parse(queryJson) : {};
const data = await apiCall(`/databases/${databaseId}/query`, 'POST', body);
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = `오류 발생: ${error.message}`;
}
});
// ========= 블록 API =========
// 블록 조회
document.getElementById('blockButton').addEventListener('click', async function() {
const blockId = document.getElementById('blockId').value;
const output = document.getElementById('blockOutput');
if (!blockId) {
output.textContent = '블록 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/blocks/${blockId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// 블록 내용 조회
document.getElementById('blockChildrenButton').addEventListener('click', async function() {
const blockId = document.getElementById('blockChildrenId').value;
const output = document.getElementById('blockChildrenOutput');
if (!blockId) {
output.textContent = '블록 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/blocks/${blockId}/children`);
output.textContent = JSON.stringify(data, null, 2);
});
// 블록 추가
document.getElementById('appendBlockButton').addEventListener('click', async function() {
const blockId = document.getElementById('appendBlockId').value;
const blockJson = document.getElementById('appendBlockJson').value;
const output = document.getElementById('appendBlockOutput');
if (!blockId) {
output.textContent = '블록 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
try {
const body = blockJson.trim() ? JSON.parse(blockJson) : { children: [] };
const data = await apiCall(`/blocks/${blockId}/children`, 'PATCH', body);
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = `오류 발생: ${error.message}`;
}
});
// 블록 삭제
document.getElementById('deleteBlockButton').addEventListener('click', async function() {
const blockId = document.getElementById('deleteBlockId').value;
const output = document.getElementById('deleteBlockOutput');
if (!blockId) {
output.textContent = '블록 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/blocks/${blockId}`, 'DELETE');
output.textContent = JSON.stringify(data, null, 2);
});
// ========= 사용자 API =========
// 사용자 목록 조회
document.getElementById('usersButton').addEventListener('click', async function() {
const output = document.getElementById('usersOutput');
output.textContent = '로딩 중...';
const data = await apiCall('/users');
output.textContent = JSON.stringify(data, null, 2);
});
// 사용자 조회
document.getElementById('userButton').addEventListener('click', async function() {
const userId = document.getElementById('userId').value;
const output = document.getElementById('userOutput');
if (!userId) {
output.textContent = '사용자 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/users/${userId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// 자신의 사용자 정보 조회
document.getElementById('meButton').addEventListener('click', async function() {
const output = document.getElementById('meOutput');
output.textContent = '로딩 중...';
const data = await apiCall('/users/me');
output.textContent = JSON.stringify(data, null, 2);
});
// ========= 코멘트 API =========
// 코멘트 조회
document.getElementById('commentsButton').addEventListener('click', async function() {
const blockId = document.getElementById('commentsBlockId').value;
const output = document.getElementById('commentsOutput');
if (!blockId) {
output.textContent = '블록 ID를 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const data = await apiCall(`/comments?block_id=${blockId}`);
output.textContent = JSON.stringify(data, null, 2);
});
// 코멘트 생성
document.getElementById('createCommentButton').addEventListener('click', async function() {
const pageId = document.getElementById('createCommentParent').value;
const text = document.getElementById('createCommentText').value;
const output = document.getElementById('createCommentOutput');
if (!pageId || !text) {
output.textContent = '페이지 ID와 코멘트 내용을 모두 입력해주세요.';
return;
}
output.textContent = '로딩 중...';
const body = {
parent: { page_id: pageId },
rich_text: [{ text: { content: text } }]
};
const data = await apiCall('/comments', 'POST', body);
output.textContent = JSON.stringify(data, null, 2);
});
</script>
</body>
</html>