Google Cloud Functionsで遊んでみる

こんにちは、久保田です。

最近、GoogleのCloud FunctionsでAPIを作りました。

作ったのはTODO管理のAPIで、ストレージはDataStoreを使いました。

Cloud FunctionsはGCP内のイベントはもちろん、Firebaseからも使える上に、エンドポイントも提供してくれるので幅広い使い方を楽しめそうです。
さらに、Cloud Functionsはローカルエミュレータもあり、開発もしやすかったです。

今回は僕が作ったAPIの一部分を例にしながら、CloudFunctionsの開発について書きます。

開発環境の用意

まずは開発環境を用意します。

Cloud Functionsは現在node.jsの6.9.1しか対応していないようですので、合わせておいた方がいいかと思います。

先述した通り、ローカルエミュレータというものがあり、ローカルでCloudFunctionsとほぼ同一の環境を用意し、コードを動かすことのできる環境を作ることができます。

なのでまずはローカルエミュレータの構築から行います。

github.com

とは言ってもnpmとかでできてしまいます。
構築が終わると、functionsコマンドが叩けるようになります。

その後はREADMEに書いてある通り、index.jsを作り、

$ functions start
$ functions deploy 関数名 --trigger-http 

これでhttp経由で動かすことのできる関数が完成です。
deployコマンドの関数名はindex.js内でexportしている関数名です。
あとはファイルを更新したら自動で更新されます。

最初のfunctionsコマンド実行時、gcloudコマンドがない場合はプロジェクトIDを聞かれますので、対象のプロジェクトIDを入力する必要があります。

※ エミュレータで動かすだけならばgcloudコマンドは必要ありませんが、GCPの他のリソースをCloud Functionsから触ったりする場合はcredentialが必要なので、gcloudコマンドを使って認証をしておいてください。

※ Dockerfileを作ったので、よかったら参考にしてください。

github.com

実装

開発環境は整ったので、どんどん実装していきます。
今回はDataStoreからデータを取得してくるところのみ言及します。

まずはpackage.jsonです。

{
  "name": "helloWorld",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/datastore": "latest"
  }
}

次はデータの取得処理です。
DataStoreにサンプルデータを登録してある状態です。

exports.helloWorld = function helloWorld(req, res) {
  datastore = require('@google-cloud/datastore')({
    projectId: プロジェクトID
  });

  var q = datastore.createQuery('Todo');
  q.run(function(err, entities, info){
    console.log("=========");
    console.log(entities);
    console.log("=========");
    res.send(entities);
  })
}

データの取得だけならばこれでできます。

実装したら、deployして実行してみます。

$ functions deploy helloWorld --trigger-http 
$ functions call helloWorld

こうすると、DataStoreに登録してあるデータが返ってきます。
(ちなみcallで実行するとPOSTで実行されます。)

ExecutionId: a2be0798-e886-4cd8-854a-51cc1bea8178
Result: [ { tags: [ 1, 3 ],
    name: 'todo1',
    created: '2017-06-09T05:09:11.244Z' },
  { tags: [ 5, 7 ], name: 'todo2', created: 1497442628874 } ]

※ 今回のデータ構造として、Todoという種類にname, created, tagsというプロパティで構成されたエンティティが入っています。

ログも見れます。

$ functions logs read
2017-06-14T17:28:40.992Z - info: User function triggered, starting execution
2017-06-14T17:28:41.328Z - info: =========
2017-06-14T17:28:41.330Z - info: [ { created: 2017-06-09T05:09:11.244Z,
    tags: [ 1, 3 ],
    name: 'camp' },
  { name: 'コストコ', created: 1497442628874, tags: [ 5, 7 ] } ]
=========
2017-06-14T17:28:41.333Z - info: Execution took 339 ms, user function completed successfully

このままではあまり面白くないので、パラメータを受け取り絞り込みをしてみます。

exports.helloWorld = function helloWorld(req, res) {
  datastore = require('@google-cloud/datastore')({
    projectId: プロジェクトID
  });

  // functions call で実行するとPOSTになるため、req.bodyでパラメータを取得する。
  // curlなどで実行した場合はreq.queryになる。
  var q = datastore.createQuery('Todo').filter('name', req.body['name']);

  q.run(function(err, entities, info){
    console.log("=========");
    console.log(entities);
    console.log("=========");
    res.send(entities);
  })
}

実行時にパラメータを渡せます。jsonファイルからも渡せます。(その際のオプションは --file=filepath)

$ functions call helloWorld --data='{"name": "todo1"}'
ExecutionId: 2186b22d-0682-4106-91aa-b8b452958839
Result: [ { name: 'todo1',
    created: '2017-06-09T05:09:11.244Z',
    tags: [ 1, 3 ] } ]

できました。

あとはgcloud経由でもGUI経由でもデプロイしてみてください。

記事では取得しかしていませんが、CRUD操作はすべてしたので、他の処理はgithubをみてください。

github.com

Cloud Functionsのおかげで、GCPのリソースを組み合わせていけば、サーバーを構築せずに簡単なAPIが作れてしまいました。
皆さんも簡単なものを作りたいときはぜひ使ってみてください!