---
name: mongodb
description: "TypeScript setup for the MongoDB Node.js driver. `@types/mongodb@4.0.7` is a stub package; install `mongodb` itself because it ships its own type definitions."
metadata:
  languages: "typescript"
  versions: "4.0.7"
  revision: 1
  updated-on: "2026-03-13"
  source: maintainer
  tags: "typescript,mongodb,nodejs,types,definitelytyped,database"
---

# MongoDB TypeScript Guide

## Golden Rule

`@types/mongodb@4.0.7` is not the package you should install for a modern MongoDB Node.js application.

The npm package page for `@types/mongodb` marks it as a stub package and says that `mongodb` provides its own type definitions. Install the real `mongodb` driver and import both runtime APIs and TypeScript types from `mongodb`.

If an older project still has `@types/mongodb` in its lockfile, remove it unless that project is intentionally pinned to a historical driver setup that depended on DefinitelyTyped declarations.

## Install

Install the MongoDB Node.js driver plus the normal TypeScript toolchain for a Node.js app:

```bash
npm install mongodb
npm install -D typescript @types/node
```

If your project already added the old stub package, remove it:

```bash
npm uninstall @types/mongodb
```

There are no package-specific credentials beyond your MongoDB connection string. The practical setup points are your `MONGODB_URI`, your database name, and your TypeScript compiler.

## Initialization

Create the client from a connection string and keep the database name in configuration instead of hard-coding it throughout your app.

```ts
import { MongoClient } from "mongodb";

const mongoUri = process.env.MONGODB_URI;
const databaseName = process.env.MONGODB_DB ?? "app";

if (!mongoUri) {
  throw new Error("Set MONGODB_URI before starting the app");
}

const client = new MongoClient(mongoUri);

await client.connect();

export const db = client.db(databaseName);
```

If your `tsconfig.json` limits ambient types with `compilerOptions.types`, include `node` so `process.env` and other Node globals stay typed:

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "types": ["node"],
    "skipLibCheck": false
  }
}
```

## Common Workflows

### Use a typed collection

Pass your document interface to `db.collection<TSchema>()` so CRUD calls are checked against your application model.

```ts
import { MongoClient, type Collection } from "mongodb";

type User = {
  email: string;
  displayName: string;
  active: boolean;
};

const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();

const db = client.db("app");
const users: Collection<User> = db.collection<User>("users");

const user = await users.findOne({ email: "ada@example.com" });

if (user) {
  console.log(user.displayName);
}
```

This is the main TypeScript boundary for the driver: the collection generic controls the document shape used by inserts, queries, and returned results.

### Insert documents without requiring `_id` in input

If your model does not include `_id`, or you want MongoDB to generate it, type insert payloads with `OptionalId<T>`.

```ts
import {
  MongoClient,
  type OptionalId,
  type WithId,
} from "mongodb";

type User = {
  email: string;
  displayName: string;
  active: boolean;
};

const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();

const users = client.db("app").collection<User>("users");

const newUser: OptionalId<User> = {
  email: "ada@example.com",
  displayName: "Ada",
  active: true,
};

await users.insertOne(newUser);

const stored: WithId<User> | null = await users.findOne({ email: newUser.email });
```

`OptionalId<T>` is the usual choice for create operations when `_id` is generated by the server, and `WithId<T>` reflects the shape you read back from the collection.

### Model `_id` explicitly with `ObjectId`

When your application code works with document identifiers directly, declare `_id` as `ObjectId` and import that type from `mongodb`.

```ts
import { MongoClient, ObjectId } from "mongodb";

type Project = {
  _id: ObjectId;
  name: string;
  ownerId: ObjectId;
};

const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();

const projects = client.db("app").collection<Project>("projects");

const projectId = new ObjectId("65f1a1938f8b6c6d4b6a9f10");
const project = await projects.findOne({ _id: projectId });
```

Use the driver's `ObjectId` type and constructor instead of treating `_id` as an arbitrary string unless your collection actually stores string IDs.

### Reuse typed filters and update documents

When you move query logic into helpers, type the inputs with `Filter<TSchema>` and `UpdateFilter<TSchema>` so callers pass valid field names and operator shapes.

```ts
import {
  MongoClient,
  type Filter,
  type UpdateFilter,
} from "mongodb";

type User = {
  email: string;
  displayName: string;
  active: boolean;
  loginCount: number;
};

function buildActiveUserFilter(email: string): Filter<User> {
  return {
    email,
    active: true,
  };
}

function recordLogin(): UpdateFilter<User> {
  return {
    $inc: { loginCount: 1 },
  };
}

const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();

const users = client.db("app").collection<User>("users");

await users.updateOne(buildActiveUserFilter("ada@example.com"), recordLogin());
```

This keeps shared data-access helpers typed against the same collection model instead of falling back to unstructured objects.

## Important Pitfalls

- Do not import from `@types/mongodb`; import from `mongodb`.
- Do not keep both `mongodb` and `@types/mongodb` installed in a modern project unless you are intentionally maintaining an older dependency tree.
- `Collection<TSchema>` only types your application code. It does not validate documents already stored in the database.
- If your interface includes `_id`, inserts may require `_id` unless you use `OptionalId<T>` for create payloads.
- If your project restricts ambient types with `compilerOptions.types`, include `node` or your MongoDB setup code may lose `process.env` typings.

## Minimal End-to-End Example

```ts
import {
  MongoClient,
  type OptionalId,
  type WithId,
} from "mongodb";

type Todo = {
  title: string;
  done: boolean;
};

const mongoUri = process.env.MONGODB_URI;
const databaseName = process.env.MONGODB_DB ?? "app";

if (!mongoUri) {
  throw new Error("Set MONGODB_URI before running this script");
}

const client = new MongoClient(mongoUri);

await client.connect();

const todos = client.db(databaseName).collection<Todo>("todos");

const created: OptionalId<Todo> = {
  title: "write docs",
  done: false,
};

await todos.insertOne(created);

const openTodos: WithId<Todo>[] = await todos.find({ done: false }).toArray();

console.log(openTodos.map((todo) => todo.title));

await client.close();
```

This is the practical boundary for `@types/mongodb`: install `mongodb`, connect with `MongoClient`, type collections with `collection<T>()`, and import the driver's helper types from the same package.

## Version-Sensitive Notes

- This guide targets `@types/mongodb@4.0.7`.
- `@types/mongodb@4.0.7` is a stub package; the runtime package you actually use is `mongodb`.

## Official Sources

- https://www.npmjs.com/package/@types/mongodb
- https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mongodb
- https://www.npmjs.com/package/mongodb
- https://www.mongodb.com/docs/drivers/node/v4.11/fundamentals/typescript/
- https://mongodb.github.io/node-mongodb-native/4.0/
