From 9b6d1f2cf58d269232fb3074844e3ccf9714fbe4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 19:09:15 +0000 Subject: [PATCH 1/3] chore(chat-agent): set up dev app for Vercel deployment with Atlas pooling Configures the chat-agent dev app to deploy to Vercel with MongoDB Atlas and preview deployments for PRs. Wires @vercel/functions attachDatabasePool into the mongoose adapter so Fluid Compute can drain idle connections between invocations, guarded by VERCEL so local tests/dev aren't affected. --- chat-agent/dev/.env.example | 13 +++++++++++++ chat-agent/dev/package.json | 1 + chat-agent/dev/src/payload.config.ts | 21 +++++++++++++++++++++ chat-agent/dev/vercel.json | 12 ++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 chat-agent/dev/.env.example create mode 100644 chat-agent/dev/vercel.json diff --git a/chat-agent/dev/.env.example b/chat-agent/dev/.env.example new file mode 100644 index 00000000..f914770b --- /dev/null +++ b/chat-agent/dev/.env.example @@ -0,0 +1,13 @@ +# MongoDB connection string. Local dev defaults to +# mongodb://localhost:27017/chat-agent-dev when unset. For Vercel deployments +# point this at a MongoDB Atlas cluster (SRV URI). +DATABASE_URI=mongodb://127.0.0.1/chat-agent-dev + +# Secret used to sign Payload tokens. Generate with `openssl rand -hex 32`. +PAYLOAD_SECRET=YOUR_SECRET_HERE + +# Provider keys for the chat agent. Only the providers you actually use need +# to be set — resolveModel in payload.config.ts will throw a clear error if a +# selected model references a missing key. +ANTHROPIC_API_KEY= +OPENAI_API_KEY= diff --git a/chat-agent/dev/package.json b/chat-agent/dev/package.json index e98b02a2..797381d8 100644 --- a/chat-agent/dev/package.json +++ b/chat-agent/dev/package.json @@ -23,6 +23,7 @@ "@payloadcms/next": "^3.83.0", "@payloadcms/richtext-lexical": "^3.83.0", "@payloadcms/ui": "^3.83.0", + "@vercel/functions": "^3.1.5", "ai": "^6.0.164", "next": "15.4.11", "payload": "^3.83.0", diff --git a/chat-agent/dev/src/payload.config.ts b/chat-agent/dev/src/payload.config.ts index 43e44a10..ffa4ae85 100644 --- a/chat-agent/dev/src/payload.config.ts +++ b/chat-agent/dev/src/payload.config.ts @@ -3,6 +3,7 @@ import { createOpenAI } from '@ai-sdk/openai' import { chatAgentPlugin, createPayloadBudget } from '@jhb.software/payload-chat-agent' import { mongooseAdapter } from '@payloadcms/db-mongodb' import { lexicalEditor } from '@payloadcms/richtext-lexical' +import { attachDatabasePool } from '@vercel/functions' import path from 'path' import { buildConfig } from 'payload' import { fileURLToPath } from 'url' @@ -114,6 +115,12 @@ export default buildConfig({ ], db: mongooseAdapter({ url: process.env.DATABASE_URI || 'mongodb://localhost:27017/chat-agent-dev', + connectOptions: { + // Close sockets quickly so Fluid Compute can drain the pool between + // invocations instead of keeping idle Atlas connections open. + maxIdleTimeMS: 5_000, + maxPoolSize: 10, + }, }), editor: lexicalEditor(), endpoints: rootEndpoints, @@ -122,6 +129,20 @@ export default buildConfig({ outputFile: path.resolve(__dirname, '../payload-types.ts'), }, async onInit(payload) { + // On Vercel Fluid Compute, hand the underlying MongoClient to + // @vercel/functions so idle connections are released when the function + // suspends. Guarded by VERCEL so local dev/tests aren't affected. + if (process.env.VERCEL) { + const client = ( + payload.db as unknown as { + connection?: { getClient?: () => unknown } + } + ).connection?.getClient?.() + if (client) { + attachDatabasePool(client as Parameters[0]) + } + } + const existingUsers = await payload.find({ collection: 'users', limit: 1, diff --git a/chat-agent/dev/vercel.json b/chat-agent/dev/vercel.json new file mode 100644 index 00000000..8f65d6c1 --- /dev/null +++ b/chat-agent/dev/vercel.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "nextjs", + "installCommand": "cd .. && pnpm install --frozen-lockfile", + "buildCommand": "pnpm build", + "outputDirectory": ".next", + "functions": { + "src/app/(payload)/api/[...slug]/route.ts": { + "maxDuration": 300 + } + } +} From 601f2e2e28890723a09bb6c5f50cc629e0cfed9f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 19:14:13 +0000 Subject: [PATCH 2/3] refactor(chat-agent): attach Vercel pool via afterOpenConnection hook Use the native afterOpenConnection adapter hook (added in @payloadcms/db-mongodb 3.65.0) instead of reaching into payload.db from onInit. The typed MongooseAdapter is passed in, so no unsafe casts. --- chat-agent/dev/src/payload.config.ts | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/chat-agent/dev/src/payload.config.ts b/chat-agent/dev/src/payload.config.ts index ffa4ae85..ebfa9efb 100644 --- a/chat-agent/dev/src/payload.config.ts +++ b/chat-agent/dev/src/payload.config.ts @@ -121,6 +121,14 @@ export default buildConfig({ maxIdleTimeMS: 5_000, maxPoolSize: 10, }, + // On Vercel Fluid Compute, hand the underlying MongoClient to + // @vercel/functions so idle connections are released when the function + // suspends. Guarded by VERCEL so local dev/tests aren't affected. + afterOpenConnection: (adapter) => { + if (process.env.VERCEL) { + attachDatabasePool(adapter.connection.getClient()) + } + }, }), editor: lexicalEditor(), endpoints: rootEndpoints, @@ -129,20 +137,6 @@ export default buildConfig({ outputFile: path.resolve(__dirname, '../payload-types.ts'), }, async onInit(payload) { - // On Vercel Fluid Compute, hand the underlying MongoClient to - // @vercel/functions so idle connections are released when the function - // suspends. Guarded by VERCEL so local dev/tests aren't affected. - if (process.env.VERCEL) { - const client = ( - payload.db as unknown as { - connection?: { getClient?: () => unknown } - } - ).connection?.getClient?.() - if (client) { - attachDatabasePool(client as Parameters[0]) - } - } - const existingUsers = await payload.find({ collection: 'users', limit: 1, From 66366caf4b2f320cdd5f5cd677f8fecb20c1ce3e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 23:10:22 +0000 Subject: [PATCH 3/3] chore(chat-agent): update pnpm-lock for @vercel/functions --- chat-agent/pnpm-lock.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/chat-agent/pnpm-lock.yaml b/chat-agent/pnpm-lock.yaml index 08ccc856..ce53cea9 100644 --- a/chat-agent/pnpm-lock.yaml +++ b/chat-agent/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@payloadcms/ui': specifier: ^3.83.0 version: 3.83.0(@types/react@19.2.14)(monaco-editor@0.55.1)(next@15.4.11(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.77.4))(payload@3.83.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + '@vercel/functions': + specifier: ^3.1.5 + version: 3.4.3 ai: specifier: ^6.0.164 version: 6.0.164(zod@4.3.6) @@ -1648,10 +1651,23 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vercel/functions@3.4.3': + resolution: {integrity: sha512-kA14KIUVgAY6VXbhZ5jjY+s0883cV3cZqIU3WhrSRxuJ9KvxatMjtmzl0K23HK59oOUjYl7HaE/eYMmhmqpZzw==} + engines: {node: '>= 20'} + peerDependencies: + '@aws-sdk/credential-provider-web-identity': '*' + peerDependenciesMeta: + '@aws-sdk/credential-provider-web-identity': + optional: true + '@vercel/oidc@3.1.0': resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + '@vitest/expect@4.1.4': resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} @@ -6239,8 +6255,14 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@vercel/functions@3.4.3': + dependencies: + '@vercel/oidc': 3.2.0 + '@vercel/oidc@3.1.0': {} + '@vercel/oidc@3.2.0': {} + '@vitest/expect@4.1.4': dependencies: '@standard-schema/spec': 1.1.0