【Notion API+Ktor】MultiplatformアプリからNotionのDBにjsonデータを送信する
はじめに
Compose Multiplatformのアプリ(iOS/Android)で、ユーザーのフィードバックをアプリ内で入力・送信してもらい、受けられるようにしたい。
- ずっと無料で
- 楽に実装でき
- DBとして保存できて
- Slackに通知がいく
ようにしたい。
上記の要求をすべて満たすのが、Notion APIを使って、NotionのDBに送信するという方法でした。送信する形式は、汎用性の点からjson形式にしました。
意外とダイレクトな情報がなかったので、備忘録および同じようなことをしたい方の時短のために、記事にさせていただきます。
流れ
- データを受け取る準備 @Notion
- Notion APIキーの追加 @AndroidStudio
- Ktorの導入 @AndroidStuido
- 送信するデータの実装 @AndroidStudio
- データ送信のためのUI実装 @AndroidStudio
- Notionにデータを送信する実装 @AndroidStudio
- Slackに通知 @Notion/Slack
1. データを受け取る準備 @Notion
APIキーの取得
まず、Notion側にAPIを使用するための設定を行います。
Notionのインテグレーションページから、新しいインテグレーションを作成します。
すると、作成したインテグレーション固有のAPIキーを取得できます。(漏洩に注意)
DBの作成
続いて、NotionにDB用のページを作成し、「テーブル」テンプレートを選択します。
「データソースを選択する」から、「新規データベース」を作成します。
続いて、データベースの各プロパティ名と形式を好きなように変更します。プロパティ名は、jsonで扱うときのキー名と一致している必要があります。
デフォルトのこれを、
例えばこう変更します。
DBにコネクトの追加
最初に作成したインテグレーションと、DBを紐づけます。画面右上のボタン(3つのドット)から「コネクトの追加」を選び、作成したインテグレーションを選択します。
これでNotion側の準備は完了です。
2. Notion APIキーの追加 @AndroidStudio
Android Studioでの作業に移ります。
NotionのAPIキーを安全な形でアクセスできるようにします。
Multiplatformのプロジェクトなので、「BuildKonfig」を使いました。詳細は別記事にて説明予定です。
基本的に、値はlocal.properties
に記述します。
notion.api.key=NotionのAPIキー
notion.db.id=NotionのDBのID(DBのページをブラウザで開いた時の、?v=より前の32桁の値)
3. Ktorの導入 @AndroidStuido
Multiplatformで、HTTP通信クライアントとしてWizardにて推奨されている
「Ktor」を導入します。
Ktorのバージョン2.3.5
では、公式の通りに進めるとio.ktor:ktor-lient-cio
がiOSにないよ、と怒られてしまいビルドできないのですが、Wizardを参考にするとAndrdoidとDesktopでは"io.ktor:ktor-client-okhttp
を、iOSではio.ktor:ktor-client-darwin
を使うようにすることで解消できるようです。
こちらも詳細は別記事にて。
4. 送信するデータの実装 @AndroidStudio
今回送信するデータは、以下のような形式を想定します。
形式 | キー名 | 説明 |
---|---|---|
title | user_id | ユーザーID |
select | evaluation | 選択肢(Good/Bad) |
rich-text | message | フィードバック内容 |
Notion APIのデータベースセクションを参考に進めます。
Database propertyページで、他の形式についても説明があります。
jsonに変換できるよう、kotlin-serializationを使ってデータクラスを作成していきます。Notionで送信されるjsonは、Body -> Parent,Properties という階層関係になっているため、それらも用意してあげます。
@Serializable
data class Body(
val parent: Parent,
val properties: Properties,
)
@Serializable
data class Parent(
val database_id: String,
)
@Serializable
data class Properties(
val user_id: UserId,
val evaluation: Evaluation,
val message: Message,
)
@Serializable
data class UserId(
val title: List<TitleText>,
)
@Serializable
data class TitleText(
val text: Content,
)
@Serializable
data class Content(
val content: String,
)
@Serializable
data class Evaluation(
val select: Select,
)
@Serializable
data class Select(
val name: String,
)
enum class Usability() {
Good,
Bad,
Soso,
}
@Serializable
data class Message(
val rich_text: List<RichText>,
)
@Serializable
data class RichText(
val text: RichTextValue,
val plain_text: String,
val href: String?,
)
@Serializable
data class RichTextValue(
val content: String,
val link: String?,
)
これで送信するデータの準備ができました。
5. データ送信のためのUI実装 @AndroidStudio
続いて、用意したデータクラスの中身をユーザーが入力できるようにするUIをこしらえます。
詳細はサンプルプロジェクトをご覧ください。UserIdの取得については別記事にて説明します。
入力内容の内部データクラスと、それの変換用拡張関数
data class FeedbackUiState(
val userId: String = "",
val evaluation: Usability = Usability.Good,
val message: String = "",
)
fun FeedbackUiState.toBody(): Body {
return Body(
parent = Parent(database_id = BuildKonfig.NOTION_DB_ID),
properties = Properties(
user_id = UserId(
title = listOf(
TitleText(
text = Content(
content = userId,
),
),
),
),
evaluation = Evaluation(
select = Select(
name = evaluation.name,
),
),
message = Message(
rich_text = listOf(
RichText(
text = RichTextValue(
content = message,
link = null,
),
plain_text = message,
href = null,
),
),
),
),
)
}
入力画面のUI
@Composable
fun FeedbackInputScreen(
modifier: Modifier = Modifier,
) {
val uiState = remember { mutableStateOf(FeedbackUiState()) }
var isMenuExpanded by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Column(modifier = modifier) {
Text(text = "Evaluation")
Box(
modifier = Modifier
.clickable { isMenuExpanded = true }
.border(0.8.dp, MaterialTheme.colorScheme.onSurface, MaterialTheme.shapes.small)
.width(200.dp)
) {
Text(
text = uiState.value.evaluation.name,
modifier = Modifier.padding(8.dp),
)
DropdownMenu(
expanded = isMenuExpanded,
onDismissRequest = { isMenuExpanded = false },
modifier = Modifier.width(200.dp)
) {
Usability.values().forEach { usability ->
DropdownMenuItem(
text = { Text(text = usability.name) },
onClick = {
uiState.value = uiState.value.copy(evaluation = usability)
isMenuExpanded = false
})
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Text(text = "Message")
OutlinedTextField(
value = uiState.value.message,
onValueChange = { uiState.value = uiState.value.copy(message = it) },
)
Button(onClick = {
scope.launch(Dispatchers.IO) {
KtorClient.sendFeedback(uiState.value.toBody(), afterAction = {
println("NotionResponse: sent successfully")
uiState.value = uiState.value.copy(message = "")
})
}
}) {
Text(text = "Send")
}
}
}
6. Notionにデータを送信する実装 @AndroidStudio
いよいよKtorのクライアントを使って、Notionにデータを送信する実装です。
まず、AndroidManifestに、Internetのパーミッションを追加しておきます。
<uses-permission android:name="android.permission.INTERNET" />
以下が、Ktorでの送信処理です。Validation関連の実装は公式のガイドを参考にしました。
object KtorClient {
suspend fun sendFeedback(
body: Body,
afterAction: () -> Unit,
) {
val response = HttpClient() {
expectSuccess = true
HttpResponseValidator {
handleResponseExceptionWithRequest { exception, request ->
val clientException = exception as? ClientRequestException ?: return@handleResponseExceptionWithRequest
println("NotionResponse: ${clientException.response}")
}
}
}.use { client ->
client.post(
urlString = "https://api.notion.com/v1/pages",
block = {
headers {
append(HttpHeaders.Authorization, "Bearer BuildKonfig.NOTION_API_KEY")
append("Content-Type", "application/json")
append("Notion-Version", "2022-06-28")
}
setBody(Json.encodeToString(body))
}
)
}
if (response.status.isSuccess()) {
afterAction()
}
}
}
ここで、アプリをビルドして、入力項目を埋めて送信すると、NotionのDBに保存されるはずです!
実際の画面の様子。
うまくいくと、値が追加されているはずです。
7. Slackに通知 @Notion/Slack
最後に、NotionのDBに値が追加されたときに、Slackに通知がいくように設定します。
NotionのDBのページで、右上少し下の、「オートメーションボタン」(稲妻アイコン)をクリックし、新規オートメーションを作成します。
好きな名前をつけて、「トリガーを追加」に「ページを追加」を、「アクションを追加」に「Slack通知を送信」を選択します。
Slackとの接続がまだの場合は、ここでSlackに接続のボタンが出るので、指示通りに進めてNotionとSlackを連携させます。
無事に接続されると、Slackチャンネルが選択できるようになるので、通知したいチャンネルを選び、「作成」ボタンを押せば完了です。
おわりに
無料でここまで便利なことができてしまうって本当にすごい。感動です。
GitHub, Notion, Slack, Figmaの四天王が存在する以前と以降で、本当に世界は変わったと思います。
サンプルプロジェクトのGitHubリポジトリはこちらで公開しています。サクッと試してみたいときや、細かい確認用にどうぞ。
Discussion