👌

ArangoDBのFoxxを始めるまで

2021/09/17に公開

ArangoDBとは

https://www.arangodb.com/
簡単に言えばDocument Database + Graph Database + Key/Value Store…まあNoSQL

Foxxとは

https://www.arangodb.com/docs/stable/foxx.html
ArangoDBに組み込んで独自RestAPI提供できるシステム

始め方

https://www.arangodb.com/docs/stable/foxx-getting-started.html
をまとめた感じです

必要ファイルの用意

manifest.json

{
  "engines": {
    "arangodb": "^3.0.0"
  },
  "main": "index.js",
  "scripts": {
    "setup": "scripts/setup.js"
  }
}

enginesでサポートするArangoDBのバージョン指定をする
mainで実行するプログラムとなる
scripts.setupでセットアップ用プログラムとなる(ここで必要コレクション(=DBで言うところのテーブル)など作成する)

index.js

'use strict';

// joi 値チェック用ライブラリ
const joi = require('joi');

// ArangoDB使用するためのライブラリ
const db = require('@arangodb').db;
const errors = require('@arangodb').errors;
const foxxColl = db._collection('myFoxxCollection');
const DOC_NOT_FOUND = errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code;
const aql = require('@arangodb').aql;

// foxx用ルータ
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();
module.context.use(router);

//===================================================================
// APIアクセス(/hello-world)
//===================================================================
router.get('/hello-world', function (req, res) {
  res.send('Hello World2!');
})
.response(['text/plain'], 'A generic greeting.')
.summary('Generic greeting')
.description('Prints a generic greeting.');

//===================================================================
// APIアクセス(/hello/:name)
//===================================================================
router.get('/hello/:name', function (req, res) {
  res.send(`Hello ${req.pathParams.name}`);
})
.pathParam('name', joi.string().required(), 'Name to greet.')
.response(['text/plain'], 'A personalized greeting.')
.summary('Personalized greeting')
.description('Prints a personalized greeting.');

//===================================================================
// APIアクセス(/sum)
//===================================================================
router.post('/sum', function (req, res) {
  const values = req.body.values;
  res.send({
    result: values.reduce(function (a, b) {
      return a + b;
    }, 0)
  });
})
.body(joi.object({
  values: joi.array().items(joi.number().required()).required()
}).required(), 'Values to add together.')
.response(joi.object({
  result: joi.number().required()
}).required(), 'Sum of the input values.')
.summary('Add up numbers')
.description('Calculates the sum of an array of number values.');

// store schema in variable to make it re-usable, see .body()
const docSchema = joi.object().required().keys({
  name: joi.string().required(),
  age: joi.number().required()
}).unknown(); // allow additional attributes

//===================================================================
// APIアクセス(/entries)
//===================================================================
router.post('/entries', function (req, res) {
  const multiple = Array.isArray(req.body);
  const body = multiple ? req.body : [req.body];

  let data = [];
  for (var doc of body) {
    const meta = foxxColl.save(doc);
    data.push(Object.assign(doc, meta));
  }
  res.send(multiple ? data : data[0]);

})
.body(joi.alternatives().try(
  docSchema,
  joi.array().items(docSchema)
), 'Entry or entries to store in the collection.')
.response(joi.alternatives().try(
  joi.object().required(),
  joi.array().items(joi.object().required())
), 'Entry or entries stored in the collection.')
.summary('Store entry or entries')
.description('Store a single entry or multiple entries in the "myFoxxCollection" collection.');

//===================================================================
// APIアクセス(/entries/:key)
//===================================================================
router.get('/entries/:key', function (req, res) {
  try {
    const data = foxxColl.document(req.pathParams.key);
    res.send(data)
  } catch (e) {
    if (!e.isArangoError || e.errorNum !== DOC_NOT_FOUND) {
      throw e;
    }
    res.throw(404, 'The entry does not exist', e);
  }
})
.pathParam('key', joi.string().required(), 'Key of the entry.')
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Retrieve an entry')
.description('Retrieves an entry from the "myFoxxCollection" collection by key.');

//===================================================================
// APIアクセス(/entries)
//===================================================================
router.get('/entries', function (req, res) {
  const keys = db._query(aql`
    FOR entry IN ${foxxColl}
    RETURN entry._key
  `);
  res.send(keys);
})
.response(joi.array().items(
  joi.string().required()
).required(), 'List of entry keys.')
.summary('List entry keys')
.description('Assembles a list of keys of entries in the collection.');

scripts/setup.js

'use strict';

// ArangoDB使用するためのライブラリ
const db = require('@arangodb').db;
const collectionName = 'myFoxxCollection';

// 指定コレクションがなければ作成する
if (!db._collection(collectionName)) {
  db._createDocumentCollection(collectionName);
}

Zipでまとめる

manifest.jsonindex.jsscripts/setup.jsをまとめたZipファイルを作る
スクショ
※ここではindex.zipを作ったものとする

ArangoDBにindex.zipをアップする

http://arangodbのホスト:8529/
でArangoDB Web UIに入ってSERVICESをクリック&Add Serviceをクリック
ArangoDB Web UIに入ってSERVICESを選択

左上にあるUploadをクリック
左上にあるUploadをクリック

Drag & Drop Filesでindex.zipをドロップする&Installをクリック
Drag & Drop Filesでindex.zipをドロップする&Installをクリック

ダイアログボックスが出るのでMount pointにアプリの名前を入れる(ここではmyfoxxとする)&Installを押す
ダイアログボックスが出るのでMount pointにアプリの名前を入れる(ここではmyfoxxとする)

myfoxxが登録される
myfoxxが登録される

myfoxxをクリックするとこのような画面になるので左上のAPIをクリック
myfoxxをクリックするとこのような画面になるので左上のAPIをクリック

index.jsでルータ定義したREST API一覧(Swagger)が出てくるのでAPIアクセス手法などがわかる
index.jsでルータ定義したREST API一覧(Swagger)が出てくるのでAPIアクセス手法などがわかる

Discussion