Copy of Blog - Feature

Key Takeaways

  • Edge runtimes break TCP pools. TiDB Cloud Starter connects over HTTPS instead.
  • Swapping a v0.dev starter database for TiDB is a connection string change.
  • A native VECTOR type keeps embeddings beside your rows, with no store to sync.
  • The Vercel Marketplace integration writes the connection variables for you.

You have an AI app running on Vercel, or a prototype that v0.dev generated in a few minutes, and now it needs a real database. The choice is harder than it looks, because serverless functions and edge runtimes break the assumptions traditional databases are built on: long-lived TCP connections, a bounded pool, and a process that stays warm between requests.

This playbook covers the full path. Pick a Vercel database that survives serverless and edge runtimes, model the data an AI app actually stores, put embeddings next to that data instead of in a separate vector store, connect from Vercel Edge Functions and Cloudflare Workers, and deploy through the Vercel Marketplace integration that wires the environment variables for you.

TiDB is an open source distributed SQL database with transactional and analytical processing in one engine. TiDB Cloud Starter is its fully managed, auto-scaling deployment option, MySQL compatible and provisioned in about a second. Vercel is the creator of Next.js and the platform most AI apps deploy to. The two fit together well, and the rest of this post shows exactly how.

Why a Vercel App Needs a Serverless Database Built for the Edge

Vercel runs your backend as serverless and edge functions rather than a long-running server, and that changes what a database has to support. A serverless function executes on demand, scales automatically, and terminates when the request finishes. A traditional API keeps a process alive behind a request-response interface, holds its connection pool open, and reuses connections across requests.

Three differences matter when you pick a database:

  • Lifecycle. A serverless function runs a specific piece of code in response to an event and exits. A traditional API stays resident and manages its own infrastructure.
  • Portability. Serverless functions bind to a platform runtime. A traditional API runs anywhere you can host a process.
  • Deployment speed. Serverless functions ship in shorter cycles because there is no infrastructure to provision, which is why AI apps iterate on them.

Connection Pooling Is the Problem Serverless Exposes

Each function invocation opens its own database connection. Under bursty traffic, a few hundred concurrent invocations become a few hundred connections, and a traditional MySQL or Postgres instance starts refusing them. Teams usually respond by adding an external pooler, which adds a hop, a component to operate, and a new failure mode.

TiDB Cloud Starter takes a different path with the TiDB Cloud serverless driver, which talks to the database over HTTPS instead of TCP. There is no pool to exhaust because there is no persistent connection in the path. Each invocation makes an HTTPS request, gets its result, and ends.

Runtimes This Playbook Covers

Everything below works on three targets: Vercel serverless functions (Node.js runtime), Vercel Edge Functions, and Cloudflare Workers. Edge runtimes are the strict case, because they do not allow raw TCP sockets at all. A driver that speaks HTTPS is not an optimization there. It is the only thing that connects.

From a v0.dev or Lovable Prototype to a Deployed Vercel App

AI builders like v0.dev and Lovable scaffold a working Next.js app in minutes, then hand you a project that expects a database URL and ships with a generic Postgres or SQLite starter. Swapping that starter for TiDB Cloud Starter takes three steps: prototype, connect, deploy.

Prototype. Generate the app as usual. The output is a standard Next.js project with an API layer, a data access file, and an .env or .env.local expecting DATABASE_URL.

Connect. Find where the generated app reads that variable. In a Prisma project it is the datasource block in prisma/schema.prisma. In a Drizzle or Kysely project it is the client initialization file. Point it at a TiDB Cloud Starter connection string:

DATABASE_URL='mysql://<user>:<password>@<host>:4000/<database>?sslaccept=strict'

If the scaffold assumed Postgres, change the provider to mysql and regenerate the client. TiDB speaks the MySQL wire protocol, so any MySQL driver, ORM, or migration tool works without modification.

Deploy. Push to GitHub and import the repo into Vercel, or use the Marketplace integration covered later in this post, which sets the connection variables during deployment.

The reason to make the swap at the prototype stage rather than later: the starter database that ships with a generated app is sized for a demo. TiDB Cloud Starter scales horizontally on the same MySQL-compatible interface, so the prototype and the production system run the same code against the same engine.

Spin Up Your TiDB Cloud Starter Cluster (Your Vercel Database)

Provisioning the database takes under a minute. Sign in to TiDB Cloud, follow the on-screen instructions to create a free TiDB Cloud Starter cluster, then click the cluster name to open it.

Choose Your Entry Point: Cloud, Starter, or Zero

Three on-ramps exist, and the right one depends on what you are building.

  • TiDB Cloud Zero provisions an ephemeral instance through a single API call with no sign-up and no billing details. Instances expire after 30 days unless you claim them, and claiming converts one into a persistent TiDB Cloud Starter instance with the data and schema migrated automatically. Zero is built for agent-driven workflows, demos, and CI, and it is currently in public preview at zero.tidbcloud.com.
  • TiDB Cloud Starter is the free, fully managed serverless tier inside TiDB Cloud. This is the default choice for a Vercel app that needs to persist real data.
  • TiDB Cloud is the full platform. Starter sits inside it, and Essential and Dedicated add capacity, isolation, and enterprise controls as an app grows.

Start on Zero if an agent or a script is doing the provisioning. Start on Starter if a human is building an app that has to outlive the week.

Grab Your Connection Details

In the cluster view, click Connect to open the connection details and note the Host, Port, and User values. You will use them to build the connection string the ORM reads.

Store those values as Vercel environment variables rather than committing them to code. In the Vercel dashboard, go to Settings > Environment Variables, add DATABASE_URL, and scope it to the environments that need it. Locally, keep it in .env.local, which Next.js excludes from git by default.

Verify connectivity before going further:

mysql --connect-timeout 15 -u '<user>' -h '<host>' -P 4000 -D '<database>' \
  --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt -p

Model AI App Data: Sessions, Chat Logs, Preferences, and Retrieval State

An AI app stores a predictable set of things: conversation sessions, the messages inside them, the actions an agent took, and the preferences that shape future responses. Prisma models all of it cleanly, and the mechanics are the same ones any Prisma project uses.

Install the adapter, the serverless driver, and the Prisma CLI:

npm install @tidbcloud/prisma-adapter @tidbcloud/serverless
npm install prisma --save-dev

Enable driver adapters in prisma/schema.prisma and define the models:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["driverAdapters"]
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model Session {
  id        String        @id @default(uuid())
  userId    String        @map("user_id") @db.VarChar(64)
  title     String?       @db.VarChar(255)
  createdAt DateTime      @default(now()) @map("created_at")
  messages  Message[]
  actions   AgentAction[]

  @@index([userId, createdAt])
  @@map("sessions")
}

model Message {
  id         BigInt   @id @default(autoincrement())
  sessionId  String   @map("session_id") @db.VarChar(36)
  role       String   @db.VarChar(16)
  content    String   @db.Text
  tokenCount Int?     @map("token_count")
  createdAt  DateTime @default(now()) @map("created_at")
  session    Session  @relation(fields: [sessionId], references: [id])

  @@index([sessionId, createdAt])
  @@map("messages")
}

model AgentAction {
  id        BigInt   @id @default(autoincrement())
  sessionId String   @map("session_id") @db.VarChar(36)
  tool      String   @db.VarChar(64)
  input     Json?
  output    Json?
  status    String   @db.VarChar(16)
  createdAt DateTime @default(now()) @map("created_at")
  session   Session  @relation(fields: [sessionId], references: [id])

  @@index([sessionId, status])
  @@map("agent_actions")
}

model UserPreference {
  userId    String   @id @map("user_id") @db.VarChar(64)
  settings  Json
  updatedAt DateTime @updatedAt @map("updated_at")

  @@map("user_preferences")
}

Export the connection string and push the schema:

export DATABASE_URL='mysql://<user>:<password>@<host>:4000/<database>?sslaccept=strict'
npx prisma db push
npx prisma generate

Two notes on how the adapter behaves. prisma db push, Prisma Migrate, and introspection use the traditional TCP connection, so run them from your machine or CI rather than from an edge function. Prisma Client queries go over HTTPS through the adapter. Initialize the client once per module:

import { PrismaTiDBCloud } from '@tidbcloud/prisma-adapter';
import { PrismaClient } from '@prisma/client';

const adapter = new PrismaTiDBCloud({ url: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });

For adapter versions earlier than v6.6.0, build the connection first with connect() from @tidbcloud/serverless and pass it to new PrismaTiDBCloud(connection). The TiDB Cloud serverless driver and Prisma integration post covers the adapter, transactions, and the differences from the TCP path in more depth.

Store Embeddings and Power Retrieval With TiDB Vector Search

TiDB has a native VECTOR data type, so embeddings live in the same database as the sessions and messages they belong to. There is no second system to provision, no sync job, and no window where the vector store and the application database disagree.

Add a document table with a fixed-dimension vector column and an HNSW index. The dimension has to match your embedding model, and 1536 matches OpenAI’s text-embedding-3-small:

CREATE TABLE documents (
  id BIGINT PRIMARY KEY AUTO_RANDOM,
  session_id VARCHAR(36),
  content TEXT,
  embedding VECTOR(1536),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  VECTOR INDEX idx_embedding ((VEC_COSINE_DISTANCE(embedding)))
);

Prisma has no native mapping for VECTOR, so create the column with raw SQL and query it through the serverless driver or $queryRaw. Retrieval is an ordinary SQL query:

import { connect } from '@tidbcloud/serverless';

const conn = connect({ url: process.env.DATABASE_URL });

const results = await conn.execute(
  `SELECT id, content, VEC_COSINE_DISTANCE(embedding, ?) AS distance
   FROM documents
   ORDER BY distance
   LIMIT 10`,
  [JSON.stringify(queryEmbedding)]
);

When you need to filter by tenant, session, or user, run the nearest-neighbor search first and filter the result, because a WHERE clause ahead of the vector ordering can stop the index from being used:

SELECT * FROM (
  SELECT id, session_id, content,
         VEC_COSINE_DISTANCE(embedding, '[0.1, 0.2, ...]') AS distance
  FROM documents
  ORDER BY distance
  LIMIT 50
) t
WHERE session_id = '<session-id>'
ORDER BY distance
LIMIT 10;

The payoff shows up at write time. An insert that stores a message, its embedding, and an agent action commits as one transaction, so retrieval state and application state stay consistent. See the TiDB vector search documentation for distance functions, index behavior, and hybrid search with full text.

Connect From the Edge: Vercel Edge Functions and Cloudflare Workers

Edge runtimes are where database choices get tested, because they prohibit raw TCP sockets. Any driver built on a TCP connection fails there regardless of how it is configured. The TiDB Cloud serverless driver connects over HTTPS, and @tidbcloud/prisma-adapter v5.11.0 and later work in Vercel Edge Functions and Cloudflare Workers.

A route handler running at the edge looks like an ordinary Prisma query:

// app/api/messages/route.ts
export const runtime = 'edge';

import { PrismaTiDBCloud } from '@tidbcloud/prisma-adapter';
import { PrismaClient } from '@prisma/client';

const adapter = new PrismaTiDBCloud({ url: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });

export async function GET(request: Request) {
  const sessionId = new URL(request.url).searchParams.get('session');

  const messages = await prisma.message.findMany({
    where: { sessionId: sessionId ?? undefined },
    orderBy: { createdAt: 'asc' },
    take: 50,
  });

  return Response.json(messages);
}

Because each invocation issues its own HTTPS request, a burst of traffic produces a burst of independent requests rather than contention for a shared pool. There is no warm-up penalty on a cold start and no pool to size.

Test locally before deploying:

npm run dev

Open http://localhost:3000/api/messages?session=<session-id> and confirm you get rows back. If the response is empty but no error appears, the connection is healthy and the table is empty, which is the expected state right after prisma db push.

Deploy on Vercel With the TiDB Cloud Integration

Configuring development, preview, and production environments by hand is slow and error prone, especially when connection details change. The TiDB Cloud integration on the Vercel Marketplace handles it in a few clicks, and the demo app is published as a TiDB Cloud Starter Template.

  1. Open the template and click Deploy. Vercel prompts you to create a GitHub repository. Give it a name, and Vercel creates it if it does not exist.
  2. In the Add Integrations section, add TiDB Cloud. In the popup, select the target Vercel project, then the TiDB Organization, Project, and Cluster. The defaults are fine for a first deployment.
  3. Select Prisma as the framework and click Add Integration. Vercel returns you to the integration screen with a deployment in progress.
  4. When the deployment finishes, click Continue to Dashboard, then Visit to confirm the app is live.
  5. Check Settings > Environment Variables. The integration has already written the connection details, so there is nothing to paste by hand.

The last step is worth verifying rather than assuming. Preview deployments and production read the same variables, which is what keeps a branch deploy from pointing at the wrong database.

TiDB Cloud Starter vs. Vercel Postgres, Neon, and Supabase

Most Vercel developers choose between Vercel Postgres (now Neon through the Marketplace), Supabase, and TiDB Cloud Starter. All three are managed, all three scale to zero, and all three offer an HTTP driver for edge runtimes. The differences that matter for AI apps show up in scaling model, vector handling, and analytics.

TiDB Cloud StarterVercel Postgres / NeonSupabase
Wire protocolMySQLPostgreSQLPostgreSQL
Write scalingHorizontal across nodes, no manual shardingVertical on a single primary, read replicas for readsVertical on a single primary, read replicas for reads
Edge connectivityHTTPS serverless driver, Prisma and Kysely adaptersHTTP driver (@neondatabase/serverless)HTTP via PostgREST, or Supavisor for pooled TCP
Vector searchNative VECTOR type with HNSW index in the same databasepgvector extensionpgvector extension
Analytics on live dataHTAP: row store plus columnar replica in one systemAnalytical queries hit the same row storeAnalytical queries hit the same row store
Beyond the databaseDatabase onlyDatabase onlyAuth, storage, realtime, edge functions

Read the table by what your app needs rather than by row count. More detail on the tiers and limits is on the TiDB Cloud Starter product page.

Spin Up Your Free TiDB Cloud Starter Cluster

You now have the full path: a prototype wired to a real database, schema for sessions and agent state, embeddings stored beside the rows they describe, edge connectivity over HTTPS, and a one-click deployment that configures itself.

Start a free TiDB Cloud Starter cluster and point your Vercel app at it. The full source for the demo app is in the demo repository on GitHub.


Try for Free


Spin up a database with 25 GiB free resources.

Start Right Away

Have questions? Let us know how we can help.

Contact Us

TiDB Cloud Dedicated

A fully-managed cloud DBaaS for predictable workloads

TiDB Cloud Starter

A fully-managed cloud DBaaS for auto-scaling workloads