🌾
【AppSheet】REST APIの作成方法
Google workspace系でアプリケーションを作成するには、これまで主にスプレッドシートをDB代わりに用いてきましたが、GASで自作クエリを作成するのはいろいろ面倒ですし、データの取得や書き込みが非常に遅いです。
今回ふと思い立ってAppSheetをDBとして使ったところ、実装の手間もそれほどかからず動作も早かったので、非常に便利だということがわかりました。
公式ドキュメントはこちら
ですが、公式ドキュメントはなかなかわかりづらいので、メモとして残します。
DBの作成
1. 注文履歴
2. 注文アイテム
3. 備品マスタ
必ずやっておくこと
AppSheetエディタで、各テーブルをDataに加えておく必要があります。
(これをやらないと、REST APIを実行しても何も書き込めず返されません。)
REST APIの作成
共通
setting > integrationsでApp Idを確認することができます。
また、Access Keyを発行することができます。
これらは以下のREST APIで使うことになります。
const APP_ID = "7b6b4413-ec3a-4d5d-b9d0-*******"
const ACCESS_KEY = "V2-************************"
CREATE
payloadのActionは"Add"を指定します。
function create注文アイテム() {
const TABLE_NAME = "注文アイテム"
const url = `https://www.appsheet.com/api/v2/apps/${APP_ID}/tables/${TABLE_NAME}/Action?applicationAccessKey=${ACCESS_KEY}`
const rows = [
{"アイテムコード":'item0002',"備品コード":'ballpen',"備品名":'黒色ボールペン',"金額":2000}
]
const payload = {
"Action":"Add",
"Properties":{
"Locale":"ja-JP"
},
"Rows":rows
}
const options = {
"method":"post",
"contentType":"application/json",
"payload":JSON.stringify(payload)
}
UrlFetchApp.fetch(url,options)
}
READ
payloadのActionは"Find"を指定します。
selectorに色々な検索式を入れることができます。
下の例では、「備品マスタ」テーブルの「格納場所」列に「実験室」が入っているレコードだけを取り出しています。
function get備品マスタ(){
const TABLE_NAME = "備品マスタ"
const url = `https://www.appsheet.com/api/v2/apps/${APP_ID}/tables/${TABLE_NAME}/Action?applicationAccessKey=${ACCESS_KEY}`
const payload = {
"Action":"Find",
"Properties":{
"Locale":"ja-JP",
"Selector": "FILTER('備品マスタ', IN('実験室', [格納場所]))"
}
}
const options = {
"method":"post",
"contentType":"application/json",
"payload":JSON.stringify(payload)
}
try {
const res = UrlFetchApp.fetch(url, options);
const responseText = res.getContentText();
console.log("Response Data: ", responseText);
} catch (error) {
console.error("Error: " + error.toString());
}
}
UPDATE
更新は一レコードずつではなく、まとめて更新します(以下の例ではrowsに入れています)
「注文アイテム」テーブルでは、「アイテムコード」がkeyとなっています。
更新すべきレコードは「アイテムコード」を指定していますので、APIはそのアイテムコードを手掛かりに該当するレコードを更新します。
function update注文アイテム(){
const TABLE_NAME = "注文アイテム"
const url = `https://api.appsheet.com/api/v2/apps/${APP_ID}/tables/${TABLE_NAME}/Action?applicationAccessKey=${ACCESS_KEY}`
const rows = [
{"アイテムコード":'item0003',"備品コード":'ballpen',"備品名":'黒色ボールペン',"金額":3000}
]
const payload = {
"Action":"Edit",
"Properties":{
"Locale":"ja-JP"
},
"Rows":rows
}
const options = {
"method":"post",
"contentType":"application/json",
"payload":JSON.stringify(payload)
}
UrlFetchApp.fetch(url,options)
}
Nodejs
クリックで展開
<!DOCTYPE html>
<html>
<head>
<script>
const TABLE_NAME = "Table 1"
const APP_ID = '203e9cf7-d9c0-49a5-a1ca-6849094bb282'
const ACCESS_KEY = 'V2-AZWbc-ELpfC-x6lCG-LB1y0-96p3q-la2gT-cYKyO-BMvPv'
const url = `https://www.appsheet.com/api/v2/apps/${APP_ID}/tables/${TABLE_NAME}/Action?applicationAccessKey=${ACCESS_KEY}`;
async function getData() {
const payload = {
Action: "Find",
Properties: {
Locale: "ja-JP",
// Selectorを省略すると、全データを取得する
// Selector:"SELECT(備品マスタ[備品コード],TRUE)" // 備品コードが何で合っても取得する(つまり全データが取得される)
// Selector:"FILTER('備品マスタ',[備品コード] = 'ballpen')" // '備品コード'が'ballpen'のレコード
//Selector:"FILTER('備品マスタ',[備品コード] = 'ballpen')" // '備品コード'が'ballpen'のレコード
// Selector: "FILTER('備品マスタ', IN('実験室', [格納場所]))" // EnumListの'格納場所'に'実験室'が含まれるレコード
}
};
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
};
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
// return await response.json()
} catch (error) {
console.error("Error: " + error.message);
return error.message
}
}
async function addData() {
const payload = {
Action: "Add",
Properties: {
Locale: "ja-JP",
},
Rows:[
{
category:'保健体育'
// 備品コード:"note",
// 備品名:"ノート",
// 格納場所:"居室 , 実験室",
// 価格:140,
// 発注単位:10,
// 申込番号:"mono-4",
// 発注先名:"モノタロウ",
// JANコード:4987072088980
}
]
};
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
};
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
// return await response.json()
} catch (error) {
console.error("Error: " + error);
return error.message
}
}
async function updateData() {
const payload = {
Action: "Edit",
Properties: {
Locale: "ja-JP",
},
Rows:[
{
'Row ID':"ZOiLnf4HQz45yF_W686ZT1",
fileName:'aaa'
}
]
};
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
};
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
// return await response.json()
} catch (error) {
console.error("Error: " + error);
return error.message
}
}
async function deleteData() {
const payload = {
Action: "Delete",
Properties: {
Locale: "ja-JP",
},
Rows:[
{
備品コード:"note",
}
]
};
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
};
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
// return await response.json()
} catch (error) {
console.error("Error: " + error);
return error.message
}
}
const doFunction=()=>{
console.time('test')
// getData()
// .then(res=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = res
// console.timeEnd('test')
// })
// .catch(err=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = err
// })
// addData()
// .then(res=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = res
// })
// .catch(err=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = err
// })
updateData()
.then(res=>{
const dataDisplayElem = document.getElementById("data-display")
dataDisplayElem.innerText = res
})
.catch(err=>{
const dataDisplayElem = document.getElementById("data-display")
dataDisplayElem.innerText = err
})
// deleteData()
// .then(res=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = res
// })
// .catch(err=>{
// const dataDisplayElem = document.getElementById("data-display")
// dataDisplayElem.innerText = err
// })
}
</script>
</head>
<body>
<button type="button" onclick=doFunction()>テスト</button>
<div id="data-display">
ここに取得したデータが表示されます
</div>
</body>
</html>
export default (APP_ID:string, ACCESS_KEY:string, TABLE_NAME:string)=>{
const request = async(payload)=>{
const url = `https://www.appsheet.com/api/v2/apps/${APP_ID}/tables/${TABLE_NAME}/Action?applicationAccessKey=${ACCESS_KEY}`;
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
};
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
// return await response.json()
} catch (error) {
console.error("Error: " + error.message);
return error.message
}
}
type Operator = "=="|">="|"<="
const getRows=(fieldName:string, operator:Operator, parameter:string)=>{
const payload = {
Action: "Find",
Properties: {
Locale: "ja-JP",
// Selectorを省略すると、全データを取得する
// Selector:"SELECT(備品マスタ[備品コード],TRUE)" // 備品コードが何で合っても取得する(つまり全データが取得される)
// Selector:"FILTER('備品マスタ',[備品コード] = 'ballpen')" // '備品コード'が'ballpen'のレコード
Selector:"FILTER('備品マスタ',[備品コード] = 'ballpen')" // '備品コード'が'ballpen'のレコード
// Selector: "FILTER('備品マスタ', IN('実験室', [格納場所]))" // EnumListの'格納場所'に'実験室'が含まれるレコード
}
};
var selector;
switch(operator){
case "==":{
selector = `SELECT(${TABLE_NAME})`
}
}
return request(payload)
}
const addRows=()=>{
const payload = {
Action: "Add",
Properties: {
Locale: "ja-JP",
},
Rows:[
{
備品コード:"note",
備品名:"ノート",
格納場所:"居室 , 実験室",
価格:140,
発注単位:10,
申込番号:"mono-4",
発注先名:"モノタロウ",
JANコード:4987072088980
}
]
};
return request(payload)
}
const updateRows=(rows:object[])=>{
const payload = {
Action: "Edit",
Properties: {
Locale: "ja-JP",
},
Rows:rows
};
return request(payload)
}
const deleteRows=(rows:object[])=>{
const payload = {
Action: "Delete",
Properties: {
Locale: "ja-JP",
},
Rows:rows
};
return request(payload)
}
return {getRows, addRows, updateRows, deleteRows}
}
Discussion