Cursor + PlanetScale + Vercel Security Blueprint

Share

To secure a Cursor + PlanetScale + Vercel stack, you need to: (1) use separate database branches for production and development environments, (2) store connection strings in Vercel environment variables with SSL enabled, (3) protect your production branch and use deploy requests for schema changes, (4) implement authorization checks in all API routes since PlanetScale lacks built-in RLS, and (5) create a .cursorignore file to prevent AI from accessing your .env files. This blueprint covers PlanetScale branching security, Prisma configuration, and application-level authorization patterns.

Setup Time2-3 hours

TL;DR

PlanetScale provides a MySQL-compatible serverless database with git-like branching. Security priorities: use different database branches for development vs production, store connection strings in Vercel environment variables, enable safe migrations with deploy requests, and implement authorization in your API routes since PlanetScale doesn't have row-level security like Supabase.

Platform Guides & Checklists

      Cursor Security Guide



      PlanetScale Security Guide



      Vercel Security Guide



      Pre-Launch Checklist

Stack Overview

PlanetScale's branching model is excellent for safe database changes, but security depends on proper configuration:

ComponentRoleSecurity Focus
CursorAI code editorQuery safety, secret management
PlanetScaleServerless MySQLBranch isolation, connection security
VercelHostingEnvironment variables, API routes

Part 1: Database Branch Security PlanetScale

Branch Strategy PlanetScale

Use PlanetScale branches to isolate environments:

Recommended branch structure
# Production branch (protected)
main
  ├── Vercel Production deployment
  ├── Safe migrations via deploy requests
  └── No direct schema changes

# Development branch
dev
  ├── Vercel Preview deployments
  ├── Schema experimentation
  └── Test data only

# Feature branches (optional)
feature/user-profiles
  ├── Schema changes for new feature
  └── Merge to dev, then main via deploy request

Connection Strings PlanetScale Vercel

Each branch has its own connection string. Never share production credentials:

Vercel Environment Variables
# Production (main branch)
DATABASE_URL="mysql://user:pass@aws.connect.psdb.cloud/mydb?sslaccept=strict"

# Preview (dev branch) - set in Vercel for preview deployments
DATABASE_URL="mysql://user:pass@aws.connect.psdb.cloud/mydb-dev?sslaccept=strict"

PlanetScale passwords can't be retrieved after creation. Store them securely in Vercel immediately. If lost, you'll need to create a new password.

Part 2: Query Security with Prisma PlanetScale

PlanetScale works well with Prisma. Configure it for serverless:

prisma/schema.prisma
datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"  // Required for PlanetScale
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String   @db.Text
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())

  @@index([authorId])
}

Part 3: Application-Level Authorization Vercel

Unlike Supabase, PlanetScale doesn't have row-level security. Implement authorization in your code:

app/api/posts/[id]/route.ts
import { prisma } from '@/lib/prisma';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';

export async function PUT(
  request: Request,
  { params }: { params: { id: string } }
) {
  // Verify authentication
  const session = await getServerSession(authOptions);
  if (!session?.user?.id) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Fetch the post
  const post = await prisma.post.findUnique({
    where: { id: params.id }
  });

  if (!post) {
    return Response.json({ error: 'Not found' }, { status: 404 });
  }

  // Authorization check - only author can update
  if (post.authorId !== session.user.id) {
    return Response.json({ error: 'Forbidden' }, { status: 403 });
  }

  // Safe to update
  const { title, content } = await request.json();
  const updated = await prisma.post.update({
    where: { id: params.id },
    data: { title, content }
  });

  return Response.json(updated);
}

Part 4: Safe Schema Migrations PlanetScale

Using Deploy Requests PlanetScale

Never run migrations directly on production. Use PlanetScale's deploy request workflow:

Migration workflow
# 1. Make schema changes on dev branch
npx prisma db push

# 2. Create deploy request in PlanetScale dashboard
#    dev → main

# 3. Review schema diff in dashboard

# 4. Deploy (applies changes to main branch)

# 5. Vercel will use the updated production schema

Security Checklist

Pre-Launch Checklist for Cursor + PlanetScale + Vercel

Production branch protected from direct changes

Separate connection strings for prod/dev

DATABASE_URL in Vercel environment variables

SSL enabled in connection string (sslaccept=strict)

Authorization checks in all API routes

Prisma relationMode set to "prisma"

No raw SQL with user input

.cursorignore excludes .env files

Deploy requests used for production migrations

Alternative Stack Configurations

Cursor + Supabase + Vercel Swap PlanetScale for Supabase PostgreSQL with built-in RLS. Different security model.

      Cursor + Neon + Railway
      Serverless Postgres with branching like PlanetScale, but with RLS support.


      Cursor + Prisma + Vercel
      General Prisma guide applicable to any database backend.

Why doesn't PlanetScale have row-level security?

PlanetScale is MySQL-compatible, and MySQL doesn't have built-in RLS like PostgreSQL. You implement authorization in your application layer. This gives you more flexibility but requires careful coding.

::faq-item{question="What's relationMode = "prisma"?"} PlanetScale doesn't support foreign key constraints at the database level. Setting relationMode to "prisma" tells Prisma to handle relations in application code instead, maintaining referential integrity through Prisma's query engine. ::

How do I handle connection pooling on Vercel?

PlanetScale handles connection pooling automatically on their edge. Unlike traditional MySQL, you don't need additional pooling like PgBouncer. The serverless driver handles this efficiently.

Using PlanetScale with Cursor?

Scan your app for query security and authorization issues.

Start Free Scan
Security Blueprints

Cursor + PlanetScale + Vercel Security Blueprint