Cameleer

Google Sheets Stream component

The google-sheets-stream component polls a range of a Google Sheets spreadsheet and emits the rows that were appended since the previous poll.

Scheme: google-sheets-stream: · Crate: cameleer-cloud · Cargo feature: cloud · Consumer: yes · Producer: no · Apache Camel: Google Sheets Stream

When to use it

  • Trigger a route when someone adds a row to a shared sheet: a new order, a support request, a task for an AI agent to work on.
  • Feed rows entered by a business team into a database, a queue or an API.
  • Run a "sheet as inbox" workflow at the edge without a webhook or a public URL (it only polls).

URI format

text
google-sheets-stream://spreadsheetId?range=Sheet1!A2:D1000[&options]

The spreadsheet id is the path (or the spreadsheetId option). range is required; the endpoint fails to start without it.

Example

Every 30 seconds, read Orders!A2:D1000 and send each new row on its own, as JSON, to the log. The first poll emits all rows already in the range.

yaml example: google-sheets-stream-new-rows
- route:
    id: new-orders
    from:
      uri: "google-sheets-stream://{{env:SHEET_ID}}?range=Orders!A2:D1000&splitResults=true&delay=30000"
      steps:
        - log: "row ${header[CamelGoogleSheets.valueIndex]}: ${body}"
        - marshal:
            json: {}
        - to: "log:new-orders"

With splitResults=true the body of each exchange is one row, for example ["1042","Ada","3","shipped"], and CamelGoogleSheets.valueIndex is its 1-based row number inside the range. Without it, one exchange per poll carries all new rows as a ValueRange map (range, majorDimension, values).

Rust DSL

rust
ctx.add_routes(|b| {
    b.from("google-sheets-stream://{{env:SHEET_ID}}?range=Orders!A2:D1000&splitResults=true&delay=30000")
        .log("row ${header[CamelGoogleSheets.valueIndex]}: ${body}")
        .marshal("json")
        .to("log:new-orders");
})?;

How new rows are detected

The consumer remembers how many rows the range had at the previous poll and emits only the rows after that position. If rows are deleted so that the range becomes shorter, it starts counting again from the new end. Edits to rows that were already emitted are not detected.

This differs from Apache Camel, whose consumer emits the whole range on every poll. Use a filter or an idempotent consumer there; with Cameleer you get only the appended rows. The position is kept in memory, so after a restart the first poll emits the whole range again.

Options

optiondefaultdescription
spreadsheetIdpathThe spreadsheet id (instead of the path).
rangeA1 range to read, for example Orders!A2:D1000. Required.
splitResultsfalseOne exchange per new row instead of one per poll.
majorDimensionROWSROWS or COLUMNS.
valueRenderOptionFORMATTED_VALUE, UNFORMATTED_VALUE or FORMULA.
delay500Milliseconds between polls (scheduled poll consumer options such as initialDelay (default 1000), repeatCount and backoffMultiplier also apply).
serviceAccountKeyGOOGLE_APPLICATION_CREDENTIALSService account JSON key (file:path, path or inline JSON).
clientId, clientSecret, refreshTokenOAuth user credentials (refresh-token flow).
accessTokenA fixed OAuth access token.
delegate (alias subject)User to impersonate with domain-wide delegation.
scopeshttps://www.googleapis.com/auth/spreadsheetsComma separated OAuth scopes.
uriEndpointOverride + overrideEndpoint=trueBase URL of an emulator or proxy.

The other shared Google options (tokenServerUrl, useMetadataServer, authenticate, proxyHost, proxyPort, trustAllCertificates) work as on the google-sheets page. includeGridData and maxResults are accepted and ignored.

Message headers

headerdirectiondescription
CamelGoogleSheets.spreadsheetIdoutThe spreadsheet id.
CamelGoogleSheets.rangeoutThe range returned by the API (for example Orders!A2:D1000).
CamelGoogleSheets.majorDimensionoutROWS or COLUMNS.
CamelGoogleSheets.valueIndexoutWith splitResults=true: the 1-based row number within the range.

Header names contain dots, so read them in Simple with the bracket form: ${header[CamelGoogleSheets.valueIndex]}.

Run it with Docker

sh
docker run --rm \
  -e SHEET_ID=1AbC...xyz \
  -e GOOGLE_APPLICATION_CREDENTIALS=/secrets/key.json \
  -v "$PWD/key.json:/secrets/key.json:ro" \
  -v "$PWD:/routes:ro" pmsudhi/cameleer run /routes/new-orders.camel.yaml

Common questions

How do I trigger a route when a row is added to a Google Sheet?

Use google-sheets-stream://<spreadsheetId>?range=...&splitResults=true. Each appended row arrives as one exchange with the row as a list.

Why do I get all rows again after a restart?

The last row position is kept in memory only. Skip the first batch with a filter, or record processed ids with an idempotent repository.

Can I watch several tabs?

Use one route per range, each with its own google-sheets-stream endpoint.

Found a problem?

If something on this page doesn't match what Cameleer does, or you hit a bug while building with it, please help fix it, whether you are a person or a coding agent: open an issue that describes what you saw, then fork the repository, fix it, add or update a test, run cargo test --workspace, and open a pull request. CONTRIBUTING.md explains the ground rules.

Edit this page on GitHub