Overview
A client-mode connector for the BigQuery API v2 that streams MQTT telemetry into BigQuery tables and runs SQL from MQTT:
Default
BASE_ADDRESS is https://bigquery.googleapis.com. Replace YOUR_PROJECT_ID in the endpoints with your GCP project id. Topics use data/google-bigquery/<object>/<verb>.
Auth
BigQuery needs an OAuth 2.0 access token inAuthorization: Bearer …. The template reads it from the MQTT payload field access_token; keep the token in a Hub secret such as GET SECRET "GOOGLE_BIGQUERY_ACCESS_TOKEN" and never commit it.
- Minting the token is out of band. Exchange a service-account key (signed JWT) for an access token with
gcloud auth print-access-token, a Google auth SDK, or your own token service, then publish or refresh it. Access tokens expire after about 1 hour. - Scopes:
https://www.googleapis.com/auth/bigquery(full),https://www.googleapis.com/auth/bigquery.insertdata(streaming insert only), orhttps://www.googleapis.com/auth/cloud-platform. - IAM: the service account needs BigQuery Data Editor on the dataset and BigQuery Job User on the project to run queries.
401 UNAUTHENTICATED response with Request had invalid authentication credentials means the token is missing, malformed, or expired.
Basic Syntax
Request bodies come from
{value.json.body}, not {payload}. The MQTT payload carries control fields (access_token, datasetId, tableId, jobId, location, maxResults) next to a body object, and only body is sent to Google. Google APIs reject unknown top-level fields with 400 Invalid JSON payload received.Example MQTT payloads
Create a dataset (topicdata/google-bigquery/dataset/create):
data/google-bigquery/table/create):
data/google-bigquery/rows/insert). insertId gives best-effort de-duplication on retries:
{"kind":"bigquery#tableDataInsertAllResponse"}. Rejected rows return HTTP 200 with insertErrors (for example no such field: pressure.), so check the response body, not just the status.
Run SQL (topic data/google-bigquery/query/run):
data/google-bigquery/query/results, data/google-bigquery/job/get):
Quotas and limits
insertAll: up to 10 MB per request; Google recommends about 500 rows per request. Streamed rows are queryable within seconds, but they can’t be changed with DML for a short window after insert.jobs.querywaits up totimeoutMs(default 10 s). IfjobCompleteisfalse, pollGetQueryResultswithjobReference.jobId.- On-demand queries are billed by bytes scanned. Use partitioned tables and
WHEREfilters on high-rate telemetry. See BigQuery quotas.
Testing
Status: blocked on credentials. The factory box has no Google OAuth credentials, so the connector has not been smoke-tested against Google. Wire format was checked against a local goccy/bigquery-emulator v0.8.1, which does not check OAuth: the request shapes for all nine events returned200 (datasets list and create, table create and list, insertAll of two rows, tabledata.list read-back, jobs.query SELECT 1 and an aggregate read-back, getQueryResults, jobs.get). Against bigquery.googleapis.com without a valid token, GET …/datasets returned 401 UNAUTHENTICATED, as expected.
To clear the gate, run GET /bigquery/v2/projects/{p}/datasets (expect 200) and POST /bigquery/v2/projects/{p}/queries with {"query":"SELECT 1","useLegacySql":false} (expect 200, jobComplete: true) using a real access token, project id, and lab dataset.

