Skip to content

The shell and Compass

There are three ways you will talk to MongoDB: the mongosh shell for quick, scriptable commands; MongoDB Compass, the official graphical client, for clicking around and exploring; and a driver in your application’s language for the real work. This lesson covers all three, ending with how to open a connection from each of the five environments this course uses.

Every one of them connects with the same kind of address - a connection string - so once you understand that, switching between tools is trivial. Everything below was run on mongo 8.3.11 with mongosh 2.11.1 and Node driver 7.7.0 on one Mac; the interactive transcript comes from a throwaway mongo:8 container with a library database holding 25 members.

mongosh is an interactive JavaScript shell. You type a command, it runs against the server, and it prints the result. Here is a real session (the test> / library> prompts are the shell’s own; lines you type follow them):

test> show dbs
admin 100.00 KiB
config 12.00 KiB
library 8.00 KiB
local 40.00 KiB
test> use library
switched to db library
library> db.members.find({}, { _id: 0, name: 1 })
[
{ name: 'Member 1' }, { name: 'Member 2' },
...
{ name: 'Member 19' }, { name: 'Member 20' }
]
Type "it" for more
library> it
[
{ name: 'Member 21' },
{ name: 'Member 22' },
{ name: 'Member 23' },
{ name: 'Member 24' },
{ name: 'Member 25' }
]
library> it
no cursor
  • show dbs lists the databases; use library selects one (it need not exist yet - MongoDB creates it at the first write); db.members.find() reads.
  • The shell prints 20 documents per batch (config.get("displayBatchSize") returned 20), then Type "it" for more. it stands for iterate, and once the cursor is exhausted it answers no cursor.

show, use and it are shell-only commands, not JavaScript: they work at the prompt but not inside a script, an --eval string, or a driver. Every one has a plain-JavaScript equivalent that does work everywhere, and this fence proves it (it seeds its own library members first):

mongosh/shell-equivalents.js
db.members.insertMany(Array.from({ length: 25 }, (_, i) => ({ name: `Member ${i + 1}`, joined: 2020 + (i % 6), fines: i % 4 })));
const lib = db.getSiblingDB("library_demo"); // like `use library_demo`
lib.members.insertOne({ name: "Grace" });
const names = db.getMongo().getDBNames(); // like `show dbs`
console.log(names.includes("library_demo")); // true: created by the first write
const cursor = db.members.find({}, { _id: 0, name: 1 });
console.log(cursor.next(), cursor.hasNext()); // like `it`, one document at a time
console.log(db.members.find().toArray().length); // or drain the whole cursor
console.log(config.get("displayBatchSize")); // the interactive page size
assert.strictEqual(db.members.find().toArray().length, 25);
assert.strictEqual(config.get("displayBatchSize"), 20);
db.getSiblingDB("library_demo").dropDatabase();
true
{ name: 'Member 1' } true
25
20

Running mongosh without typing: --eval and --file

Section titled “Running mongosh without typing: --eval and --file”

For scripts and cron jobs you do not sit at a prompt. --eval runs one expression, --file runs a script, and --quiet suppresses the banner. These ran against the throwaway container (with credentials, see the next section):

scripts/mongosh-eval-file.sh
mongosh --quiet "mongodb://root:secret@localhost:27017/library?authSource=admin" \
--eval 'db.members.countDocuments({ fines: { $gt: 1 } })'
mongosh --quiet "mongodb://root:secret@localhost:27017" --file report.js
12
1 of 2 members owe fines

where report.js is an ordinary script - note console.log, not show:

mongosh/report.js
const library = db.getSiblingDB("report_demo");
library.members.insertMany([{ name: "Ada", fines: 0 }, { name: "Alan", fines: 3 }]);
const total = library.members.countDocuments();
const owing = library.members.countDocuments({ fines: { $gt: 0 } });
console.log(`${owing} of ${total} members owe fines`);
assert.strictEqual(owing, 1);
library.dropDatabase();

MongoDB Compass is the official desktop GUI, and it is the friendliest way to explore data you do not yet understand. You connect by pasting a connection string into the box on the start screen and clicking Connect. Compass also embeds a MongoDB shell.

Once connected, the sidebar lists your databases and collections. The Compass documentation (mongodb.com/docs/compass, fetched 2026-09-30) names these tabs for a collection:

  • Documents - insert, browse and query documents; edit a field in place.
  • Aggregations - a visual pipeline builder: add stages one at a time and watch the documents transform (the aggregation module leans on it).
  • Schema - samples the collection and shows which fields exist and what types they hold.
  • Explain Plan - how MongoDB executed a query, index or full scan (the heart of the indexes module).
  • Indexes - list, create and drop indexes.
  • Validation - schema validation rules.

Compass is a desktop app, so we could not run it in the same automated way as the rest of this lesson; its layout changes between releases, so trust the docs page over any screenshot. Compass and the shell are complementary: Compass for seeing and exploring, the shell for repeating and scripting. Neither replaces a driver in your application code.

When it is time to write an application, you connect with a driver. The connection string is the same; only the API around it differs. Here is how to open a connection in each of the five environments. Python, Go, Rust and Node each printed connected when we ran the snippets below verbatim against a plain mongo:8 container on port 27017.

scripts/mongosh-connect.sh
mongosh "mongodb://localhost:27017"

(Versions used: PyMongo 4.18.2, Go driver v2.9.1, Rust driver 3.9.1, Node driver 7.7.0. The Python, Go and Rust tabs were run in scratch programs that supply client/ctx around the snippet.)

The format is mongodb://[user:password@]host[:port][/database][?options]. Three options decide most “it worked yesterday” problems. We ran each against a real server with a 2 second serverSelectionTimeoutMS:

// app/connection-options.mjs @skip-verify needs a second, auth-enabled container on host port 27019
import { MongoClient } from "mongodb";
async function tryUri(label, uri) {
const client = new MongoClient(uri, { serverSelectionTimeoutMS: 2000 });
try {
await client.connect();
await client.db("library").collection("members").findOne();
console.log(label, "-> ok");
} catch (e) {
console.log(label, "->", e.name + ":", e.message.split("\n")[0]);
} finally { await client.close(); }
}
await tryUri("no authSource ", "mongodb://root:secret@localhost:27019/library");
await tryUri("authSource=admin", "mongodb://root:secret@localhost:27019/library?authSource=admin");
await tryUri("replicaSet=rs0 ", "mongodb://localhost:27117/?replicaSet=rs0");
await tryUri("replicaSet=nope ", "mongodb://localhost:27117/?replicaSet=nope");
await tryUri("directConnection", "mongodb://localhost:27117/?directConnection=true");
no authSource -> MongoServerError: Authentication failed.
authSource=admin -> ok
replicaSet=rs0 -> ok
replicaSet=nope -> MongoServerSelectionError: Server selection timed out after 2000 ms
directConnection -> ok
  • authSource - the root user was created in the admin database. When the string names a database (/library) and no authSource, the driver authenticates against that database, where the user does not exist. Same credentials, Authentication failed. until you add ?authSource=admin. With no database in the path the default is admin.
  • replicaSet - must match the set’s real name; a wrong name never finds a matching member, so the driver times out with a server-selection error rather than “wrong name”.
  • directConnection=true - skip discovery and talk only to the address you gave (useful when the set advertises a hostname your machine cannot reach, see the Docker lesson).

Once a client is connected, you reuse it for the whole life of your program. A client owns a connection pool (plus monitoring connections to the server) and is meant to be shared. We measured the difference with 50 sequential “requests” on a local server:

50 requests, new client each, never closed: 203 ms, connections +100
50 requests, one shared client: 33 ms, connections +2

(Second run: 162 ms vs 36 ms; +100 vs +2 both times.) Two server connections per client leaked, and about six times the latency, on a local socket. Across a network with TLS the gap only widens.

flowchart TD
  Q["What do you want to do"] --> S["Quick one-off command or a script"]
  Q --> C["Explore and understand unfamiliar data"]
  Q --> D["Build a feature in your app"]
  S --> SH["mongosh shell"]
  C --> CO["Compass GUI"]
  D --> DR["A driver in your language"]
Pick the shell for scripting, Compass for exploring, and a driver for application code
  • The connection string format is mongodb://[user:password@]host:port[/database][?options]. The host is the only required part.
  • Use the shell vs Compass vs a driver by intent: the shell for fast and repeatable commands, Compass for visual exploration and schema discovery, and a driver for anything your application does in production.
  • Authentication changes the string, not the workflow. If you set a root user in the Docker lesson, include root:secret@ before the host in every tool, and authSource=admin when the path names another database.
  • show, use, it are not JavaScript. Inside a script use db.getSiblingDB(...), db.getMongo().getDBNames() and cursor methods.

An assistant writing an Express endpoint on top of MongoDB will often “keep the handler self-contained”:

🐛 Spot the bug

// AI comment: "self-contained handler - connects, queries, done"
app.get("/members/:name", async (req, res) => {
const client = new MongoClient(process.env.MONGO_URL);
const member = await client.db("library").collection("members").findOne({ name: req.params.name });
res.json(member);
});
Show the answer

Every request builds a new client, connects it, and never closes it. We ran exactly that loop 50 times against a local server: it took 203 ms (162 ms on a second run) against 33 ms for one shared client, and the server’s connection count went up by 100, two per abandoned client (a connection for the pool and one for monitoring). Under real traffic the connection count climbs until the server’s or OS’s limit is hit and requests start failing. The fix is a single client created once at startup and reused (and closed on shutdown):

app/shared-client.mjs
// One client for the whole process: this is what the prelude's `client` is.
const members = client.db("library").collection("members");
await members.insertOne({ name: "Grace" });
const member = await members.findOne({ name: "Grace" }); // called per request, same pool
if (member?.name !== "Grace") throw new Error("expected Grace");
console.log(member.name);
Grace

A connection string is an address plus instructions: where to knock (host:port), who you are (user:password), where your account lives (authSource), and what topology to expect (replicaSet, directConnection). The shell, Compass and drivers are three front ends to the same door. The only stateful object is the client - a pool of long-lived connections - and that is why it is created once.

ChoiceBenefitCost
mongoshFast to script and repeat, runs over SSH on a server with no GUI, --eval / --file fit cron and CIYou need to remember command syntax and field names yourself; show/use/it do not work outside the prompt
Compass (GUI)Instantly see the schema and real documents, build an aggregation pipeline stage by stage, understand new data fasterNeeds a screen - cannot be used over SSH or automated - and is heavier than one terminal command
One shared driver client33 ms vs 203 ms for 50 requests, +2 connections vs +100 (measured above)You must manage its lifecycle (create at startup, close on shutdown)
  • “Compass is a good tool to automate routine jobs.” - It is built for click-to-explore. Work that runs every day should be a mongosh --file script or driver code.
  • “A new client per request is fine, the driver pools anyway.” - The pool lives inside the client; a new client is a new pool. Measured: connections +100 for 50 requests.
  • “mongosh and a driver share identical syntax.” - use, show dbs, it exist only at the shell prompt. The drivers use their own APIs.
  • “Same credentials, so any connection string works.” - without authSource=admin the string above failed with Authentication failed.
  • “A wrong replicaSet name gives a clear error.” - it gives a 2 second server-selection timeout.

Real-world example

The driver itself encodes this rule: a MongoClient created with no options reports maxPoolSize 100 and minPoolSize 0 (Node driver 7.7.0, client.options), a pool sized per process - not per request.

In mongosh, what does typing "it" do after a find()?
Which Compass tab samples a collection and shows which fields and types exist?
What is the recommended lifecycle for a driver client object?
The root user was created in admin. This string fails with "Authentication failed." although the password is right: mongodb://root:secret@localhost:27017/library. Why?
Which of these works inside a mongosh --file script?