MongoDB
MongoDB is a popular open-source document database that stores data as flexible BSON documents.
The MongoDB Wrapper allows you to read and write data from MongoDB within your Postgres database. Top-level BSON fields are mapped to declared columns by exact name; missing fields surface as SQL NULL. A special _doc jsonb column receives the full document for nested and array access.
Preparation
Before you can query MongoDB, you need to enable the Wrappers extension and store your credentials in Postgres.
Enable Wrappers
Make sure the wrappers extension is installed on your database:
1 | |
Enable the MongoDB Wrapper
Enable the mongodb_wrapper FDW:
1 2 3 | |
Store your credentials (optional)
By default, Postgres stores FDW credentials inside pg_catalog.pg_foreign_server in plain text. Anyone with access to this table will be able to view these credentials. Wrappers is designed to work with Vault, which provides an additional level of security for storing credentials. We recommend using Vault to store your credentials.
1 2 3 4 5 6 | |
Connecting to MongoDB
We need to provide Postgres with the credentials to connect to MongoDB, and any additional options. We can do this using the create server command:
1 2 3 4 5 | |
1 2 3 4 5 | |
The connection string follows the standard MongoDB URI format and supports mongodb:// and mongodb+srv:// schemes. Credentials, TLS options, replica set names, and other driver options can all be encoded in the URI.
Some connection string examples:
mongodb://root:secret@localhost:27017/mongodb://app_user:password@db.example.com:27017/?tls=truemongodb://user:pass@host1:27017,host2:27017/?replicaSet=rs0mongodb+srv://user:password@cluster0.mcqtkst.mongodb.net/?appName=Cluster0
MongoDB Atlas IP Access List
If you connect to MongoDB Atlas, you must allow network access from your Supabase database server in Atlas Network Access / IP Access List.
Supabase database server egress IP addresses are dynamic and there is no fixed IP range you can safelist. In practice, this often means allowing 0.0.0.0/0 in Atlas to make the connection work.
If you are concerned about this security risk, place a proxy or gateway with a fixed public IP between your Supabase database instance and MongoDB, and safelist only that proxy IP in Atlas.
Create a schema
We recommend creating a schema to hold all the foreign tables:
1 | |
Options
The following options are available when creating MongoDB foreign tables:
database- MongoDB database name, requiredcollection- MongoDB collection name, requiredrowid_column- Column to use as the row identifier, optional for data scan, required for data modify. The conventional value is_id.
Entities
Collections
The MongoDB Wrapper supports data reads and writes from MongoDB collections.
Operations
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| Collections | ✅ | ✅ | ✅ | ✅ | ❌ |
Usage
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Notes
- Supports
where,order by, andlimitclause pushdown - Documents are streamed one at a time from MongoDB; no full buffering in memory
- A column named
_docof typejsonbreceives the complete document and can be used with Postgres JSON operators (->,->>) to access nested fields and arrays - When
rowid_columnis set, INSERT, UPDATE, and DELETE are supported
Schema Mapping
Each column declared on the foreign table maps to a top-level BSON field of the same name (exact match):
- If a document does not contain a field, the corresponding column is set to
NULL. - Dots in column names are treated as literal characters — they do not traverse embedded documents. Use the
_doccolumn for nested field access. - If a column named
_docof typejsonbis declared, it receives the full BSON document serialized as JSON. When_docis declared, projection is disabled so that the complete document is returned from MongoDB.
Example: access a nested address.city field via _doc:
1 2 | |
Query Pushdown Support
This FDW supports where, order by, and limit clause pushdown.
Supported Operators
The following SQL predicates are translated to MongoDB filter operators:
| SQL predicate | MongoDB filter |
|---|---|
= |
{field: {$eq: v}} |
!= |
{field: {$ne: v}} |
< |
{field: {$lt: v}} |
<= |
{field: {$lte: v}} |
> |
{field: {$gt: v}} |
>= |
{field: {$gte: v}} |
IN (...) |
{field: {$in: [...]}} |
NOT IN (...) |
{field: {$nin: [...]}} |
IS NULL |
{field: {$eq: null}} |
IS NOT NULL |
{field: {$ne: null}} |
Multiple where predicates are AND'd at the top level of the filter document. Array-form predicates like IN (...) / NOT IN (...) (and the equivalent = ANY(ARRAY[...]) / <> ALL(ARRAY[...])) are pushed down as $in / $nin. Arbitrary OR predicates between unrelated columns are not pushed down — they are re-checked by Postgres. Any predicate shape that is not supported is omitted from the MongoDB filter and re-checked by Postgres after the rows are returned, so the result is always correct.
Supported Data Types
| BSON Type | Postgres Type | Notes |
|---|---|---|
| Boolean | bool | |
| Int32 | int2 / int4 / int8 | |
| Int64 | int8 / numeric | |
| Double | float8 / float4 | |
| Decimal128 | numeric | |
| String | text / varchar | |
| ObjectId | text | Returned as a 24-character lowercase hex string. A 24-char hex value in a qual is coerced back to ObjectId for the filter. |
| DateTime | timestamp / timestamptz | |
| Document | jsonb | |
| Array | jsonb | |
| Binary | bytea | |
| Null / missing | any | Column is set to NULL |
Note
_id fields that are ObjectId values are returned as their 24-character hex representation. When querying by _id with a 24-character hex string, the value is automatically coerced back to an ObjectId for the filter — no explicit casting is required.
Writes
INSERT, UPDATE, and DELETE are enabled when rowid_column is set on the foreign table.
- INSERT: Each non-null column is written as a top-level BSON field. Null columns are omitted entirely (not stored as explicit BSON nulls). If
_idis not supplied, MongoDB generates anObjectIdautomatically. - UPDATE: Non-null columns are passed to
$set; null columns are passed to$unset, which removes the field from the document. The document is located by matchingrowid_column. - DELETE: The document matching
rowid_column = rowidis deleted viadelete_one.
Limitations
This section describes important limitations and considerations when using this FDW:
- Aggregate pushdown is not supported —
COUNT,SUM,AVG,MIN, andMAXare computed in Postgres after fetching rows import foreign schemais not supported; foreign table definitions must be declared manuallyLIKEpredicates are not pushed down to MongoDB$regex; pattern matching happens in Postgres- Nested field path access (e.g.,
address.city) is not supported as column names — use the_doc jsonbcolumn and Postgres JSON operators instead - Multi-document transactions and batched writes are not supported in v1
- Change streams are not supported
- Materialized views using foreign tables may fail during logical backups
- For MongoDB Atlas, Supabase egress IPs are dynamic (no fixed range). You may need to allow
0.0.0.0/0in Atlas IP Access List, or use a proxy/gateway with a fixed IP to avoid broad public access
Examples
Basic example
This example shows how to query a MongoDB collection from Postgres.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
Data modification example
This example demonstrates INSERT, UPDATE, and DELETE on a foreign table. The rowid_column option is required for data modification:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
Using Vault for credentials
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |