LoginSignup
9
3

More than 5 years have passed since last update.

Azure Functions を TypeScript で書いてみる。

Last updated at Posted at 2017-10-05

Mac で、Azure Functions をいじろうと思って、C# もかけるけど、まずは、node + Azure Functions CLI の組み合わせで、書いてみようと思った。簡単な、 CosmosDB に接続するサンプル。

噂の Callback hell

一番簡単そうな、CosmosDB の node からのチュートリアルをやってみた。

コードが見当たらなかったので、自分で書いたのを GitHub に上げておいた。

ちなみに、こんな感じのが3ファイルぐらい必要だった。単なる ToDo に、、、orz

taskDao.js

var DocumentDBClient = require('documentdb').DocumentClient;
var docdbUtils = require('./docdbUtils');

function TaskDao(documentDBClient, databaseId, collectionId) {
    this.client = documentDBClient;
    this.databaseId = databaseId;
    this.collectionId = collectionId;

    this.database = null;
    this.collection = null;
}

TaskDao.prototype = {
    init: function (callback) {
        var self = this;

        docdbUtils.getOrCreateDatabase(self.client, self.databaseId, function(err, db) {
            if (err) {
                callback(err);
            } else {
                self.database = db;
                docdbUtils.getOrCreateCollection(self.client, self.database._self, self.collectionId, function (err, coll) {
                    if (err) {
                        callback(err);
                    } else {
                        self.collection = coll;
                    }
                });
            }
        });
    },
    find: function (querySpec, callback) {
        var self = this;
        self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
            if (err) {
                callback(err);
            } else {
                callback(null, results);
            }
        });
    },

    addItem: function (item, callback) {
        var self = this;

        item.date = Date.now();
        item.completed = false;

        self.client.createDocument(self.collection._self, item, function (err, results) {
            if (err) {
                callback(err);
            } else {
                callback(null, results);
            }
        });
    },

    updateItem: function (itemId, callback) {
        var self = this;

        self.getItem(itemId, function (err, doc) {
            if (err) {
                callback(err);
            } else {
                doc.completed = true;
                self.client.replaceDocument(doc._self, doc, function(err, replaced) {
                    if (err) {
                        callback(err);
                    } else {
                        callback(null, replaced);
                    }
                });
            }
        });
    },

    getItem: function (itemId, callback) {
        var self = this;

        var querySpec = {
            query: 'SELECT * FROM root r WHERE r.id = @id',
            parameters: [{
                name: '@id',
                value: itemId
            }]
        };

        self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
            if (err) {
                callback(err);
            } else {
                callback(null, results[0])
            }
        });
    }
};

module.exports = TaskDao;

私の感想は、、、これが、コールバックヘルと言うやつか、、、こんなしょーもないことするのに、こんなコードが要るとは、、、辛すぎる。だった。そうだ、TypeScript だったらどうだろう。楽じゃないかな?

ライブラリを調べてみるとあった!

こ、これだ、これしかない! これを使って、Functions を書いてみよう。

Azure Functions を TypeScript 対応にしてみる

とりあえず、HttpTrigger を TypeScript に変えてみる。ちなみに、Azure Functions の TypeScript は、現在 Preview になって要るが、Azure Functions CLI の、2.0はまだ対応していない。自力だ。

Function App を作成

$ func init
Writing .gitignore
Writing host.json
Writing local.settings.json
Created launch.json

Initialized empty Git repository in /Users/ushio/Codes/AzureFunctions/some/.git/

Functions を作成

$ func new
Select a language: 
1. C#
2. JavaScript
Choose option: 2
JavaScript
Select a template: 
1. BlobTrigger
2. HttpTrigger
3. QueueTrigger
4. TimerTrigger
Choose option: 2
HttpTrigger
Function name: [HttpTriggerJS] 
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/index.js
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/sample.dat
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/function.json

テンプレートが生成された

TypeScript 環境を作る

cd HttpTriggerJS
npm init
tsc --init
cp index.js index.ts

index.ts を次のように変える

index.ts

export async function run (context: any, req: any) {

typeconfig.json を次のように変える。これをしないと、async メソッドは、promise を返さないとエラーになる。ところが、最初の1つ目のfunction では返しても仕方がないケースがあるので、新しいバージョンの TypeScript ではエラーにならなくなっている。

{
  "compilerOptions": {
    /* Basic Options */                       
    "target": "ES2015",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
    "lib": [ "es2015"],   

can't find process error

もしかすると、こんなエラーに出会うかもしれない。

index.ts(7,34): error TS2304: Cannot find name 'process'.
index.ts(7,65): error TS2304: Cannot find name 'process'.

このケースは、これで解消する。そもそも process 見つからんとかおかしいよね。これはバグ。

npm install --save-dev @types/node

下記の議論をみてみると詳細が書いてありJます。

下記のエラーは先ほどのtypeconfig.json の設定で解消されます。

error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor.  Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.

ついに Azure Functions

ここまで設定すると、無事、CosmosDB にアクセスするファンクションが動きました。簡単!

import * as DB from "documentdb-typescript";


export async function run (context: any, req: any) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const client = new DB.Client(process.env["COSMOS_DB_HOST"], process.env["COSMOS_DB_KEY"]);
    client.enableConsoleLog = true;

    await client.openAsync();
    console.log(await client.getAccountInfoAsync());
    var dbs = await client.listDatabasesAsync();

    if (req.query.name || (req.body && req.body.name)) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello! " + (req.query.name || req.body.name) + dbs.map(db => db.id)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
    context.done();
};

ちなみに、上記のは、documentdb-typescript のサンプルそのままだが、最初は、サンプルのままだと、エラーだった。Issue を書いて、解決したので、プルリクエストを書いておいた。

TypeError: Cannot read property 'getDatabaseAccount' of undefined

次のコミットが参考になります。 commit.

ToDo

残念ながら、TypeScript のローカルデバッグはできません。(生成されたjsはできると思うけど)。どうやったらできるか考えてみます。

Resource

実は、このサンプルを GitHub においています。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3