Getting Started w/ Dart on GCP

“The Missing Tutorials” series

Daz Wilkin
Google Cloud - Community
5 min readApr 3, 2019

--

A belated but hopefully still useful addition to this series.

A developer on Stackoverflow was unable to find good documentation for Google Cloud Speech-to-Text API using Dart(lang).

I’d not used Dart before and was unable to find much by way of documentation to provide a response. So I had a go. It wasn’t easy but that’s mostly because I really wasn’t familiar with Dart nor had I used the Speech-to-Text API (but that’s not complicated).

Cutting to the chase, here’s the solution “How old is the Brooklyn bridge?”

Setup

I’m running Linux (Debian). I’ll provide pointers for installing Dartlang (here) although I installed from a zip rather than using a package manager but will leave to you to install this for yourself. I went with the Server SDK.

PROJECT_ID=[[YOUR-PROJECT-ID]]
LANG=dart
mkdir -p ${HOME}/${PROJECT_ID}/${LANG}
cd ${HOME}/${PROJECT_ID}/${LANG}

You’ll need to enable the Speech-to-Text API:

gcloud services enable speech.googleapis.com --project=${PROJECT}

And you’ll need a JSON key to a (permissionless) service account so that your code can access the API:

ROBOT=[[YOUR-SERVICE-ACCOUNT]]
FILE=${PWD}/${ROBOT}.key.json
gcloud iam service-accounts create $ROBOT \
--display-name=$ROBOT \
--project=$PROJECT
gcloud iam service-accounts keys create ${FILE} \
--iam-account=${ROBOT}@${PROJECT}.iam.gserviceaccount.com \
--project=$PROJECT

Stagehand

Dart’s SDK includes Stagehand which scaffolds projects — I used a Console app — and helps with package management:

mkdir speech && cd speechstagehand console-full

Helpfully, the command completes with instructions for how to proceed:

--> to provision required packages, run 'pub get'
--> run your app using `dart bin/main.dart`.

Before, I ran pub get, I opened the directory in Visual Studio Code and decided I need to add references to Google’s API Client Libraries

Digression: see Google Client Libraries Explained.

Confusingly, there are several (non-disambiguating) Google-managed GitHub libraries for Dart: https://github.com/dart-lang/gcloud, https://github.com/dart-lang/googleapis, and hidden within this https://github.com/dart-lang/googleapis/tree/master/generated/googleapis. The latter provided me the most help with an example using Google Cloud Storage.

I learned that the central repo of Dart packages is here:

https://pub.dartlang.org/

Searching by googleapis found me this:

https://pub.dartlang.org/packages/googleapis

and I subsequently realized that I need the OAuth package to:

https://pub.dartlang.org/packages/googleapis_auth

Conveniently, clicking “installing” on any of these pages provides the configuration to be pasted into pubspec.yaml:

dependencies:
googleapis: ^0.53.0
googleapis_auth: ^0.2.7

Then, you can run pub get to pull these packages from the repo into your project.

Client

The GitHub repo provided some boilerplate code for accessing Google API Client Libraries (Cloud Storage) authorized by a service account JSON key:

import 'package:googleapis/speech/v1.dart';
import 'package:googleapis_auth/auth_io.dart';
final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
"private_key_id": ...,
"private_key": ...,
"client_email": ...,
"client_id": ...,
"type": "service_account"
}
''');
const _SCOPES = const [SpeechApi.CloudPlatformScope];void main() {
clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
var speech = new SpeechApi(http_client);
speech...
});
}

It was simple to guess the replacement values for the Speech API.

NB It is better to reference the service account JSON key as a file but I’m a noob and so used the text literal as shown in the example into which I copied and pasted the content of ${ROBOT}.key.json.

This threw me though because the auth-io library accepts either JSON strings or Dart Maps but, when I came to instantiated the Speech API’s recognize class, it’s fromJson method does not….. I ended up having to write myself a sample from scratch to understand how this works in Dart:

NB I learned something else by attempting to reverse-engineer Google’s code. It uses e.g. core.Stringbut DartPad raised errors for this. I realized the core prefix was some form of aliasing and looked to the imports in the Google code and saw import 'dart:core' as core;. This is likely a good practice of specificity and ensuring that, when you write e.g. String, and mean String from dart:core that it is what you get.

Checking Google’s Speech-to-Text documentation, I realized that the key method I’d be using is recognize:

https://cloud.google.com/speech-to-text/docs/reference/rest/v1/speech/recognize

And that the method takes:

{
"config": {
object(RecognitionConfig)
},
"audio": {
object(RecognitionAudio)
}
}

Searching through the GitHub repo, uncovered:

https://github.com/dart-lang/googleapis/blob/9181a52ad90c47c56d38576090ebbc94957ab716/generated/googleapis/lib/speech/v1.dart#L442

Which has a signature that looked promising:

async.Future<RecognizeResponse> recognize(RecognizeRequest request)

Having used futures in Node.JS, I realized that the boilerplate code’s employed another future and I had my starting point:

clientViaServiceAccount(_credentials, _SCOPES).then((...) {...})

in the form of:

speech.speech.recognize(_recognizeRequest).then((response) {...})

When speech.recognize did not work, I checked the code and compared with Cloud Storage and saw:

SpeechResourceApi get speech => new SpeechResourceApi(_requester);

and realized I needed to add it (and should probably rethink my naming of the first one).

The REST API documentation shows that the method returns:

{
"results": [
{
object(SpeechRecognitionResult)
}
]
}

But, from the source, it’s clear that this is called RecognizeResponse in the Dart library. I got as far as:

speech.speech.recognize(_recognizeRequest).then((response) {
for (var result in response.results) {
print(result.toJson());
}
});

BUT it took me a long time (as I mentioned above) to work out how to pass the Map|JSON to the command. I ended up by using the source code (and my example) as my guides:

final _json = {
"audio":{
"content":"[[SEE BELOW]]"
},
"config": {
"encoding": "LINEAR16",
"sampleRateHertz": 16000,
"languageCode": "en-US"
}
};
final _recognizeRequest = RecognizeRequest.fromJson(_json);

This seemed to pass muster and I stopped receiving egregious errors when trying to dart bin/main.dart ;-)

I needed a (base-64 encoded) sample file.

Conveniently, the Google documentation covers exactly this issue:

https://cloud.google.com/speech-to-text/docs/base64-encoding

And, I guessed that somewhere in one of Google’s other samples, there’d be a convenient audio file to use:

https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/speech/cloud-client/resources

I used audio.raw.

I ran it through:

base64 audio.raw -w 0 > audio.b64

And, because I still don’t know how to use files in Dart, I copied and pasted its content into my source code too ;-)

Then, I ran it:

dart bin/main.dart

And, it worked:

{
alternatives: [{
confidence: 0.9835046,
transcript: how old is the Brooklyn Bridge
}]
}

Conclusion

The consistency of Google’s API (!) Client Libraries makes it very easy to move between languages because the API Client Libraries are mostly the same (regardless of language) and because Google does an excellent job in documenting its REST APIs.

One thing I’m missing and assume is available is the online documentation for the Dart APIs. It’s clear that there’s extensive comment-based documentation included in Google’s sources but I was unable to find the site where this is presented. Obviously, that would be helpful.

19–04–04: Update — Found the documentation: https://pub.dartlang.org/documentation/googleapis/latest/googleapis.speech.v1/googleapis.speech.v1-library.html

Generally, when I am learning a new Google API, I use Google’s **excellent** APIs Explorer to test calls. In this case, the API structure was clear and my primary challenge was in working out how to interact with Google’s machine-generated API Client Library.

Hopefully this post helps others navigate using Dart(lang) and Google’s API Client Libraries.

That’s all!

--

--