ENotion is a Kotlin library for interacting with the Notion API, supporting the conversion of Notion database content into HTML format.
-
Database Query –
getDataBase
Query any Notion database with optional filter and sorts.
Each page is returned as a JSON object whosecontentfield already contains fully‑rendered HTML for all supported block types. -
Record Insertion –
insertRecord
Insert a row into a database while automatically converting Kotlin/SQL values to the correct Notion property structure, with optional markdownContent rendered as rich Notion blocks. The legacy overloadinsertRecord(databaseId, vararg fields)remains available for backward compatibility. -
Record Update –
updateRecord
Patch one or more columns of an existing page with the same automatic type handling as insertRecord, and optionally append converted Markdown content. The legacy overloadupdateRecord(databaseId, pageId, vararg fields)remains available for backward compatibility. -
Markdown Content Support –
markdownContent
TheinsertRecordandupdateRecordmethods now accept an optional markdownContent parameter.- Converts full Markdown (headings 1‑3, paragraphs, images, tables, lists, block quotes, horizontal rules, inline *
bold* / italic /
code, links, strike‑through) into native Notion blocks on‑the‑fly. - Images inside tables are automatically extracted and appended with captions to satisfy Notion API constraints.
- Supports an optional high‑fidelity converter via Node’s
@tryfabric/martianwhen available; otherwise falls back to the built‑in Kotlin converter. - Code blocks normalize languages to Notion’s allowed list and degrade to
plain textwhen unknown, preventing 400 validation errors.
- Converts full Markdown (headings 1‑3, paragraphs, images, tables, lists, block quotes, horizontal rules, inline *
bold* / italic /
-
Data image auto‑upload
Markdowndata:image/...;base64,...blobs are uploaded automatically via Notion's Direct Upload API and rendered asfile_uploadimage blocks. Optionally provide adataImageUploadercallback to override the behaviour and return anexternalUrlorfileUploadId. On failure the content falls back to a paragraph to avoid Notion validation errors. -
HTML Rendering & Safety
HTML output fromgetDataBaseescapes text and attributes and usesrel="noopener noreferrer"on links. -
Video Blocks
getDataBaserenders Notionvideoblocks with a default 640x360 footprint, streaming uploaded files via<video>and auto-converting YouTube/Vimeo links into embeddable<iframe>players (falling back to external links for other providers). -
Workspace Search –
findNotionDatabase
Locate a database by its exact title and return its ID. -
Database Creation –
createNotionDatabase
Programmatically create a brand‑new database with a custom schema under any parent page. -
Schema Management –
ensureDatabaseSchema
Compare the remote schema with local column definitions and add any missing properties on‑the‑fly. -
Timestamp Helper –
getLatestTimestamp
Quickly obtain the newest timestamp stored in a designateddate,rich_text, ortitlecolumn and normalize it toyyyy-MM-dd HH:mm:ss. Supported inputs includeISO_OFFSET_DATE_TIME,yyyy-MM-dd HH:mm:ssXXX,yyyy-MM-dd HH:mm:ss.SSSXXX,yyyy-MM-dd HH:mm:ss, andyyyy-MM-dd HH:mm. -
Miscellaneous Utilities
Helpers for reading a database’spropertiesobject, mapping SQL types to Notion types, and extracting primitive values from Notion properties.
Add ENotion to your project and initialize it with your Notion API key:
import easy.notion.ENotion
import java.io.FileInputStream
import java.util.Properties
fun main() {
val props = Properties().apply { load(FileInputStream("config.properties")) }
val notion = ENotion(props.getProperty("NOTIONKEY"))
println(notion.getDataBase("your_database_id"))
}val dbId = notion.findNotionDatabase("My Tasks")
if (dbId == null) println("Database not found") else println("ID = $dbId")val newDbId = notion.createNotionDatabase(
pageId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // parent page
databaseName = "Log Entries",
columns = mapOf("Level" to "select", "Message" to "rich_text"),
timeField = "Created",
primaryKey = "ID",
)// insert (with Markdown body)
notion.insertRecord(
databaseId = newDbId!!,
markdownContent = """
# Welcome
This record was **created** via *ENotion*.
---
| Picture | Description |
|:------:|-------------|
|  | Cute kitten |
""".trimIndent(),
"ID" to "42",
"Level" to "INFO",
"Message" to "Application started",
"Created" to "2025-05-25 10:00:00",
)
// legacy overload remains valid
notion.insertRecord(
newDbId,
"ID" to "43",
"Level" to "INFO",
)
// update (patch fields + append more Markdown)
notion.updateRecord(
databaseId = newDbId,
pageId = "pppppppppppppppppppppppppppppppp",
markdownContent = "- **现价**:¥208",
"Level" to "ERROR",
"Message" to "Oops, something went wrong",
)
// legacy overload remains valid
notion.updateRecord(
newDbId,
"pppppppppppppppppppppppppppppppp",
"Level" to "WARN",
)
// Optional: data image auto‑upload (custom uploader example)
val notionWithUploader = ENotion(
apikey = props.getProperty("NOTIONKEY"),
dataImageUploader = { mime, bytes, suggestedName ->
// Upload bytes to your hosting and return an https URL. Example only:
val url = myUpload(bytes, suggestedName ?: "image", mime) // implement yourself
if (url != null) ENotion.DataImageUploadResult(externalUrl = url) else null
}
)val ok = notion.ensureDatabaseSchema(
databaseId = newDbId,
localColumns = mapOf("ID" to "VARCHAR", "Level" to "VARCHAR", "Detail" to "TEXT"),
timeField = "Created",
primaryKey = "ID",
)Convert Notion database content to HTML:
<style>
hr { border: none; border-top: 1px solid #E1E3E5; margin: 16px 0; }
table { border-collapse: collapse; border: 1px solid #E1E3E5; }
th, td { border: 1px solid #E1E3E5; padding: 8px; text-align: left; }
thead th { background-color: #f2f2f2; }
tbody th { background-color: #f2f2f2; }
img { max-width: 600px; }
a { text-decoration: underline; }
pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow: auto; }
pre code { background: transparent; padding: 0; }
code { background: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-family: monospace; }
</style>
<p>Sample paragraph content</p>
<h1>Sample Heading</h1>
<ul>
<li>Sample list item</li>
</ul>
<table style="width:600px;">
<thead>
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
</tr>
</tbody>
</table>The library automatically prepends a lightweight default stylesheet (visible in the snippet above) so the HTML is ready for direct embedding.
Note
When you supply markdownContent to insertRecord / updateRecord, ENotion converts the Markdown to Notion blocks first, so the HTML produced bygetDataBasewill precisely match your original Markdown—including images, tables, and dividers.
Create a config.properties file in the project root directory and add the following content:
NOTIONKEY=your_notion_api_key
# Optional for examples
# DATABASEID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Optional: for high‑fidelity Markdown conversion, install Node and Martian globally:
npm i -g @tryfabric/martian
- Language: Kotlin
- HTTP: OkHttp 4.x
- JSON: org.json
- Build Tool: Gradle (Maven optional)
- Build:
./gradlew build - Run tests only:
./gradlew test - Publish to local Maven:
./gradlew publishMavenJavaPublicationToMavenLocal
For a quick online example (manual, not a unit test), run src/test/kotlin/TestNotionTools.kt in your IDE with a valid
config.properties in the project root.
- Ensure that the
config.propertiesfile is not committed to version control (already configured in.gitignore). - Make sure the Notion API key and database ID are valid before use.
This project is licensed under the MIT License.