I was trying GCP API Gateway using Firebase authentication. I can see my request has been processed from the logs and completed with response code 200. However, I am not getting the response back to my client. I am getting the response when I call the function directly (by passing the api gateway) both from client and curl. Am I missing something ?
API Config
swagger: "2.0"
info:
title: API Endpoints
description: API Endpoints
version: 1.0.1
schemes:
- https
produces:
- application/json
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/my-project"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"
x-google-audiences: "my-project"
paths:
/hello:
get:
summary: Test link
operationId: hello
x-google-backend:
address: https://us-central1-my-project.cloudfunctions.net/hello
security:
- firebase: []
responses:
"200":
description: A successful response
schema:
type: string
"403":
description: Failed to authenticate
Function
/**
* Responds to any HTTP request.
*
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
console.log("I am here inside the app")
res.status(200).send(message);
};
Additional Logs
{ insertId: "8c13b49c-2752-4216-8188-d445f4724ef14850908905639612439@a1" jsonPayload: { api_key_state: "NOT CHECKED" api_method: "1.myapi.hello" api_name: "1.myapi" api_version: "1.0.1" client_ip: "999.999.999.999" http_method: "GET" http_response_code: 200 location: "us-central1" log_message: "1.myapi.hello is called" producer_project_id: "myproject" request_latency_in_ms: 161 request_size_in_bytes: 4020 response_size_in_bytes: 579 service_agent: "ESPv2/2.17.0" service_config_id: "myapi-config" timestamp: 1606313914.9804168 url: "/hello" } logName: "projects/myproject/logs/myapi%2Fendpoints_log" receiveTimestamp: "2020-11-25T14:18:36.521292489Z" resource: { labels: { location: "us-central1" method: "1.myapi.hello" project_id: "myproject" service: "myapi" version: "1.0.1" } type: "api" } severity: "INFO" timestamp: "2020-11-25T14:18:34.980416865Z" }
Client Side Code
The client side is written in Flutter - Dart
and uses firebase authentication
. I am not adding them here as it is pretty standard and in my case proprietary.
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:example/.env.dart';
final _dio = Dio();
class GetAPIWidget extends StatefulWidget {
const GetAPIWidget({Key key}) : super(key: key);
@override
_GetAPIWidgetState createState() => _GetAPIWidgetState();
}
class _GetAPIWidgetState extends State<GetAPIWidget> {
String text = 'No response yet';
@override
void initState() {
// TODO: implement initState
getAPIResponse(endpoint: 'hello').then((value) {
setState(() {
text = value.toString();
});
}).catchError((onError) {
setState(() {
text = onError.toString();
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(text),
),
);
}
}
Future<Map<String, dynamic>> getAPIResponse({
/// Endpoint to our internal API Gateway. For this option to work url should be null
String endpoint,
Map<String, dynamic> queryParameters,
Map<String, dynamic> headers,
}) async {
try {
final Response response = await _dio.get<Map<String, dynamic>>(
'$api_gateway/$endpoint',
queryParameters: queryParameters ?? <String, dynamic>{},
options: Options(
// receiveTimeout: 20,
responseType: ResponseType.json,
contentType: Headers.jsonContentType,
headers: headers ??
<String, dynamic>{
Headers.contentTypeHeader: Headers.jsonContentType,
Headers.acceptHeader: Headers.jsonContentType,
'Authorization': 'Bearer ${idToken from firebase auth}',
}),
);
print(response.statusCode);
print(response.data);
if (response.statusCode == 200) {
return response.data;
} else {
throw response.statusMessage;
}
} on DioError catch (e) {
if (e.type == DioErrorType.CONNECT_TIMEOUT) {
throw ('Connection Timeout');
}
if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
throw ('Session Timeout');
}
}
return <String, dynamic>{};
}
The client side returned {} which is the default return message. Neither of the prints showed up on client side logs.
It was not working because header was missing in the response.
res.setHeader('Content-Type', 'application/json');