Skip to main content
Technical Preview

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>.
Storage Write API is not included. The BigQuery Storage Write API (and Storage Read API) is gRPC/protobuf only, so a REST_API connector can’t call it. This template uses the legacy tabledata.insertAll streaming endpoint, which is plain HTTPS + JSON. Load jobs with media upload (/upload/bigquery/v2/…) and destructive deletes are also left out.

Auth

BigQuery needs an OAuth 2.0 access token in Authorization: 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), or https://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.
A 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 (topic data/google-bigquery/dataset/create):
Create a telemetry table (topic data/google-bigquery/table/create):
Stream rows (topic data/google-bigquery/rows/insert). insertId gives best-effort de-duplication on retries:
A clean insert returns {"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):
Fetch results or job status (topics data/google-bigquery/query/results, data/google-bigquery/job/get):
Always send location and maxResults for the GET events, since the template puts them in the query string. Use the dataset’s location (US, EU, europe-west1, …); jobs outside the US and EU multi-regions are not found without it.

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.query waits up to timeoutMs (default 10 s). If jobComplete is false, poll GetQueryResults with jobReference.jobId.
  • On-demand queries are billed by bytes scanned. Use partitioned tables and WHERE filters 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 returned 200 (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.
Last modified on October 21, 2026