Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@asmfnk

FirebaseCloudFunctionsでExpressを使うと"Cannot GET /***"が出る

More than 1 year has passed since last update.

はじめに

FirebaseCloudFunctionsでExpressを用いた際、最初で結構躓いたので記録しておきます。
ただFunctionsにブラウザでアクセスしたらjsonが帰ってくるというものを作ろうとしたところCannot GET /***と表示されました。そして他のどこにもエラーが出力されないので解決がしづらいものでした。

結論をいうと、これはExpressのエラーでただ純粋に/***というPathでGETメソッドが見つからない、というだけです。
firebase-functionsは関係なく、ちゃんとExpress呼び出しまで働いてくれてました。
……が、大本の原因はfirebase-functionsに直感的でない挙動があることでした。

ソースコード

PROJECT/functions/src/index.ts

import {NextFunction, Request, Response} from 'express'
const express = require('express');
const functions = require('firebase-functions');

const app = express();

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();

// build multiple CRUD interfaces:
app.get('/:id', (req: Request, res: Response) => {
    return res.send({hoge: "fuga"})
});

// Expose Express API as a single Cloud Function:
exports.main = functions.https.onRequest(app);

firabase.json特定のURLだけFunctionを使う設定です。

  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/f/**", "function": "main"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }

発生したこと

firebase emulators:start

以上のコマンドでローカルで試すと、
http://localhost:5001/PROJECT/us-central1/main/tekito-idにアクセスするとちゃんと{hoge: "fuga"}のjsonが帰ってきます。
しかし、

firebase deploy

で本番環境にデプロイしたあと、
https://PROJECT.firebaseapp.com/f/tekito-idにアクセスするとCannot GET /***になってしまいます。

何が原因か

expressに以下の関数をミドルウェアとして追加してpathを見てみましょう。
このミドルウェアはすべてのアクセスでログ出力の関数を実行してくれます。

先ほどと同様にブラウザでアクセスしてログを見ます。ローカルではコンソール、本番環境では
Project Console: https://console.firebase.google.com/project/PROJECT/overview
で見れます。

import {NextFunction, Request, Response} from 'express'
const express = require('express');
const functions = require('firebase-functions');

const app = express();

const admin = require('firebase-admin');
admin.initializeApp();

// ここを追加!!!!!!!!!!!!!!
app.use(function (req: Request, res: Response, next: NextFunction) {
    console.log(req.path);
    next()
});

app.get('/:id', (req: Request, res: Response) => {
    return res.send({hoge: "fuga"})
});

exports.main = functions.https.onRequest(app);
console.log()
// ローカル
/tekito-id

// 本番
/f/tekito-id

なんで違うんや!
そうなんです。
本番になると渡されるpathが変わります。

対処

まず本番と環境の違いを出すために以下のコマンドでFirebaseの環境変数を設定します。
firebase functions:config:set environment.production=true

ここでtrue自体は文字列になってしまいますし大した意味はありません。ただ、 production という中身がtruthyなキーを持っていることが重要です。

次にFunction(TSファイル)を直します。

import {NextFunction, Request, Response} from 'express'
const express = require('express');
const functions = require('firebase-functions');

const app = express();
const router = express.Router();// 追加

const admin = require('firebase-admin');
admin.initializeApp();

// 条件分岐
if (functions.config().environment.production) {
    app.use('/f', router);
} else {
    app.use('', router);
}

// 変更
router.get('/:id', (req: Request, res: Response) => {
    return res.send({hoge: "fuga"})
});

exports.main = functions.https.onRequest(app);

ここでレスポンスを返すメインの関数をrouterに変更します。
このexpress.Router()クラスはミニアプリケーションと呼ばれ、Pathごとに処理をまとめたり、moduleに切り分けたりする際に使います。

条件文において本番では'true'の文字列が入るため /f 以降のPathをrouterに渡します。
ローカル環境ではundefinedになるため何もつけないPathがrouterに渡されます。

これにてどちらの環境でも一つのソースコードで動作するようになりました!

1
Help us understand the problem. What is going on with this article?
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
asmfnk
フルスタックエンジニアのフリをしたエンジニア

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?