Help us understand the problem. What is going on with this article?

Azure Functionsでサーバーサイドアプリを動かす

More than 3 years have passed since last update.

Azure Functionsでサーバーサイドアプリを動かす

by ovrmrw
1 / 23

When

2016/12/02

At

Gotanda.js #6 in Oisix


今回のGitHubリポジトリ

ovrmrw/qiita-advent-calendar-2016-microservices-1

現在更新中:innocent:

===

話す内容

  • Azure Functionsの基本。
  • Azure Functionsでサーバーサイドアプリを動かす。

自己紹介

ちきさん
(Tomohiro Noguchi)

Twitter: @ovrmrw

ただのSIer。

Angular Japan User Group (ng-japan)スタッフ。

3a2512bb-aa72-4515-af42-1f1721252f39.jpg


アカウントIDの由来

  1. the day after tomorrow
  2. overmorrow(俗語)
  3. 略して
  4. ovrmrw
  5. 「先を見据えて」よりさらに先を、みたいな感じです。

(よく聞かれるので:innocent:)


(ここから本編)


Azure Functionsの基本から

  • AWS Lambda + API Gatewayみたいなもの。
  • (Herokuみたいな)Web Appsと同じ仕組みのVMを起動するのでAWS Lambdaより遅い。(多分)
  • ストレージIOがとても遅いのでモジュールを多数importする場合はWebpackでバンドルすべき
  • Node.js以外にもC#とかF#とかでも書けるらしい。

Rule1. サブフォルダの名前がエンドポイントになる。

root
|--function1
|  |--function.json
|  |--main.ts
|--function2
   |--function.json
   |--main.ts

生成されるエンドポイント。

  • https://my-azure-functions.azurewebsites.net/api/function1
  • https://my-azure-functions.azurewebsites.net/api/function2

Rule2. ファンクションフォルダにはfunction.jsonが必須。

HTTPエンドポイントの場合はこのように書く。
function.jsonが存在しないとファンクションと見なされない。

function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ],
  "disabled": false
}

Rule3. contextを引数に取る関数をexportする。

最高にシンプルなファンクションはこんな感じ。

main.ts
export function azureFunction(context, req) {
  context.res = {
    status: 200,
    body: {
      message: 'Hello world.',
    }
  };
  context.done();
}

Rules

  • exportする関数は最低限contextを引数に取ること。
  • context.resstatusbodyをセットして返すこと。
  • 最後にcontext.done()に呼ぶこと。(あるいはPromiseを返すこと)
main.ts(Promise)
export async function azureFunction(context, req) {
  context.res = {
    status: 200,
    body: {
      message: 'Hello world.',
    }
  };
  // context.done(); <-- Promiseを返す場合はdone()は呼ばなくていい。
}

Rule3-2. 複数ファイルで構成される場合はindex.tsを用意しておくとトラブルがない。

例えばこのような状況ではindex.tsを作っておかないとNot Found沼に嵌ることがある。

root
|--function1
   |--function.json
   |--index.ts
   |--main.ts
   |--server.ts

index.tsはファンクションをimportしてexport defaultでexportするだけで良い。

index.ts
import { azureFunction } from './main';

export default azureFunction;

(ここまでが基本編)


Azure Functionsのエンドポイントでサーバーサイドアプリ(Express, Hapi等)を動かすことができる。


Server-side app on Azure Functions

root
|--hapi-simple
   |--function.json
   |--index.ts
   |--main.ts
   |--server.ts

(example)

  • https://my-azure-functions.azurewebsites.net/api/hapi-simple
    • { message: 'Hello world'}
  • https://my-azure-functions.azurewebsites.net/api/hapi-simple/Gotanda.js
    • { message: 'Hello world, Gotanda.js'}

function.jsonにちょっと細工

"route":"hapi-simple/{*segments}"を書くことでルーティングが可能になる。

function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "route":"hapi-simple/{*segments}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ],
  "disabled": false
}

server.ts

サーバーサイド(Hapi)のコードをそのまま持ち込んで、URIを生成する関数をPromiseでexportする。

server.ts
import * as Hapi from 'hapi';

const server = new Hapi.Server();
server.connection({
  host: 'localhost'
});

server.route([
  {
    method: 'GET',
    path: '/',
    handler: (req, reply) => {
      const message = 'Hello world.';
      reply({ message });
    }
  },
  {
    method: 'GET',
    path: '/{name}',
    handler: (req, reply) => {
      const message = 'Hello world, ' + req.params['name'];
      reply({ message });
    }
  }
]);


let uri: string | undefined;

export function createUri(): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    if (uri) {
      resolve(uri);
    } else {
      server.start((err) => {
        if (err) { reject(err); }
        uri = server.info.uri;
        console.log('Server running at:', uri);
        resolve(uri);
      });
    }
  });
}

main.ts

Hapiサーバー起動後のURIを取得して、ファンクションに入ってきたrequestオブジェクトをリレーしてHapiサーバーにfetchする。

main.ts
import { customFetch } from '../lib/utils';
import { createUri } from './server';

export async function azureFunction(context, req) {
  try {
    const uri: string = await createUri();
    const res = await customFetch(uri, req);

    if (res.status === 200) {
      context.res = {
        status: res.status,
        body: res.myBody,
        headers: res.myHeaders,
      };
    } else { // Not Found
      context.res = {
        status: res.status,
        body: res.statusText,
      }
    }
  } catch (err) {
    context.res = {
      status: 500,
      body: err,
    };
  }
}

index.ts

main.tsで定義したファンクションをexport defaultでexportしているだけ。

index.ts
import { azureFunction } from './main';

export default azureFunction;

1ドメインでこんなことも可能です。

root
|--express1
|  |--function.json
|  |--index.ts
|  |--main.ts
|  |--server.ts
|--express2
|  |--function.json
|  |--index.ts
|  |--main.ts
|  |--server.ts
|--express3
   |--function.json
   |--index.ts
   |--main.ts
   |--server.ts

生成されるエンドポイント。

  • https://my-azure-functions.azurewebsites.net/api/express1
  • https://my-azure-functions.azurewebsites.net/api/express2
  • https://my-azure-functions.azurewebsites.net/api/express3

イメージ図
Server-side app on Azure Functions.png


(動作デモ)


  • まだJSONを返すAPIしか試作してないけど、HTMLを返すこともできるのでは?
  • つまりAngular SSR on Azure Functionsをやりたい。
  • ここ数日トライし続けているが光が見えない。
  • 誰か作れたら教えてください。

===

  • (最新情報) HTMLは返せた。でも画像が返せない。もうAzureの仕様なのでは…
  • 画像は全て別サーバーから取得するようにしたらイケるかもしれない。

Thanks!

ovrmrw
ちきさんです。ただのWebエンジニアです。
http://overmorrow.hatenablog.com/
opt
"INNOVATION AGENCY" を標榜するインターネット広告代理店。エンジニア組織 "Opt Techonologies" を中心にアドテクetc...に取り組んでいます。
https://opt-technologies.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした