Callable Cloud Function error: Response is missing data field - firebase

Don't know how to get responce from cloud function in flutter.
My cloud function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.testDemo = functions.https.onRequest((request, response) => {
return response.status(200).json({msg:"Hello from Firebase!"});
});
My flutter code
///Getting an instance of the callable function:
try {
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'testDemo',);
///Calling the function with parameters:
dynamic resp = await callable.call();
print("this is responce from firebase $resp");
} on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
flutter: caught firebase functions exception
flutter: INTERNAL
flutter: Response is missing data field.
flutter: null

Use
exports.testDemo = functions.https.onCall((data, context) => {
return {msg:"Hello from Firebase!"};
});
in cloud functions. Callable is different than Request
When you call the functions you need to add the parameters:
change:
// Calling a function without parameters is a different function!
dynamic resp = await callable.call();
to:
dynamic resp = await callable.call(
<String, dynamic>{
'YOUR_PARAMETER_NAME': 'YOUR_PARAMETER_VALUE',
},
);
as described here
then to print the response:
print(resp.data)
print(resp.data['msg'])
The Firebase Functions for flutter example here and here

You must explicity put the attribute "data" in the json of response.
Like:
response.send({
"status" : success,
"data" : "some... data"
});

you can convert to .onCall type function if you want to call function by it's name. aldobaie's answer
or for onRequest type function:
we need to call it as RESTful APIs.
While deploying to firebase, we'll get functions URL
or we can get function URL from dashboard of functions from firebase.
URL is in formate of:
https://us-central1-< projectName >.cloudfunctions.net/< functionName >
call it as any other RESTful API. plus point of using it as API is that data field in response isn't mandatory and freedom of response formate.

Related

Firebase Function using Auth getUserByEmail

I'm trying to create a simple Firebase function using Firebase Auth that returns a user's uid if I send it a valid email address. No matter what I change I always get the same error:
Error fetching user data: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type function ([Function (anonymous)])
To call it I use: ...cloudfunctions.net/checkemail?email=wyn#wyn.com
I have tried wyn%40wyn.com as well. The user does exist.
I'm obviously doing something wrong. But what?
export const checkemail = functions
.region("deleted")
.https.onRequest(async (request, response) =>{
const email = String(request.query.email);
await admin.auth().getUserByEmail(email)
.then((userRecord)=> {
const data = userRecord.toJSON;
response.send(data);
})
.catch(function(error) {
response.send("Error fetching user data: "+ error);
});
});
toJSON is a method. Therefore it needs brackets: const data = userRecord.toJSON()

Error when using Spotify access token with API through Firebase callable cloud function

I've been working on this for a while now and feel like I've read everything I can find but still can't get it to work. I'm trying to build a Firebase callable cloud function that uses axios to get a Spotify access token through client credentials auth flow and then uses that token to get data from my own account from the Spotify API. I'm using a chained function starting with axios.post and then axios.get.
The code works when it's getting the access token through axios.post but as soon as I chain an axios.get to use the token with the API something goes wrong. I'm new to Firebase and node.js so am not sure exactly how to catch the errors properly. The most common error is either a null result or a 'Unhandled error RangeError: Maximum call stack size exceeded' in the Firebase log... can't work out what either actually means for my code... With this particular version of my code I get a null result and a mass of around 50 different error logs in Firebase.
I've tried splitting the functions, using async and await and different arrangements of the headers but not a lot really changes. I've found similar questions but nothing that seemed to solve the issue. Any help would be amazing!
const functions = require("firebase-functions");
const axios = require('axios');
const qs = require('qs');
exports.spot = functions.https.onCall( async (data, context) => {
const client_id = //REMOVED;
const client_secret = //REMOVED;
const auth_token = Buffer.from(`${client_id}:${client_secret}`, 'utf-8').toString('base64');
const token_url = 'https://accounts.spotify.com/api/token';
const stringify_data = qs.stringify({'grant_type':'client_credentials'});
const api_url = 'https://api.spotify.com/v1/recommendations'
return axios
.post(token_url, stringify_data, {
headers: {
'Authorization': `Basic ${auth_token}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
grant_type: 'client_credentials'
},
json: true
})
.then(result => {
return axios.get(api_url, {
headers: {
'Authorization': `Bearer ${result.data.access_token}`,
}
})
})
.then(result => {
return result
})
.catch(error => {
console.log(error);
})
});

Flutter Firebase Cloud functions: The data couldn’t be read because it isn’t in the correct format

I tried to call my cloud function using the cloud_functions plugin from my Flutter project with the following code:
final HttpsCallable callable = new CloudFunctions(region: "europe-west3")
.getHttpsCallable(functionName: 'helloWorld');
dynamic resp = await callable.call(<String, dynamic>{'id': id, 'chatId': chat.chatId});
And get the following error:
ERROR: PlatformException(3840, The data couldn’t be read because it isn’t in the correct format., null)
By my research, I saw that the problem can appear when you forget to put the region on the server and client side, but the error persist.
Also I try to pass by http request who succeed:
var parameters = {'id': id, 'chatId': chat.chatId};
var url = "https://europe-west3-{MY_DOMAIN}.cloudfunctions.net/helloWorld";
await http.post(url, body: parameters).then((res) {...}
So I think the problem come from the plugin where I maybe may have forgotten something. Any ideas ?
Cloud function (test):
exports.helloWorld = functions
.region('europe-west3')
.https.onRequest((request, response) => {
try {
response.send('Hello from Firebase!');
} catch (e) {
console.log(e);
throw new functions.https.HttpsError('calc-error', e);
}
});
As you are using a callable on your Flutter app, try to convert your function to use onCall instead of onRequest:
Firebase function using onCall:
export const helloWorld = functions.https.onCall((data, context) => {
functions.logger.info("data:", data);
return {
message: "bye!"
};
});
Flutter app:
emulator setup:
// after: Firebase.initializeApp()
FirebaseFunctions.instance.useFunctionsEmulator(origin: 'http://localhost:5001');
calling the function:
FirebaseFunctions functions = FirebaseFunctions.instance;
HttpsCallable callable = functions
.httpsCallable('helloWorld');
final results = await callable.call({
'name': 'Erick M. Sprengel',
'email': 'erick#mail.com'
});
Be careful about the difference between onCall vs onRequest.
It's easy to convert, but I think it's better to check this question: Firebase Cloud Functions: Difference between onRequest and onCall
Extra tips:
I'm using the emulator, and I'm not setting the region.
Remember to re-build your function after each change. I'm using npm run build -- --watch
in my case, I have wrong callable function name
FirebaseFunctions.httpsCallable('wrong name in here')
You need to set region on both side.
In your function:
exports.foo = functions.region('europe-west3').https.onCall(async (data, context) => ...
In Flutter:
final functions = FirebaseFunctions.instanceFor(region: 'europe-west3');
functions.httpsCallable('foo');

Getting "firebase.functions(app)" arg expects a FirebaseApp instance or undefined in React-Native Firebase

I am triggering an httpscallable function in GoogleCloud but receiving this error back which I could not find anywhere in documentation what is it:
"firebase.functions(app)" arg expects a FirebaseApp instance or undefined.
Ensure the arg provided is a Firebase app instance; or no args to use
the default Firebase app.
Here is my code in RN app:
import { firebase } from '#react-native-firebase/functions';
...
try {
await firebase.functions('europe-west1').httpsCallable('createUserTest')();
}
catch (httpsError) {
console.log(httpsError.message);
}
And my Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createUserTest = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const callerUid = context.auth.uid;
const callerUserRecord = await admin.auth().getUser(callerUid);
return { result: callerUserRecord.customClaims };
} catch (error) {
return { error: error };
}
});
I am using this function for testing purposes just to see if I can receive back the current user custom claims or not, however, its returning that error.
It looks like you're not using the provided client API correctly. I suggest reviewing the documentation, especially example 3. You'll want to do this instead:
const defaultApp = firebase.app();
const functionsForRegion = defaultApp.functions('europe-west1');
await functionsForRegion.httpsCallable("createUserTest")()

Using a callable function to send data back to the client from Firebase

I have created a callable Cloud Function to read data from Firebase and send back the results to the client, however, only "null" is being returned to the client.
exports.user_get = functions.https.onCall((data, context) => {
if (context.auth && data) {
return admin.firestore().doc("users/" + context.auth.uid).get()
.then(function (doc) {
return { doc.data() };
})
.catch(function (error) {
console.log(error);
return error;
})
} return
});
I just reproduced your case connecting from a Cloud Function with a Firestore database and retriving data. As I can see you are trying to access the field in a wrong way when you are using "users/" + context.auth.uid, the method can't find the field so its returning a null value.
I just followed this Quickstart using a server client library documentation to populate a Firestore database and make a Get from it with node.js.
After that i followed this Deploying from GCP Console documentation in order to deploy a HTTP triggered Cloud Function with the following function
exports.helloWorld = (req, res) => {
firestore.collection('users').get()
.then((snapshot) => {
snapshot.forEach((doc) => {
console.log(doc.id, '=>', doc.data().born);
let ans = {
date : doc.data().born
};
res.status(200).send(ans);
});
})
And this is returning the desired field.
You can take a look of my entire example code here
This is because you are making a query from a database firestore, however the cloud support team has made it very cool to protect your applications from data leakages and so in a callable function as the name suggest you can only return data you passed to the same callable function through the data parameter and nothing else. if you try to access a database i suggest you use an onRequest Function and use the endpoint to get you data. that way you not only protect your database but avoid data and memory leakage.
examples of what you can return from a callable function
exports.sayHello = functions.https.onCall((data, context) => {
const name = data.name;
console.log(`hello ${name}`);
return `It was really fun working with you ${name}`;
});
first create a function in your index.js file and accept data through the data parameter but as i said you can only return data you passed through the data parameter.
now call the function
this is in the frontend code (attach an event listener to a button or something and trigger it
/* jsut say hello from firebase */
callButton.addEventListener('click', () => {
const sayHello = firebase.functions().httpsCallable('getAllUsers');
sayHello().then(resutls => {
console.log("users >>> ", resutls);
});
});
you can get your data using an onRequest like so
/* get users */
exports.getAllUsers = functions.https.onRequest((request, response) => {
cors(request, response, () => {
const data = admin.firestore().collection("users");
const users = [];
data.get().then((snapshot) => {
snapshot.docs.forEach((doc) => {
users.push(doc.data());
});
return response.status(200).send(users);
});
});
});
using a fetch() in your frontend code to get the response of the new onRequest function you can get the endpoint to the function in your firebase console dashboard.
but not that to hit the endpoint from your frontend code you need to add cors to your firebase cloud functions to allow access to the endpoint.
you can do that by just adding this line to the top of your index.js file of the firebase functions directory
const cors = require("cors")({origin: true});

Resources