Cloud Functions for FirebaseでApollo Serverを実装する

投稿日
Cloud Functions for FirebaseでApollo Serverを実装する

Apollo Serverをどこかにデプロイして試してみたい!


そう思って調べてみるとCloud Functions for Firebaseが対応している事が分かりました。



Firebaseは使い慣れている事もあったため、

Cloud Functions for FirebaseにApollo Serverをデプロイしてみる事にしました。

検証した環境

1 firebase 11.0.1
2 express 4.18.1
3 apollo-server-express 3.8.1
4 graphql 16.5.0

開発を行う前の準備

先にFirebaseのWebサイトからプロジェクトを作成しておきます

Firebaseのプロジェクトを作成する

表示された通りに進めていきプロジェクトの作成が完了すればOKです!



Functionsを使うためのローカル環境の作成

Cloud Functions for Firebaseをローカルで試す方法はGoogleがしっかり用意してくれていて、

インストール方法も丁寧にまとめられています。

はじめに: 最初の関数の記述、テスト、デプロイ  |  Firebase Documentation


Cloud Functions for Firebaseをエミュレートしたものを firebase-functions と言うみたいですね





まずローカル環境を作成するためのコマンドをグローバルにインストールします。

$ npm install -g firebase-tools

↑を実行するとfirebaseというコマンドが使用出来るようになります。

$ firebase --version
11.0.1

firebase-toolsをインストールした最初はログインが必要になります。

$ firebase login



ローカルで実行するための環境を作成します

$ firebase init functions


いくつか質問されるため答えていきます。私は今回以下のように答えました。


  • Use an existing project・・先ほど作ったプロジェクトを選択
  • What language would you like to use to write Cloud Functions?・・TypeScript
  • Do you want to use ESLint to catch probable bugs and enforce style?・・Y
    • ESLintの導入
  • Do you want to install dependencies with npm now?・・n
    • npmを使ってライブラリをインストールするか?(私はyarnを使いたいのでここではNOを選択)


全て答え終わると必要なファイル一式が作成されます。



firebase functionsのライブラリをインストール

今回は最後の質問で「NO」選択していてライブラリが未インストールになっています

そのためライブラリのインストールから行います。



firebase functionsの処理はfunctionsフォルダの中に書くので移動します。

$ cd functions/


package.jsonに記載されているnodeのバージョンと合わせるため

nodenvを使ってnodeのバージョンを固定します。

functions/package.json
{
  "engines": {
    "node": "16"
  },
  ・・・
}

package.jsonの記載が16なので記事を書いている時LTSの16.15.0をインストール

$ nodenv local 16.15.0


nodeのバージョンを固定出来たらライブラリをインストールします

$ yarn install


npmの部分を書き換える

firebase initした際に自動的にnpm-scriptsも作られています(便利)が、

内部でnpmを利用しているのでyarnに書き換えます。


具体的には serveshellstartの3つです

functions/package.json
"scripts": {
  "serve": "yarn run build && firebase emulators:start --only functions",
  "shell": "yarn run build && firebase functions:shell",
  "start": "yarn run shell",
  ・・・
}


ローカルで動くようにする

hello worldにあたるコードがコメントアウトする形で添付されています。

コメントアウトを外して動かせるようにします

functions/src/index.ts
import * as functions from "firebase-functions";
 
// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript
 
export const helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});


ここまで出来たら試しに実行してみます!

$ yarn run serve
・・・
  functions[us-central1-helloWorld]: http function initialized (http://localhost:5001/xxxxxx/us-central1/helloWorld).
・・・

コマンドを実行すると沢山の情報が出力されますが、

目を凝らすと ↑ のような1行が表示されているのでブラウザでアクセスします。


ローカルでfirebase functionsが実行出来た

無事文字が表示されました!



GraphQLの実装

firebase functionsを動かす下地が出来たので、GraphQLを動かせるようにしていきます。



実装方法は複数あるようなのですが、

いざという時の対応がしやすく記事も多くある expressを使った実装 をしていきます。




まず必要なパッケージをインストールします。

注意点としては引き続きfunctionsフォルダで実行する必要がある、ということです。

$ yarn add express apollo-server-express graphql


apollo-server-expressはexpressとApollo Serverを統合するためのパッケージで、

Apollo Server公式に以下のように書かれています。

apollo-server-express is the Apollo Server package for Express, the most popular Node.js web framework. It enables you to attach a GraphQL server to an existing Express server.


[翻訳] apollo-server-expressは、Node.jsのWebフレームワークとして最も人気のあるExpress用のApollo Serverパッケージです。既存のExpressサーバーにGraphQLサーバーをアタッチすることが可能です。


Choosing an Apollo Server package - Apollo GraphQL Docs より引用




ここまで出来たらGraphQLのコードと合わせてindex.tsを書き換えていきます。



GraphQLのコードに関しては「もしGoogle Cloud Functionsにデプロイするなら」という記事で紹介されているexampleコードを使わせてもらいます

Deploying with Google Cloud Functions - Apollo GraphQL Docs

functions/src/index.ts
import * as functions from "firebase-functions";
import express from "express";
import {ApolloServer, gql} from "apollo-server-express";
 
const typeDefs = gql`
    type Query {
        hello: String
    }
`;
 
const resolvers = {
  Query: {
    hello: () => "Hello world!",
  },
};
 
const server = new ApolloServer({
  typeDefs,
  resolvers,
});
 
const app = express();
server.start().then(() => {
  server.applyMiddleware({app, path: "/"});
});
 
export const graphql = functions.https.onRequest(app);
  • typeDefsからnew ApolloServerまでのところがApollo Serverの設定
  • const appからapplyMiddlewareまでの流れがexpressとApollo Serverを統合しているところ
  • 最後の1行でfirebase functionsで使えるようにしています


最後にtsconfig.jsonに2行追記します

functions/tsconfig.json
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}
  • allowSyntheticDefaultImportsはESLint対策(ないとexpressのimportで怒られる)
  • esModuleInteropはビルドにコケる対策になります
    • TypeError: express_1.default is not a functionというエラーが出力されるのを解消

esModuleInteropは以下の記事を参考にさせていただきましたmm

ExpressでTypeError: express_1.default is not a function エラーの解消方法 - code-log




ここまで出来たらいよいよです!

$ yarn run serve
・・・
  functions[us-central1-graphql]: http function initialized (http://localhost:5001/xxxxxx/us-central1/graphql).
・・・


http://localhost:5001 からはじまるサイトにアクセスすると、

Apollo Studioが立ち上がってQueryで定義したhelloが確認出来ました!

firebase functionsでApollo Studioを立ち上げqueryの確認が完了

Cloud Functions for Firebaseにデプロイする

Cloud Functions for Firebaseを利用するためには無料プランのSparkから従量制プランのBlazeに変更する必要があります。


大量に通信しなければ無料で使えますが、場合によってはお金が発生する可能性が出てきます。




まずfirebaseのページ > 設定 > 使用量と請求額ページ よりプランを変更します

使用量と請求額よりプランを変更する


SparkからBlazeへ!

料金プランを変更する



これでFirebase側の設定は出来たので後はデプロイするだけ。

デプロイコマンドも最初から用意されているので非常に簡単でyarn run deployを実行するだけ!!

$ yarn run deploy
・・・
  Deploy complete!
 
Project Console: https://console.firebase.google.com/project/xxxxx/overview


Cloud Functions for Firebaseに関数が作成されています

Functionsに関数が作成された


デプロイされたGraphQLはアクセスしてもApollo Studioが起動しないためcurlコマンドで確認します

$ curl -X POST -H "Content-Type: application/json" \
 -d "{ \"query\": \"query { hello }\"}" https://us-central1-xxxx.cloudfunctions.net/graphql
{"data":{"hello":"Hello world!"}}

無事「Hello world!」が出力されました!デプロイ完了です!

プロフィール画像
Yuki Takara
都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。