To secure a Bolt.new + MongoDB stack, start by validating every query input. In Express a query parameter can arrive as an object rather than a string, and that single fact is what makes NoSQL injection work. Then move connection strings into environment variables, write the authorization checks yourself since MongoDB has no row-level security to lean on, and lock down network access to the instance. Document databases fail differently from SQL ones, and this blueprint is about those differences.
TL;DR
Bolt-generated MongoDB apps tend to ship with two holes: NoSQL injection, and no authorization checks at all. Validate every query input, remembering that user input can arrive as an object and not just a string. Keep connection strings in environment variables and out of anything the browser downloads. Write the authorization layer yourself. MongoDB won't do it for you.
MongoDB Security with Bolt.new
MongoDB doesn't fail the way Postgres does, so the habits don't carry over. Here's what Bolt tends to generate, and what it costs you:
| Common Bolt Pattern | Security Issue | Fix |
|---|---|---|
| Direct user input in queries | NoSQL injection | Input validation with Zod/Joi |
| Connection string in code | Credential exposure | Environment variables |
| No authorization checks | Data leakage | App-level auth middleware |
| Find without filters | Full collection exposure | Always scope to user |
Part 1: Preventing MongoDB NoSQL Injection
The Problem
Bolt hands user input straight to the query more often than not:
// Bolt might generate this
app.get('/api/user', async (req, res) => {
const user = await db.collection('users').findOne({
username: req.query.username // Can be an object!
});
res.json(user);
});
// Attack: ?username[$ne]=null returns first user
// Attack: ?username[$gt]= returns users alphabetically
The Fix
import { z } from 'zod';
const usernameSchema = z.string().min(1).max(50);
app.get('/api/user', async (req, res) => {
// Validate input is a string
const result = usernameSchema.safeParse(req.query.username);
if (!result.success) {
return res.status(400).json({ error: 'Invalid username' });
}
const user = await db.collection('users').findOne({
username: result.data // Guaranteed string
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
// Remove sensitive fields
const { password, ...safeUser } = user;
res.json(safeUser);
});
Critical: in Express and Node, a query parameter written as ?key[$operator]=value arrives as an object, not a string. Your code reads it as a value. MongoDB reads it as an operator. Check the type before it reaches a query.
Part 2: MongoDB Connection String Security
Check for Exposed Credentials
# Look for hardcoded connection strings
grep -r "mongodb" . --include="*.ts" --include="*.js"
grep -r "mongodb+srv" .
# Should find only:
# process.env.MONGODB_URI or similar
Proper Configuration
import { MongoClient } from 'mongodb';
const uri = process.env.MONGODB_URI;
if (!uri) {
throw new Error('MONGODB_URI environment variable not set');
}
const client = new MongoClient(uri);
let db: Db | null = null;
export async function getDb() {
if (!db) {
await client.connect();
db = client.db(); // Uses database from connection string
}
return db;
}
Part 3: Authorization Middleware
MongoDB has no row-level security. There's no policy layer to fall back on if a query forgets to scope itself, so every check lives in your application code:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export interface AuthRequest extends Request {
user?: { id: string; email: string };
}
export function requireAuth(
req: AuthRequest,
res: Response,
next: NextFunction
) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
req.user = decoded as { id: string; email: string };
next();
} catch {
return res.status(401).json({ error: 'Invalid token' });
}
}
Using Auth in Routes
import { Router } from 'express';
import { ObjectId } from 'mongodb';
import { requireAuth, AuthRequest } from '../middleware/auth';
import { getDb } from '../lib/mongodb';
const router = Router();
// Get user's own posts only
router.get('/', requireAuth, async (req: AuthRequest, res) => {
const db = await getDb();
const posts = await db.collection('posts')
.find({ userId: req.user!.id })
.toArray();
res.json(posts);
});
// Update with ownership check
router.put('/:id', requireAuth, async (req: AuthRequest, res) => {
const db = await getDb();
const post = await db.collection('posts').findOne({
_id: new ObjectId(req.params.id)
});
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
if (post.userId !== req.user!.id) {
return res.status(403).json({ error: 'Not authorized' });
}
await db.collection('posts').updateOne(
{ _id: new ObjectId(req.params.id) },
{ $set: { title: req.body.title, content: req.body.content } }
);
res.json({ success: true });
});
export default router;
Security Checklist
Post-Export Checklist for Bolt + MongoDB
No hardcoded connection strings
MONGODB_URI in environment variables
Input validation on all query parameters
Authorization middleware on protected routes
Queries scoped to authenticated user
Sensitive fields excluded from responses
MongoDB user has minimal permissions
Network access restricted (Atlas/cloud)
Alternative Stacks to Consider
**Bolt.new + Supabase**
PostgreSQL with built-in RLS protection
**Bolt.new + PlanetScale**
MySQL with branching workflows
**Bolt.new + Convex**
TypeScript-native database alternative
What's NoSQL injection?
SQL injection smuggles code in as a string. NoSQL injection doesn't have to. MongoDB's query language is made of objects, so an attacker who can send an object like {$ne: null} where you expected a username is rewriting your query's logic rather than escaping out of it.
Should I use Mongoose with Bolt-generated code?
It helps. Mongoose's schema validation catches the object-where-a-string-belongs case for free. If Bolt gave you native driver code instead, you don't have to migrate to get that. Zod at the route boundary does the same job.
How do I restrict MongoDB network access?
In MongoDB Atlas, open Network Access and delete the 0.0.0.0/0 entry, which allows the entire internet. Add your deployment server's IP addresses and nothing else. Then a leaked connection string is worth much less, because whoever has it still can't reach the database.
Exported a Bolt + MongoDB app?
Scan for NoSQL injection and authorization issues.