How to Use MongoDB Atlas with Astro: Complete Guide
Step-by-step guide to integrating MongoDB Atlas with your Astro website.
MongoDB Atlas is a fully managed cloud MongoDB database service. If your data is document-shaped (nested objects, variable schemas, arrays of items), MongoDB is often a better fit than relational databases. Atlas handles the infrastructure: clustering, backups, scaling, and monitoring. You just write queries. For Astro developers, MongoDB Atlas is a solid choice when your content does not fit neatly into rows and columns, or when you want the flexibility to evolve your data model without migrations.
The integration uses MongoDB's official Node.js driver or Mongoose ODM in your Astro server-side code. You query Atlas from API routes, SSR pages, or build scripts and render the results.
Prerequisites
- Node.js 18+
- An Astro project (
npm create astro@latest) - A MongoDB Atlas account (free tier includes 512MB storage, Dedicated clusters from $57/mo)
- A cluster created in Atlas with a database user and network access configured
Installation
Install the MongoDB Node.js driver:
npm install mongodb
Or if you prefer Mongoose for schema validation and modeling:
npm install mongoose
Configuration
Get your connection string from the Atlas dashboard. Go to Database > Connect > Drivers and copy the connection string.
Add it to .env:
MONGODB_URI=mongodb+srv://username:password@cluster0.abc123.mongodb.net/mydb?retryWrites=true&w=majority
Create a database client helper. With the native driver:
// src/lib/mongodb.ts
import { MongoClient } from "mongodb";
const uri = import.meta.env.MONGODB_URI;
const client = new MongoClient(uri);
let connected = false;
export async function getDb(dbName = "mydb") {
if (!connected) {
await client.connect();
connected = true;
}
return client.db(dbName);
}
With Mongoose:
// src/lib/mongoose.ts
import mongoose from "mongoose";
const uri = import.meta.env.MONGODB_URI;
let isConnected = false;
export async function connectDB() {
if (isConnected) return;
await mongoose.connect(uri);
isConnected = true;
}
Define a Mongoose model for your content:
// src/models/Post.ts
import mongoose from "mongoose";
const postSchema = new mongoose.Schema({
title: { type: String, required: true },
slug: { type: String, required: true, unique: true },
content: String,
excerpt: String,
tags: [String],
published: { type: Boolean, default: false },
featuredImage: String,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
});
export const Post = mongoose.models.Post || mongoose.model("Post", postSchema);
Enable SSR or hybrid mode in your Astro config:
// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
output: "hybrid",
adapter: node({ mode: "standalone" }),
});
Basic Usage
Query posts from MongoDB and render them. Using the native driver:
---
// src/pages/blog/index.astro
export const prerender = false;
import { getDb } from "../../lib/mongodb";
import BaseLayout from "../../layouts/BaseLayout.astro";
const db = await getDb();
const posts = await db
.collection("posts")
.find({ published: true })
.sort({ createdAt: -1 })
.limit(50)
.toArray();
---
<BaseLayout title="Blog">
<h1>Blog</h1>
{posts.map((post) => (
<article>
<a href={`/blog/${post.slug}`}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<time>{new Date(post.createdAt).toLocaleDateString()}</time>
</a>
</article>
))}
</BaseLayout>
Using Mongoose:
---
// src/pages/blog/index.astro
export const prerender = false;
import { connectDB } from "../../lib/mongoose";
import { Post } from "../../models/Post";
import BaseLayout from "../../layouts/BaseLayout.astro";
await connectDB();
const posts = await Post.find({ published: true })
.sort({ createdAt: -1 })
.limit(50)
.lean();
---
<BaseLayout title="Blog">
<h1>Blog</h1>
{posts.map((post) => (
<article>
<a href={`/blog/${post.slug}`}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</a>
</article>
))}
</BaseLayout>
API route for creating posts:
// src/pages/api/posts.ts
import type { APIRoute } from "astro";
import { getDb } from "../../lib/mongodb";
export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
const db = await getDb();
const result = await db.collection("posts").insertOne({
title: body.title,
slug: body.slug,
content: body.content,
excerpt: body.excerpt,
tags: body.tags || [],
published: body.published || false,
createdAt: new Date(),
updatedAt: new Date(),
});
return new Response(
JSON.stringify({ id: result.insertedId, success: true }),
{
status: 201,
headers: { "Content-Type": "application/json" },
}
);
};
Production Tips
Use Atlas Search for full-text search. Atlas includes a Lucene-based search engine. Create a search index on your collection and run
$searchaggregation queries. This eliminates the need for a separate search service like Algolia or Meilisearch.Enable connection pooling. The MongoDB driver handles connection pooling automatically, but configure
maxPoolSizein your connection options for production. A pool size of 10-20 is usually sufficient for SSR Astro sites.Add indexes for query performance. Create indexes on fields you query frequently (slug, published, createdAt). Without indexes, MongoDB scans every document. Use
db.collection.createIndex()or define them in Atlas's UI.Use Atlas's free tier wisely. The M0 free tier gives you 512MB with shared resources. It is fine for development and low-traffic sites. For production, M2 ($9/mo) or M5 ($25/mo) shared tiers give you more predictable performance.
Implement pagination. For large collections, use cursor-based pagination with
_idcomparisons instead of skip/limit. This stays performant regardless of how many documents you have.
Alternatives to Consider
- Supabase if you prefer a relational database (Postgres) with built-in auth and real-time features.
- Neon if you want serverless Postgres with branching and autoscaling.
- Firebase Firestore if you need real-time data sync and are building a more interactive application.
Wrapping Up
MongoDB Atlas and Astro work well together for content-driven applications, especially when your data has nested structures or evolves frequently. The flexible document model means you can add fields without migrations, and Atlas Search gives you full-text search without an additional service. The free tier is enough to get started and test your architecture, and the official driver is mature and well-maintained. For Astro sites that outgrow file-based content but need more flexibility than a traditional CMS, MongoDB Atlas is a practical choice.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.