Skip to content

Dart callables#1258

Merged
jhuleatt merged 2 commits intodart-launchfrom
oncall-dart
Apr 7, 2026
Merged

Dart callables#1258
jhuleatt merged 2 commits intodart-launchfrom
oncall-dart

Conversation

@jhuleatt
Copy link
Copy Markdown
Collaborator

@jhuleatt jhuleatt commented Apr 7, 2026

No description provided.

@jhuleatt jhuleatt merged commit 61c3bcf into dart-launch Apr 7, 2026
2 checks passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Dart quickstarts for Firebase Cloud Functions, featuring both standard and streaming callable function examples. The implementation includes project setup, dependency configurations, and logic for weather forecasting and basic arithmetic. Review feedback highlights opportunities to enhance the robustness of the streaming implementation through better error handling and consistent return types. Additionally, improvements were suggested regarding Dart coding conventions for unused parameters and the adoption of structured logging over standard print statements.

Comment on lines +25 to +47
Future<dynamic> weatherForecastApi(num lat, num lng) async {
final resp = await http.get(
Uri.parse('https://api.weather.gov/points/$lat,$lng'),
);

if (resp.statusCode >= 400) {
return 'error: ${resp.statusCode}';
}

final pointData = jsonDecode(resp.body) as Map<String, dynamic>;
final forecastUrl = pointData['properties']['forecast'] as String;
final forecastResp = await http.get(Uri.parse(forecastUrl));

if (forecastResp.statusCode >= 400) {
return 'error: ${forecastResp.statusCode}';
}

// add an artificial wait to emphasize stream-iness
final randomWait = Random().nextDouble() * 1500;
await Future<void>.delayed(Duration(milliseconds: randomWait.toInt()));

return jsonDecode(forecastResp.body);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This function returns a String on error and a decoded JSON object (dynamic) on success. This mixed return type can lead to runtime errors on the client if not handled carefully. A better practice is to have a consistent return type and throw an exception on error. The caller can then use a try-catch block for robust error handling.

Future<dynamic> weatherForecastApi(num lat, num lng) async {
  final resp = await http.get(
    Uri.parse('https://api.weather.gov/points/$lat,$lng'),
  );

  if (resp.statusCode >= 400) {
    throw Exception('Failed to get weather points: ${resp.statusCode}');
  }

  final pointData = jsonDecode(resp.body) as Map<String, dynamic>;
  final forecastUrl = pointData['properties']['forecast'] as String;
  final forecastResp = await http.get(Uri.parse(forecastUrl));

  if (forecastResp.statusCode >= 400) {
    throw Exception('Failed to get weather forecast: ${forecastResp.statusCode}');
  }

  // add an artificial wait to emphasize stream-iness
  final randomWait = Random().nextDouble() * 1500;
  await Future<void>.delayed(Duration(milliseconds: randomWait.toInt()));

  return jsonDecode(forecastResp.body);
}

Comment on lines +66 to +79
final forecast = await weatherForecastApi(latitude, longitude);
final result = {
'latitude': latitude,
'longitude': longitude,
'forecast': forecast,
};

// clients that support streaming will have each
// forecast streamed to them as they complete
if (request.acceptsStreaming) {
await response.sendChunk(result);
}

return result;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To handle potential errors from weatherForecastApi gracefully without crashing the entire function for all locations, you should wrap the API call in a try-catch block. This allows you to process other locations even if one fails, and you can stream back a structured error for the failed location. This makes the client-side handling much more robust.

          try {
            final forecast = await weatherForecastApi(latitude, longitude);
            final result = {
              'latitude': latitude,
              'longitude': longitude,
              'forecast': forecast,
            };

            // clients that support streaming will have each
            // forecast streamed to them as they complete
            if (request.acceptsStreaming) {
              await response.sendChunk(result);
            }

            return result;
          } catch (e, s) {
            logger.error(
              'Failed to fetch forecast for lat: $latitude, long: $longitude',
              error: e,
              stackTrace: s,
            );
            final errorResult = {
              'latitude': latitude,
              'longitude': longitude,
              'error': 'There was an error fetching the forecast for this location.',
            };

            if (request.acceptsStreaming) {
              await response.sendChunk(errorResult);
            }
            return errorResult;
          }

// [START allAdd]
// [START addFunctionTrigger]
// Adds two numbers to each other.
firebase.https.onCall(name: 'addNumbers', (request, response) async {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The response parameter is not used in this function. According to Dart conventions, it's good practice to rename unused parameters to _ or prefix them with _ (e.g., _response) to signal that they are intentionally unused.

Suggested change
firebase.https.onCall(name: 'addNumbers', (request, response) async {
firebase.https.onCall(name: 'addNumbers', (request, _) async {

Comment on lines +53 to +55
print(
'User details: uid=$uid, name=$name, picture=$picture, email=$email',
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For logging in Cloud Functions, it's better to use the structured logger provided by firebase_functions instead of print. The logger automatically adds metadata to logs, which makes them easier to search and filter in Cloud Logging.

Suggested change
print(
'User details: uid=$uid, name=$name, picture=$picture, email=$email',
);
logger.info(
'User details: uid=$uid, name=$name, picture=$picture, email=$email',
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant