Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions app/api/collisions/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { getCollisionsCollection } from "@/lib/mongodb";

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const limitParam = searchParams.get("limit");
const limit = Math.max(
1,
Math.min(Number.parseInt(limitParam ?? "100", 10) || 100, 1000)
);
const collection = await getCollisionsCollection("collisions_2025");
const data = await collection.find({}).limit(limit).toArray();

return NextResponse.json({ data });
}
Comment on lines +1 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix limit parsing (0 should not default to 100) + consider projection/error handling.
parseInt(...) || 100 treats 0 as invalid and returns 100. Also, consider projecting out _id (or mapping it) and returning a controlled error response on DB failures.

Proposed fix
 export async function GET(request: Request) {
   const { searchParams } = new URL(request.url);
   const limitParam = searchParams.get("limit");
-  const limit = Math.max(
-    1,
-    Math.min(Number.parseInt(limitParam ?? "100", 10) || 100, 1000)
-  );
-  const collection = await getCollisionsCollection("collisions_2025");
-  const data = await collection.find({}).limit(limit).toArray();
-
-  return NextResponse.json({ data });
+  const parsed = limitParam ? Number.parseInt(limitParam, 10) : NaN;
+  const limit = Math.max(1, Math.min(Number.isFinite(parsed) ? parsed : 100, 1000));
+
+  try {
+    const collection = await getCollisionsCollection();
+    const data = await collection
+      .find({}, { projection: { _id: 0 } })
+      .limit(limit)
+      .toArray();
+    return NextResponse.json({ data });
+  } catch (err) {
+    console.error("Failed to fetch collisions", err);
+    return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { getCollisionsCollection } from "@/lib/mongodb";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const limitParam = searchParams.get("limit");
const limit = Math.max(
1,
Math.min(Number.parseInt(limitParam ?? "100", 10) || 100, 1000)
);
const collection = await getCollisionsCollection("collisions_2025");
const data = await collection.find({}).limit(limit).toArray();
return NextResponse.json({ data });
}
export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { getCollisionsCollection } from "@/lib/mongodb";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const limitParam = searchParams.get("limit");
const parsed = limitParam ? Number.parseInt(limitParam, 10) : NaN;
const limit = Math.max(1, Math.min(Number.isFinite(parsed) ? parsed : 100, 1000));
try {
const collection = await getCollisionsCollection();
const data = await collection
.find({}, { projection: { _id: 0 } })
.limit(limit)
.toArray();
return NextResponse.json({ data });
} catch (err) {
console.error("Failed to fetch collisions", err);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
🤖 Prompt for AI Agents
In @app/api/collisions/route.ts around lines 1 - 16, The GET handler's limit
parsing uses `parseInt(...) || 100` which turns a valid 0 into 100 and hides
parse errors; change the logic in `GET` to parse `limitParam` with
`Number.parseInt(limitParam ?? "100", 10)`, detect NaN via
`Number.isNaN(parsed)` and only use the 100 default when parsing fails, then
clamp the parsed value with `Math.max(0, Math.min(parsed, 1000))` (use 0 as the
minimum if you want to allow zero); add a try/catch around the DB calls (the
`getCollisionsCollection`, `collection.find(...).limit(limit).toArray()`
sequence) and return a controlled NextResponse with status 500 on error; finally
add a projection on the find (e.g. `collection.find({}, { projection: { _id: 0 }
})`) or map results to remove/transform `_id` before returning.



23 changes: 23 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions lib/mongodb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { MongoClient } from "mongodb";
import type { CollisionRecord } from "@/types/collision";

function normalizeMongoUri(raw: string | undefined) {
console.log("raw", raw);
if (!raw) return "";
let value = raw.trim();
// Strip surrounding quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
// Remove accidental trailing semicolon
if (value.endsWith(";")) {
value = value.slice(0, -1);
}
// If there is stray punctuation right after the scheme (e.g., "mongodb+srv://;user")
// normalize it by removing non-alphanumeric chars immediately following "://"
value = value.replace(
/^(mongodb(?:\+srv)?:\/\/)\s*[^A-Za-z0-9]+/,
(_m, scheme) => `${scheme}`
);
return value;
}

const uri = process.env.MONGODB_URI;
console.log("uri", uri);
if (!uri) {
throw new Error("❌ MONGODB_URI is not defined");
}
console.log("uri.startsWith('mongodb://')", uri.startsWith("mongodb://"));
console.log("uri.startsWith('mongodb+srv://')", uri.startsWith("mongodb+srv://"));
if (!(uri.startsWith("mongodb://") || uri.startsWith("mongodb+srv://"))) {
throw new Error(
`❌ Invalid MONGODB_URI. It must start with "mongodb://" or "mongodb+srv://". Received prefix: ${uri.slice(
0,
20
)}...`
);
}
Comment on lines +4 to +42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the lib/mongodb.ts file
find . -name "mongodb.ts" -type f 2>/dev/null

Repository: Lemirq/cision

Length of output: 73


🏁 Script executed:

# Check the size and content of lib/mongodb.ts
if [ -f "lib/mongodb.ts" ]; then
  wc -l lib/mongodb.ts
fi

Repository: Lemirq/cision

Length of output: 74


🏁 Script executed:

# Read the content of lib/mongodb.ts to verify the code state
if [ -f "lib/mongodb.ts" ]; then
  cat -n lib/mongodb.ts
fi

Repository: Lemirq/cision

Length of output: 2720


🌐 Web query:

mongodb driver v7 ObjectId JSON serialization toJSON() Next.js Route Handlers

💡 Result:

Short answer: convert ObjectId values to plain strings before returning them from Next.js Route Handlers (Next.js disallows objects with custom toJSON methods). Use ObjectId.toString() (or String(id)) when mapping documents, or serialize with a replacer/EJSON on the server — don’t rely on the driver’s toJSON behavior across versions.

Why + references:

  • Next.js route handlers enforce JSON-serializable plain values and will error on objects with toJSON methods. [1]
  • The MongoDB ObjectId exposes a toString() method to get the hex string. Use that to convert IDs. [2]
  • Driver v7 ships with bson v7 (breaking changes possible), so behavior may differ vs older drivers — explicitly converting is safer. [3]

Minimal example for an app-route handler (Next.js):

  • Map documents before returning:
    const docs = await collection.find({}).toArray();
    const out = docs.map(d => ({ ...d, _id: d._id.toString() }));
    return new Response(JSON.stringify(out), { headers: { 'Content-Type': 'application/json' } });

Alternative approaches:

  • Return only plain fields (e.g., replace _id with id string).
  • Use a JSON replacer: JSON.stringify(doc, (k, v) => (v && v._bsontype === 'ObjectId' ? v.toString() : v));
  • Use server-side EJSON serialization if you need to preserve BSON types and then decode client-side (but this adds complexity).

If you want, tell me your Next.js / mongodb package versions and I’ll give an exact snippet for your setup.

Sources:
[1] Next.js serialization / "Objects with toJSON methods are not supported".
[2] MongoDB docs: ObjectId.toString().
[3] MongoDB Node.js driver v7 docs / release notes.


🏁 Script executed:

# Search for all uses of normalizeMongoUri throughout the repo
rg "normalizeMongoUri" --type ts --type js

Repository: Lemirq/cision

Length of output: 125


Remove secret-leaking logs and apply normalizeMongoUri() before validation/connection.

The code logs raw and uri (which expose credentials) and validates the un-normalized URI directly, so quoted/semicolon URIs will fail even though the normalization function exists and should handle them.

Proposed fix
 function normalizeMongoUri(raw: string | undefined) {
-  console.log("raw", raw);
   if (!raw) return "";
   let value = raw.trim();
   // Strip surrounding quotes if present
   if (
     (value.startsWith('"') && value.endsWith('"')) ||
     (value.startsWith("'") && value.endsWith("'"))
   ) {
     value = value.slice(1, -1);
   }
   // Remove accidental trailing semicolon
   if (value.endsWith(";")) {
     value = value.slice(0, -1);
   }
   // If there is stray punctuation right after the scheme (e.g., "mongodb+srv://;user")
   // normalize it by removing non-alphanumeric chars immediately following "://"
   value = value.replace(
     /^(mongodb(?:\+srv)?:\/\/)\s*[^A-Za-z0-9]+/,
     (_m, scheme) => `${scheme}`
   );
   return value;
 }

-const uri = process.env.MONGODB_URI;
-console.log("uri", uri);
+const uri = normalizeMongoUri(process.env.MONGODB_URI);
 if (!uri) {
-  throw new Error("❌ MONGODB_URI is not defined");
+  throw new Error("MONGODB_URI is not defined");
 }
-console.log("uri.startsWith('mongodb://')", uri.startsWith("mongodb://"));
-console.log("uri.startsWith('mongodb+srv://')", uri.startsWith("mongodb+srv://"));
 if (!(uri.startsWith("mongodb://") || uri.startsWith("mongodb+srv://"))) {
   throw new Error(
-    `❌ Invalid MONGODB_URI. It must start with "mongodb://" or "mongodb+srv://". Received prefix: ${uri.slice(
-      0,
-      20
-    )}...`
+    'Invalid MONGODB_URI. It must start with "mongodb://" or "mongodb+srv://".'
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function normalizeMongoUri(raw: string | undefined) {
console.log("raw", raw);
if (!raw) return "";
let value = raw.trim();
// Strip surrounding quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
// Remove accidental trailing semicolon
if (value.endsWith(";")) {
value = value.slice(0, -1);
}
// If there is stray punctuation right after the scheme (e.g., "mongodb+srv://;user")
// normalize it by removing non-alphanumeric chars immediately following "://"
value = value.replace(
/^(mongodb(?:\+srv)?:\/\/)\s*[^A-Za-z0-9]+/,
(_m, scheme) => `${scheme}`
);
return value;
}
const uri = process.env.MONGODB_URI;
console.log("uri", uri);
if (!uri) {
throw new Error("❌ MONGODB_URI is not defined");
}
console.log("uri.startsWith('mongodb://')", uri.startsWith("mongodb://"));
console.log("uri.startsWith('mongodb+srv://')", uri.startsWith("mongodb+srv://"));
if (!(uri.startsWith("mongodb://") || uri.startsWith("mongodb+srv://"))) {
throw new Error(
`❌ Invalid MONGODB_URI. It must start with "mongodb://" or "mongodb+srv://". Received prefix: ${uri.slice(
0,
20
)}...`
);
}
function normalizeMongoUri(raw: string | undefined) {
if (!raw) return "";
let value = raw.trim();
// Strip surrounding quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
// Remove accidental trailing semicolon
if (value.endsWith(";")) {
value = value.slice(0, -1);
}
// If there is stray punctuation right after the scheme (e.g., "mongodb+srv://;user")
// normalize it by removing non-alphanumeric chars immediately following "://"
value = value.replace(
/^(mongodb(?:\+srv)?:\/\/)\s*[^A-Za-z0-9]+/,
(_m, scheme) => `${scheme}`
);
return value;
}
const uri = normalizeMongoUri(process.env.MONGODB_URI);
if (!uri) {
throw new Error("MONGODB_URI is not defined");
}
if (!(uri.startsWith("mongodb://") || uri.startsWith("mongodb+srv://"))) {
throw new Error(
'Invalid MONGODB_URI. It must start with "mongodb://" or "mongodb+srv://".'
);
}
🤖 Prompt for AI Agents
In @lib/mongodb.ts around lines 4 - 42, Remove the console.log statements that
print the raw/uri values (they leak secrets), call normalizeMongoUri on
process.env.MONGODB_URI and use the returned value for all subsequent validation
and connection logic (replace uses of the raw uri variable with the normalized
value), and validate the normalized URI with the existing startsWith checks
(throw the same errors but referencing the normalized value or its prefix
snippet); ensure no sensitive values are logged anywhere in this module.


declare global {
// prevents multiple connections in dev (hot reload)
var _mongoClientPromise: Promise<MongoClient> | undefined;
}

let clientPromise: Promise<MongoClient>;

if (process.env.NODE_ENV === "development") {
if (!global._mongoClientPromise) {
const client = new MongoClient(uri);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
const client = new MongoClient(uri);
clientPromise = client.connect();
}

export async function getDb() {
const client = await clientPromise;
const dbName = process.env.MONGODB_DB;
return client.db(dbName);
}
Comment on lines +62 to +66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle missing MONGODB_DB explicitly (avoid accidentally using the wrong database).
client.db(dbName) with dbName === undefined can silently select an unintended default DB.

Proposed fix (pick one behavior)
 export async function getDb() {
   const client = await clientPromise;
-  const dbName = process.env.MONGODB_DB;
-  return client.db(dbName);
+  const dbName = process.env.MONGODB_DB?.trim();
+  // Option A (strict): require explicit DB name
+  // if (!dbName) throw new Error("MONGODB_DB is not defined");
+  // return client.db(dbName);
+
+  // Option B (flexible): fall back to default DB from URI
+  return dbName ? client.db(dbName) : client.db();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getDb() {
const client = await clientPromise;
const dbName = process.env.MONGODB_DB;
return client.db(dbName);
}
export async function getDb() {
const client = await clientPromise;
const dbName = process.env.MONGODB_DB?.trim();
// Option A (strict): require explicit DB name
// if (!dbName) throw new Error("MONGODB_DB is not defined");
// return client.db(dbName);
// Option B (flexible): fall back to default DB from URI
return dbName ? client.db(dbName) : client.db();
}
🤖 Prompt for AI Agents
In @lib/mongodb.ts around lines 62 - 66, getDb currently calls client.db(dbName)
even when process.env.MONGODB_DB is undefined, which can return an unintended
default DB; update getDb to explicitly validate that process.env.MONGODB_DB is
set (e.g., const dbName = process.env.MONGODB_DB) and throw a clear error
including the env var name when it's missing before calling client.db(dbName);
reference the getDb function and clientPromise so the change is made in that
function only.


export async function getCollisionsCollection(collectionName = "collisions_2025") {

const db = await getDb();
return db.collection<CollisionRecord>(collectionName);
}
140 changes: 139 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading