Octov0.11.7
Guides

MongoDB Documents

Store and query JSON documents with the mongodb connector.

This guide wires an HTTP API to a MongoDB collection: store request bodies as documents, read them back by id, list them, and total them with an aggregation. It follows samples/mongodb-orders.yaml. Database CRUD builds the same API over SQL, where a row needs a column mapping in each direction; a message body already is a document.

There is no in-memory MongoDB, so this sample needs a server. One container is enough:

docker run -d --rm -p 27017:27017 --name octo-mongo mongo:7
task run:sample -- mongodb-orders.yaml

Declare the connector

The mongodb connector owns the client. Only uri is required; database is the default for blocks that do not name one.

env:
  - name: MONGODB_URI
    default: mongodb://localhost:27017
  - name: MONGODB_DATABASE
    default: octo-samples
  - name: MONGODB_VERIFY_ON_START
    default: "true"

connectors:
  - name: orders-db
    type: mongodb
    settings:
      uri: ${MONGODB_URI}
      database: ${MONGODB_DATABASE}
      verifyOnStart: ${MONGODB_VERIFY_ON_START}
      maxPoolSize: 4

A ${VAR} the env: block never declares is a load error, not an empty string. The defaults here run against the container above with nothing exported. maxPoolSize is 4 rather than the default 32 because the sample's flows run four workers each. The 32 is sized to the workers a flow gets when it does not choose a count, max(8, GOMAXPROCS * 4), which is 32 on an eight-core host.

Keep the password out of the URI. The connector takes it as its own password setting and merges it in at startup, so only the credential is a secret.

Store the request body

Storing a body is one block:

      - type: mongodb-insert
        name: insert-order
        settings:
          connector: orders-db
          collection: '"orders"'
          document: body

collection is a CEL expression, so a flow can route to a different collection per message; '"orders"' is the expression string literal orders. document writes one document for an object and all of them for a list. Posting an order answers with the ids MongoDB generated:

$ curl -s -X POST localhost:8080/api/v1/orders -d '{"sku":"A1","qty":2,"price":10}'
{"insertedCount":1,"insertedIds":[{"$oid":"6a72e1e21393cc3cf3cb59a2"}]}

Read it back without converting anything

Documents cross the message boundary as MongoDB Extended JSON in both directions, so the {"$oid": ...} from a response goes straight back into a filter unchanged:

      - type: mongodb-find
        name: select-order
        settings:
          connector: orders-db
          collection: '"orders"'
          filter: '{"_id": {"$oid": vars.id}}'
          single: true
$ curl -s localhost:8080/api/v1/orders/6a72e1e21393cc3cf3cb59a2
{"_id":{"$oid":"6a72e1e21393cc3cf3cb59a2"},"price":10,"qty":2,"sku":"A1"}

single: true returns the document rather than a list, and null when nothing matched (an answer to branch on, not an error). Without it the result is a list, [] rather than null when empty, so a foreach over the body gets zero iterations.

List, newest first

      - type: mongodb-find
        name: list-orders
        settings:
          connector: orders-db
          collection: '"orders"'
          sort: '[{"_id": -1}]'
          limit: 50

sort is a list of single-key objects, not one object with several keys. MongoDB applies sort keys in the order written and a CEL object cannot carry that order, so a multi-key object is refused. One key on its own may be written either way.

Total it on the server

A pipeline is JSON, written the way it would be typed into mongosh, with body and vars reachable inside it:

      - type: mongodb-aggregate
        name: total-by-sku
        settings:
          connector: orders-db
          collection: '"orders"'
          pipeline: |
            [
              {"$group": {"_id": "$sku",
                          "orders": {"$sum": 1},
                          "value": {"$sum": {"$multiply": ["$qty", "$price"]}}}},
              {"$sort": [{"value": -1}, {"_id": 1}]}
            ]
$ curl -s localhost:8080/api/v1/orders/totals
[{"_id":"A1","orders":2,"value":50},{"_id":"B2","orders":1,"value":50}]

Both skus total 50 and the second sort key breaks the tie by _id ascending. A $sort stage follows the same list-form rule as the find block's sort.

Test it without a server

The sample's suite (mongodb-orders_test.yaml) mocks the blocks. A mock replaces the block, not the connector, which still starts and by default pings the server; verifyOnStart: false lets the config build without one. One case leaves the block unmocked and points the connector at a refused port: the dial failure proves the block issued the call.

task runtime:test:samples

Tear the container down with docker rm -f octo-mongo.

On this page