-
Notifications
You must be signed in to change notification settings - Fork 18
feat: add Express + MongoDB boilerplate to community projects and doc⦠#115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add Express + MongoDB boilerplate to community projects and doc⦠#115
Conversation
WalkthroughAdds an Express + MongoDB boilerplate guide, inserts it into Developer Resources navigation, and adds a corresponding card under Community β Boilerplates & Starters. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Client
participant API as Express API
participant Dodo as Dodo Payments
participant DB as MongoDB
rect rgba(200,235,255,0.25)
note over API: Create Payment flow
Dev->>API: POST /api/payments {amount, currency, ...}
API->>Dodo: createPayment(payload)
Dodo-->>API: payment {id, status, ...}
API->>DB: upsert Payment document
API-->>Dev: 201 Created {payment}
end
sequenceDiagram
autonumber
actor Dev as Client
participant API as Express API
participant Dodo as Dodo Payments
participant DB as MongoDB
rect rgba(220,255,220,0.25)
note over API: Create Subscription flow
Dev->>API: POST /api/subscriptions {planId, customer, ...}
API->>Dodo: createSubscription(payload)
Dodo-->>API: subscription {id, status, ...}
API->>DB: upsert Subscription document
API-->>Dev: 201 Created {subscription}
end
sequenceDiagram
autonumber
participant Dodo as Dodo Payments
participant API as Express Webhook Endpoint
participant DB as MongoDB
rect rgba(255,245,200,0.35)
note over API: Webhook verification using raw body
Dodo->>API: POST /webhooks (signature header + raw body)
API->>API: verifySignature(rawBody, header)
alt signature valid
API->>DB: upsert/update by event type (subscription.active, on_hold, payment.succeeded, payment.failed)
API-->>Dodo: 200 OK
else invalid
API-->>Dodo: 400/401 Error
end
end
Estimated code review effortπ― 2 (Simple) | β±οΈ ~10 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touchesβ Passed checks (5 passed)
β¨ Finishing touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (3)
community/projects.mdx
(1 hunks)developer-resources/express-mongodb-boilerplate.mdx
(1 hunks)docs.json
(1 hunks)
π§° Additional context used
π Path-based instructions (2)
**/*.{md,mdx}
π CodeRabbit inference engine (.cursor/rules/mintlify.mdc)
Every documentation page must begin with YAML frontmatter containing title and description
Files:
community/projects.mdx
developer-resources/express-mongodb-boilerplate.mdx
**/*.mdx
π CodeRabbit inference engine (.cursor/rules/mintlify.mdc)
**/*.mdx
: Use clear, direct language appropriate for technical audiences
Write instructions in second person (you)
Use active voice over passive voice
Use present tense for current states and future tense for outcomes
Maintain consistent terminology across documentation
Keep sentences concise while preserving necessary context
Use parallel structure in lists, headings, and procedures
Lead sections with the most important information (inverted pyramid)
Use progressive disclosure: introduce basics before advanced topics
Break complex procedures into numbered steps using /
Include prerequisites and context before instructions
Provide expected outcomes for each major step
End sections with next steps or related information
Use descriptive, keyword-rich headings for navigation and SEO
Focus on user goals and outcomes rather than system features
Anticipate common questions and address them proactively
Include troubleshooting for likely failure points
Provide multiple pathways (beginner vs advanced) when appropriate, with an opinionated recommended path
Use for supplementary information that supports main content
Use for expert advice, shortcuts, or best practices
Use for critical cautions, breaking changes, or destructive actions
Use for neutral background or contextual information
Use for success confirmations or achievement indicators
Use to present the same concept in multiple languages
Provide complete, runnable code examples with language specified and filename when relevant
Include RequestExample/ResponseExample blocks for API endpoint docs
Document API parameters using (path, query, header, body) with types and required/defaults
Document API responses using with names, types, and required flags
Use for nested object properties or hierarchical information
Use / for platform-specific or alternative approaches
Use / for suppl...
Files:
community/projects.mdx
developer-resources/express-mongodb-boilerplate.mdx
π Additional comments (2)
community/projects.mdx (1)
52-56
: Card addition looks solid.Clear positioning, concise pitch, and consistent tag list. Nice addition.
docs.json (1)
126-126
: Navigation entry added in the right slot.Keeps the Integration Guides grouping coherent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
β»οΈ Duplicate comments (2)
developer-resources/express-mongodb-boilerplate.mdx (2)
58-71
: esModuleInterop guidance added β resolves prior blocker.The Tip addresses the earlier default-import compilation issue and clearly sets required tsconfig flags. Good.
171-177
: Fix noβesModuleInterop example: default import of SDK will fail.Without esModuleInterop,
import DodoPayments from 'dodopayments'
can break. Use TSimport = require
to instantiate safely.Apply this diff:
-// src/routes/payments.ts (no esModuleInterop) -import * as express from 'express'; -import DodoPayments from 'dodopayments'; +// src/routes/payments.ts (no esModuleInterop) +import * as express from 'express'; +import DodoPayments = require('dodopayments'); const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY }); const router = express.Router();
π§Ή Nitpick comments (8)
developer-resources/express-mongodb-boilerplate.mdx (8)
290-297
: Add idempotency (dedup) for webhook events.Retries can re-deliver the same event; current handler will reprocess. Dedup by webhook-id with a unique collection.
Apply these diffs to the snippets:
- models.ts: add ProcessedEvent model with unique _id
// src/db/models.ts import mongoose from 'mongoose'; +const { Schema } = mongoose; @@ export const Payment = mongoose.model('Payment', PaymentSchema); export const Subscription = mongoose.model('Subscription', SubscriptionSchema); +const ProcessedEventSchema = new Schema( + { _id: { type: String, required: true } }, + { timestamps: true } +); +ProcessedEventSchema.index({ _id: 1 }, { unique: true }); +export const ProcessedEvent = mongoose.model('ProcessedEvent', ProcessedEventSchema);
- webhooks.ts: import and short-circuit on duplicates
-import { Subscription, Payment } from '../db/models'; +import { Subscription, Payment, ProcessedEvent } from '../db/models'; @@ - await webhook.verify(rawBody, headers); + await webhook.verify(rawBody, headers); + // Idempotency: store event id; ignore if already processed + try { + await ProcessedEvent.create({ _id: headers['webhook-id'] }); + } catch (e: any) { + if (e?.code === 11000) { + return res.json({ received: true }); + } + throw e; + }Also applies to: 299-315, 365-373
192-227
: Return 500 for server errors; reserve 400 for bad input.Currently all exceptions map to 400. Differentiate validation vs server errors.
Apply this diff in both routes:
router.post('/', async (req, res) => { - try { + try { const { @@ - if (!billing || !customer || !product_cart) { - return res.status(400).json({ error: 'billing, customer, and product_cart are required' }); + if (!billing || !customer || !product_cart) { + return res.status(400).json({ error: 'billing, customer, and product_cart are required' }); } @@ - res.json(payment); - } catch (err: any) { - res.status(400).json({ error: err.message }); + res.json(payment); + } catch (err: any) { + const status = err?.name === 'ValidationError' ? 400 : 500; + res.status(status).json({ error: err.message ?? 'Internal Server Error' }); }Repeat analogous change for subscriptions route (validation 400; others 500).
Also applies to: 242-279
98-121
: Tighten Mongoose schema types and indexes.Use Mixed/Map for metadata, and make ids unique for faster upserts and integrity.
Apply this diff:
-const CustomerSchema = new mongoose.Schema({ - customerId: { type: String, index: true }, +const CustomerSchema = new mongoose.Schema({ + customerId: { type: String, index: true, unique: true }, email: String, name: String, }); -const PaymentSchema = new mongoose.Schema({ - paymentId: { type: String, index: true }, +const PaymentSchema = new mongoose.Schema({ + paymentId: { type: String, index: true, unique: true }, status: String, amount: Number, currency: String, customerId: String, - metadata: {}, + metadata: { type: mongoose.Schema.Types.Mixed, default: {} }, }); -const SubscriptionSchema = new mongoose.Schema({ - subscriptionId: { type: String, index: true }, +const SubscriptionSchema = new mongoose.Schema({ + subscriptionId: { type: String, index: true, unique: true }, status: String, productId: String, customerId: String, currentPeriodEnd: Date, - metadata: {}, + metadata: { type: mongoose.Schema.Types.Mixed, default: {} }, });
334-338
: Consider upsert for stateβonly updates.If an on_hold/failed event arrives before initial creation, the update will noβop. Upsert ensures record presence.
Apply this diff:
await Subscription.updateOne( { subscriptionId: data.subscription_id }, - { status: 'on_hold' } + { status: 'on_hold' }, + { upsert: true } ); @@ await Payment.updateOne( { paymentId: p.payment_id }, - { status: 'failed' } + { status: 'failed' }, + { upsert: true } );Also applies to: 359-362
49-56
: Use Steps and include a run/test step.Make the Quickstart executable endβtoβend with a start command and verification.
Apply this diff:
-## Quickstart - -```bash -npm init -y -npm install express mongoose cors dotenv dodopayments standardwebhooks -npm install -D typescript ts-node @types/express @types/node @types/cors -npx tsc --init -``` +## Quickstart + +<Steps> + <Step title="Initialize project"> + ```bash + npm init -y + npm install express mongoose cors dotenv dodopayments standardwebhooks + npm install -D typescript ts-node @types/express @types/node @types/cors + npx tsc --init + ``` + </Step> + <Step title="Add a start script"> + ```json filename="package.json" + { + "scripts": { + "dev": "ts-node src/app.ts" + } + } + ``` + </Step> + <Step title="Run and verify"> + ```bash + npm run dev + # Expect: "Server listening on :3000" + ``` + </Step> +</Steps>
73-80
: Use realistic env examples and warn against committing secrets.Prefer a local Mongo URI in examples and add a secrets warning.
Apply this diff:
-```bash -DODO_PAYMENTS_API_KEY=sk_test_xxx -DODO_WEBHOOK_SECRET=whsec_xxx -MONGODB_URI=mongodb+srv://<user>:<pass>@<cluster>/dodo?retryWrites=true&w=majority -PORT=3000 -``` +```bash +DODO_PAYMENTS_API_KEY=sk_test_1234567890 +DODO_WEBHOOK_SECRET=whsec_1234567890 +# Local dev +MONGODB_URI=mongodb://127.0.0.1:27017/dodo +PORT=3000 +``` + +<Warning> +Never commit .env files or secrets to source control. Use environment variables in production. +</Warning>
88-91
: Handle connecting state and surface connection errors.Avoid duplicate connects and add basic error handling.
Apply this diff:
export async function connectDB(uri: string) { - if (mongoose.connection.readyState === 1) return; - await mongoose.connect(uri, { dbName: 'dodo' }); + if (mongoose.connection.readyState === 1 || mongoose.connection.readyState === 2) return; + try { + await mongoose.connect(uri, { dbName: 'dodo' }); + } catch (err) { + console.error('Mongo connection error', err); + throw err; + } }
379-381
: Reinforce rawβbody constraint with a code hint.Add a minimal snippet showing pathβscoped raw as an alternative to routerβlevel.
Apply this diff:
<Warning> Ensure your Express app does not use `express.json()` on the webhook route, as it must read the raw body for signature verification. +```ts +// Alternative: path-scoped raw +app.post('/webhooks/dodo', raw({ type: '*/*' }), webhooksRouter); +``` </Warning>
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (1)
developer-resources/express-mongodb-boilerplate.mdx
(1 hunks)
π§° Additional context used
π Path-based instructions (2)
**/*.{md,mdx}
π CodeRabbit inference engine (.cursor/rules/mintlify.mdc)
Every documentation page must begin with YAML frontmatter containing title and description
Files:
developer-resources/express-mongodb-boilerplate.mdx
**/*.mdx
π CodeRabbit inference engine (.cursor/rules/mintlify.mdc)
**/*.mdx
: Use clear, direct language appropriate for technical audiences
Write instructions in second person (you)
Use active voice over passive voice
Use present tense for current states and future tense for outcomes
Maintain consistent terminology across documentation
Keep sentences concise while preserving necessary context
Use parallel structure in lists, headings, and procedures
Lead sections with the most important information (inverted pyramid)
Use progressive disclosure: introduce basics before advanced topics
Break complex procedures into numbered steps using /
Include prerequisites and context before instructions
Provide expected outcomes for each major step
End sections with next steps or related information
Use descriptive, keyword-rich headings for navigation and SEO
Focus on user goals and outcomes rather than system features
Anticipate common questions and address them proactively
Include troubleshooting for likely failure points
Provide multiple pathways (beginner vs advanced) when appropriate, with an opinionated recommended path
Use for supplementary information that supports main content
Use for expert advice, shortcuts, or best practices
Use for critical cautions, breaking changes, or destructive actions
Use for neutral background or contextual information
Use for success confirmations or achievement indicators
Use to present the same concept in multiple languages
Provide complete, runnable code examples with language specified and filename when relevant
Include RequestExample/ResponseExample blocks for API endpoint docs
Document API parameters using (path, query, header, body) with types and required/defaults
Document API responses using with names, types, and required flags
Use for nested object properties or hierarchical information
Use / for platform-specific or alternative approaches
Use / for suppl...
Files:
developer-resources/express-mongodb-boilerplate.mdx
```ts | ||
// src/routes/webhooks.ts | ||
import { Router } from 'express'; | ||
import { raw } from 'express'; | ||
import { Subscription, Payment } from '../db/models'; | ||
import { Webhook, type WebhookUnbrandedRequiredHeaders } from 'standardwebhooks'; | ||
|
||
const router = Router(); | ||
|
||
// Use Standard Webhooks with your secret | ||
const webhook = new Webhook(process.env.DODO_WEBHOOK_SECRET as string); | ||
|
||
// Use raw body for signature verification (must not be pre-parsed by express.json()) | ||
router.post('/', raw({ type: 'application/json' }), async (req, res) => { | ||
try { | ||
const rawBody = req.body.toString('utf8'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use raw('/') for robust signature verification.
Webhook providers may send content-types like application/webhook+json or include charset; raw({ type: 'application/json' })
can miss these.
Apply this diff:
-// Use raw body for signature verification (must not be pre-parsed by express.json())
-router.post('/', raw({ type: 'application/json' }), async (req, res) => {
+// Use raw body for signature verification (must not be pre-parsed by express.json())
+router.post('/', raw({ type: '*/*' }), async (req, res) => {
π 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.
```ts | |
// src/routes/webhooks.ts | |
import { Router } from 'express'; | |
import { raw } from 'express'; | |
import { Subscription, Payment } from '../db/models'; | |
import { Webhook, type WebhookUnbrandedRequiredHeaders } from 'standardwebhooks'; | |
const router = Router(); | |
// Use Standard Webhooks with your secret | |
const webhook = new Webhook(process.env.DODO_WEBHOOK_SECRET as string); | |
// Use raw body for signature verification (must not be pre-parsed by express.json()) | |
router.post('/', raw({ type: 'application/json' }), async (req, res) => { | |
try { | |
const rawBody = req.body.toString('utf8'); | |
// src/routes/webhooks.ts | |
import { Router } from 'express'; | |
import { raw } from 'express'; | |
import { Subscription, Payment } from '../db/models'; | |
import { Webhook, type WebhookUnbrandedRequiredHeaders } from 'standardwebhooks'; | |
const router = Router(); | |
// Use Standard Webhooks with your secret | |
const webhook = new Webhook(process.env.DODO_WEBHOOK_SECRET as string); | |
// Use raw body for signature verification (must not be pre-parsed by express.json()) | |
router.post('/', raw({ type: '*/*' }), async (req, res) => { | |
try { | |
const rawBody = req.body.toString('utf8'); |
π€ Prompt for AI Agents
In developer-resources/express-mongodb-boilerplate.mdx around lines 286 to 301,
the webhook route uses raw({ type: 'application/json' }) which can miss webhook
requests that use non-standard content-types (e.g., application/webhook+json or
include a charset); update the middleware to use raw({ type: '*/*' }) so the raw
body is preserved for all content-types (or an equivalent predicate that returns
true), keeping signature verification reliable, and ensure the rest of the
handler continues to call req.body.toString('utf8') as before.
π Description
Add Express + MongoDB boilerplate guide with webhook handling and Mongo persistence. Link it under Integration Guides and Community β Boilerplates.
π― Type of Change
π What documentation does this PR affect?
β Checklist
mintlify dev
docs.json
(if applicable)π§ͺ Testing
πΈ Screenshots (if applicable)
N/A
π Related Issues
Closes #99
π Additional Notes
Includes
dodopayments-webhooks
usage, raw body verification, Mongo models, and routes.Summary by CodeRabbit