agprojects
      Tutorial
      {"antigravity"
      "prisma"
      "database"
      "mcp"
      "schema"}

      How to Extend Prisma Schema Using Antigravity

      Learn how to extend your Prisma schema with Antigravity's MCP integration. Add models, relations, migrations, and complex patterns intelligently.

      By Projects.tech TeamNovember 20, 202516 min read25 views
      How to Extend Prisma Schema Using Antigravity

      How to Extend Prisma Schema Using Antigravity

      Overview

      Learn how to leverage Antigravity's Prisma MCP integration to extend your database schema intelligently. This guide covers adding models, relations, migrations, and complex schema patterns with AI assistance.

      Why Antigravity + Prisma?

      Antigravity's Prisma MCP Server integration provides:

      • Schema Awareness: Agent understands your current database structure
      • Smart Suggestions: Proposes correct table and column names
      • Relation Management: Handles complex many-to-many relationships
      • Migration Generation: Creates safe database migrations
      • Type Safety: Generates TypeScript types automatically

      Prerequisites

      • Existing Next.js project with Prisma
      • Antigravity editor access
      • PostgreSQL/MySQL/SQLite database

      Connect Prisma MCP Server

      Step 1: Open MCP Store

      1. Click "..." dropdown (top of agent panel)
      2. Select "MCP Store"
      3. Find "Prisma" in the list
      4. Click "Install"

      Step 2: Authenticate

      Follow the on-screen prompts to connect Prisma to your project.

      Step 3: Verify Connection

      The agent can now:

      • Read your prisma/schema.prisma
      • Understand existing models and relations
      • Suggest schema extensions
      • Generate migrations

      Common Schema Extension Scenarios

      1. Add a New Model

      Scenario: Add a Comment model to blog posts

      Prompt:

      Add a Comment model to the Prisma schema with:
      - id (UUID)
      - content (Text)
      - createdAt (DateTime)
      - authorId (references User)
      - postId (references Post)
      - One-to-many relation: Post has many Comments
      - One-to-many relation: User has many Comments
      - Include cascade delete when post is deleted
      

      Antigravity Response:

      // prisma/schema.prisma
      
      model Comment {
        id        String   @id @default(uuid())
        content   String   @db.Text
        createdAt DateTime @default(now())
        updatedAt DateTime @updatedAt
      
        // Relations
        authorId  String
        author    User     @relation(fields: [authorId], references: [id])
      
        postId    String
        post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
      
        @@index([postId])
        @@index([authorId])
      }
      
      // Update Post model
      model Post {
        // ... existing fields
        comments  Comment[]
      }
      
      // Update User model
      model User {
        // ... existing fields
        comments  Comment[]
      }
      

      Migration Command:

      npx prisma migrate dev --name add-comments
      

      2. Add Many-to-Many Relation

      Scenario: Posts can have multiple authors (collaboration)

      Prompt:

      Extend the Prisma schema to support multiple authors per post:
      - Create many-to-many relation between User and Post
      - Use explicit join table PostAuthor
      - Include role field (AUTHOR, EDITOR, CONTRIBUTOR)
      - Add createdAt timestamp
      

      Antigravity Response:

      model PostAuthor {
        id        String   @id @default(uuid())
        role      AuthorRole @default(AUTHOR)
        createdAt DateTime @default(now())
      
        userId    String
        user      User     @relation(fields: [userId], references: [id])
      
        postId    String
        post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
      
        @@unique([userId, postId])
        @@index([userId])
        @@index([postId])
      }
      
      enum AuthorRole {
        AUTHOR
        EDITOR
        CONTRIBUTOR
      }
      
      model Post {
        // ... existing fields
        authors   PostAuthor[]
      }
      
      model User {
        // ... existing fields
        authoredPosts PostAuthor[]
      }
      

      3. Add Enum Field

      Scenario: Add post visibility levels

      Prompt:

      Add a visibility field to Post model:
      - Enum type: PUBLIC, PRIVATE, UNLISTED
      - Default to PUBLIC
      - Add index for filtering
      

      Antigravity Response:

      enum PostVisibility {
        PUBLIC
        PRIVATE
        UNLISTED
      }
      
      model Post {
        // ... existing fields
        visibility PostVisibility @default(PUBLIC)
      
        @@index([visibility])
      }
      

      4. Add JSON Field

      Scenario: Store flexible metadata

      Prompt:

      Add a metadata JSON field to Post:
      - Store custom key-value pairs
      - Optional field
      - Use Prisma Json type
      

      Antigravity Response:

      model Post {
        // ... existing fields
        metadata  Json?
      }
      

      Usage:

      await prisma.post.create({
        data: {
          title: "Example Post",
          metadata: {
            featured: true,
            readingTime: 5,
            customField: "value"
          }
        }
      });
      

      5. Add Soft Delete

      Scenario: Implement soft delete pattern

      Prompt:

      Add soft delete support to Post model:
      - Add deletedAt DateTime field (nullable)
      - Add deletedBy User relation (nullable)
      - Exclude soft-deleted posts by default in queries
      

      Antigravity Response:

      model Post {
        // ... existing fields
        deletedAt DateTime?
        deletedBy String?
        deletedByUser User? @relation("DeletedPosts", fields: [deletedBy], references: [id])
      
        @@index([deletedAt])
      }
      
      model User {
        // ... existing relations
        deletedPosts Post[] @relation("DeletedPosts")
      }
      

      Query helper:

      // Exclude soft-deleted posts
      const activePosts = await prisma.post.findMany({
        where: { deletedAt: null }
      });
      
      // Include soft-deleted
      const allPosts = await prisma.post.findMany();
      
      // Only soft-deleted
      const trashedPosts = await prisma.post.findMany({
        where: { deletedAt: { not: null } }
      });
      

      6. Add Timestamps

      Scenario: Track creation and updates

      Prompt:

      Add timestamp tracking to Category model:
      - createdAt (auto-set on create)
      - updatedAt (auto-update on change)
      

      Antigravity Response:

      model Category {
        // ... existing fields
        createdAt DateTime @default(now())
        updatedAt DateTime @updatedAt
      }
      

      7. Add Unique Constraints

      Scenario: Ensure email uniqueness

      Prompt:

      Add unique constraint to User email field
      Add composite unique constraint on Post (userId, slug)
      

      Antigravity Response:

      model User {
        email     String   @unique
        // ... other fields
      }
      
      model Post {
        userId    String
        slug      String
      
        @@unique([userId, slug])
      }
      

      8. Add Indexes

      Scenario: Optimize query performance

      Prompt:

      Add indexes to Post model for:
      - status field
      - publishedAt field
      - Composite index on (status, publishedAt)
      - Full-text search on title and content
      

      Antigravity Response:

      model Post {
        // ... existing fields
      
        @@index([status])
        @@index([publishedAt])
        @@index([status, publishedAt])
        @@fulltext([title, content])
      }
      

      9. Add Self-Referential Relation

      Scenario: Nested categories (parent/child)

      Prompt:

      Make Category model hierarchical:
      - Add parent-child relationship
      - Category can have one parent
      - Category can have many children
      - Nullable parent (for root categories)
      

      Antigravity Response:

      model Category {
        id          String     @id @default(uuid())
        name        String
        parentId    String?
      
        // Self-referential relation
        parent      Category?  @relation("CategoryHierarchy", fields: [parentId], references: [id])
        children    Category[] @relation("CategoryHierarchy")
      
        @@index([parentId])
      }
      

      10. Add Polymorphic Relations

      Scenario: Comments on multiple types (Posts, Videos)

      Prompt:

      Create a Comment model that can belong to either Post or Video:
      - commentableType (POST or VIDEO)
      - commentableId (UUID of either)
      - Use discriminated union pattern
      

      Antigravity Response:

      enum CommentableType {
        POST
        VIDEO
      }
      
      model Comment {
        id               String          @id @default(uuid())
        content          String          @db.Text
        commentableType  CommentableType
        commentableId    String
      
        authorId         String
        author           User            @relation(fields: [authorId], references: [id])
      
        createdAt        DateTime        @default(now())
      
        @@index([commentableType, commentableId])
      }
      
      // Usage in queries
      const postComments = await prisma.comment.findMany({
        where: {
          commentableType: 'POST',
          commentableId: postId
        }
      });
      

      Advanced Schema Patterns

      Auditing Pattern

      Track who created and modified records:

      Prompt:

      Add audit fields to Post model:
      - createdBy (User relation)
      - updatedBy (User relation)
      - Track creation and update authors
      

      Antigravity Response:

      model Post {
        // ... existing fields
      
        createdById String
        createdBy   User   @relation("PostsCreated", fields: [createdById], references: [id])
      
        updatedById String
        updatedBy   User   @relation("PostsUpdated", fields: [updatedById], references: [id])
      
        @@index([createdById])
        @@index([updatedById])
      }
      
      model User {
        // ... existing relations
        postsCreated  Post[] @relation("PostsCreated")
        postsUpdated  Post[] @relation("PostsUpdated")
      }
      

      Versioning Pattern

      Keep history of changes:

      Prompt:

      Add versioning to Post model:
      - version number (integer)
      - PostVersion model to store history
      - Track all changes with timestamps
      

      Antigravity Response:

      model Post {
        // ... existing fields
        version   Int      @default(1)
        versions  PostVersion[]
      }
      
      model PostVersion {
        id        String   @id @default(uuid())
        postId    String
        post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
      
        version   Int
        title     String
        content   String   @db.Text
        createdAt DateTime @default(now())
        createdBy String
        author    User     @relation(fields: [createdBy], references: [id])
      
        @@unique([postId, version])
        @@index([postId])
      }
      

      Multi-Tenancy Pattern

      Isolate data by organization:

      Prompt:

      Add multi-tenancy to the schema:
      - Organization model
      - All content models belong to Organization
      - Add organizationId to Post, User, Category
      

      Antigravity Response:

      model Organization {
        id        String   @id @default(uuid())
        name      String
        slug      String   @unique
        createdAt DateTime @default(now())
      
        users     User[]
        posts     Post[]
        categories Category[]
      }
      
      model Post {
        // ... existing fields
        organizationId String
        organization   Organization @relation(fields: [organizationId], references: [id])
      
        @@index([organizationId])
      }
      
      model User {
        // ... existing fields
        organizationId String
        organization   Organization @relation(fields: [organizationId], references: [id])
      
        @@index([organizationId])
      }
      

      Migration Best Practices

      1. Review Generated Migrations

      Always check migrations before applying:

      npx prisma migrate dev --name your-change --create-only
      

      Review the SQL in prisma/migrations/:

      -- CreateTable
      CREATE TABLE "Comment" (
          "id" TEXT NOT NULL PRIMARY KEY,
          "content" TEXT NOT NULL,
          "authorId" TEXT NOT NULL,
          CONSTRAINT "Comment_authorId_fkey" FOREIGN KEY ("authorId")
            REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
      );
      
      -- CreateIndex
      CREATE INDEX "Comment_authorId_idx" ON "Comment"("authorId");
      

      Then apply:

      npx prisma migrate dev
      

      2. Handle Data Migrations

      For schema changes affecting existing data:

      Prompt:

      I need to add a "featured" boolean field to Post model.
      Existing posts should default to false.
      Create a data migration to set the top 10 posts to featured=true.
      

      Antigravity Response:

      1. Schema change:
      model Post {
        featured Boolean @default(false)
      }
      
      1. Data migration script:
      // prisma/migrations/20250120_set_featured_posts.ts
      import { PrismaClient } from '@prisma/client';
      const prisma = new PrismaClient();
      
      async function main() {
        const topPosts = await prisma.post.findMany({
          orderBy: { views: 'desc' },
          take: 10,
        });
      
        await prisma.post.updateMany({
          where: { id: { in: topPosts.map(p => p.id) } },
          data: { featured: true },
        });
      
        console.log(`Set ${topPosts.length} posts as featured`);
      }
      
      main()
        .catch(console.error)
        .finally(() => prisma.$disconnect());
      

      3. Rename Fields Safely

      Prompt:

      Rename Post.title to Post.headline
      Preserve existing data
      

      Antigravity Response:

      1. Create migration manually:
      -- AlterTable
      ALTER TABLE "Post" RENAME COLUMN "title" TO "headline";
      
      1. Update schema:
      model Post {
        headline String // formerly title
      }
      
      1. Regenerate client:
      npx prisma generate
      

      4. Remove Fields Safely

      Prompt:

      Remove Post.oldField
      Create backup before deletion
      

      Antigravity Steps:

      1. Backup data:
      // scripts/backup-old-field.ts
      const posts = await prisma.post.findMany({
        select: { id: true, oldField: true }
      });
      
      fs.writeFileSync('backup-oldField.json', JSON.stringify(posts));
      
      1. Remove from schema:
      model Post {
        // oldField removed
      }
      
      1. Create migration:
      npx prisma migrate dev --name remove-old-field
      

      Troubleshooting

      Foreign Key Constraint Errors

      Error: "Foreign key constraint failed"

      Solution via Antigravity:

      Update the schema to add onDelete cascade to the Comment relation:
      
      Comment.postId -> Post.id (onDelete: Cascade)
      
      This ensures comments are deleted when posts are deleted.
      

      Result:

      model Comment {
        postId String
        post   Post   @relation(fields: [postId], references: [id], onDelete: Cascade)
      }
      

      Unique Constraint Violations

      Error: "Unique constraint failed on slug"

      Solution via Antigravity:

      Add validation in the application layer to check slug uniqueness
      before creating posts:
      
      async function createPost(data) {
        let slug = slugify(data.title);
        let counter = 1;
      
        while (await prisma.post.findUnique({ where: { slug } })) {
          slug = `${slugify(data.title)}-${counter}`;
          counter++;
        }
      
        return prisma.post.create({
          data: { ...data, slug }
        });
      }
      

      Migration Conflicts

      Error: "Migration conflicts detected"

      Solution:

      I have migration conflicts. Help me resolve them.
      
      Current state: My local migrations differ from production
      

      Antigravity Response:

      1. Check migration history:
      npx prisma migrate status
      
      1. If behind production:
      npx prisma migrate deploy
      
      1. If ahead of production:
      npx prisma migrate resolve --rolled-back 20250120_migration_name
      
      1. If diverged:
      npx prisma migrate diff \
        --from-schema-datamodel prisma/schema.prisma \
        --to-schema-datasource prisma/schema.prisma
      

      Testing Schema Changes

      1. Test Migrations Locally

      # Create test database
      createdb myapp_test
      
      # Run migrations
      DATABASE_URL="postgresql://localhost/myapp_test" \
        npx prisma migrate dev
      
      # Seed test data
      npx prisma db seed
      
      # Test queries
      npm test
      

      2. Validate Relations

      Prompt:

      Test the Comment -> Post relation by:
      1. Creating a post
      2. Creating comments for that post
      3. Querying post with comments
      4. Deleting post and verify comments cascade delete
      

      Antigravity Response:

      // test/post-comments.test.ts
      import { prisma } from '@/lib/prisma';
      
      describe('Post Comments Relation', () => {
        let post: Post;
      
        beforeAll(async () => {
          post = await prisma.post.create({
            data: {
              title: 'Test Post',
              content: 'Content',
            }
          });
        });
      
        it('creates comments for post', async () => {
          const comment = await prisma.comment.create({
            data: {
              content: 'Test comment',
              postId: post.id,
              authorId: testUser.id,
            }
          });
      
          expect(comment.postId).toBe(post.id);
        });
      
        it('queries post with comments', async () => {
          const postWithComments = await prisma.post.findUnique({
            where: { id: post.id },
            include: { comments: true }
          });
      
          expect(postWithComments.comments.length).toBeGreaterThan(0);
        });
      
        it('cascade deletes comments when post deleted', async () => {
          const commentCount = await prisma.comment.count({
            where: { postId: post.id }
          });
      
          await prisma.post.delete({ where: { id: post.id } });
      
          const remainingComments = await prisma.comment.count({
            where: { postId: post.id }
          });
      
          expect(remainingComments).toBe(0);
        });
      });
      

      Real-World Examples

      E-commerce Schema

      Prompt:

      Extend my schema for an e-commerce platform:
      - Product model with variants
      - Order model with order items
      - Cart model
      - Payment model
      - Shipping address
      - Include all necessary relations
      

      Antigravity generates:

      model Product {
        id          String    @id @default(uuid())
        name        String
        description String    @db.Text
        price       Decimal   @db.Decimal(10, 2)
        stock       Int       @default(0)
        variants    ProductVariant[]
        orderItems  OrderItem[]
        createdAt   DateTime  @default(now())
      }
      
      model ProductVariant {
        id          String    @id @default(uuid())
        productId   String
        product     Product   @relation(fields: [productId], references: [id])
        name        String    // e.g., "Size: Large, Color: Blue"
        price       Decimal   @db.Decimal(10, 2)
        stock       Int
        sku         String    @unique
      }
      
      model Cart {
        id        String     @id @default(uuid())
        userId    String     @unique
        user      User       @relation(fields: [userId], references: [id])
        items     CartItem[]
        createdAt DateTime   @default(now())
      }
      
      model CartItem {
        id        String  @id @default(uuid())
        cartId    String
        cart      Cart    @relation(fields: [cartId], references: [id])
        productId String
        product   Product @relation(fields: [productId], references: [id])
        quantity  Int     @default(1)
      }
      
      model Order {
        id              String        @id @default(uuid())
        userId          String
        user            User          @relation(fields: [userId], references: [id])
        items           OrderItem[]
        total           Decimal       @db.Decimal(10, 2)
        status          OrderStatus   @default(PENDING)
        shippingAddress ShippingAddress?
        payment         Payment?
        createdAt       DateTime      @default(now())
      }
      
      model OrderItem {
        id        String  @id @default(uuid())
        orderId   String
        order     Order   @relation(fields: [orderId], references: [id])
        productId String
        product   Product @relation(fields: [productId], references: [id])
        quantity  Int
        price     Decimal @db.Decimal(10, 2)
      }
      
      model ShippingAddress {
        id         String  @id @default(uuid())
        orderId    String  @unique
        order      Order   @relation(fields: [orderId], references: [id])
        street     String
        city       String
        state      String
        zipCode    String
        country    String
      }
      
      model Payment {
        id            String        @id @default(uuid())
        orderId       String        @unique
        order         Order         @relation(fields: [orderId], references: [id])
        amount        Decimal       @db.Decimal(10, 2)
        status        PaymentStatus @default(PENDING)
        method        String        // e.g., "credit_card", "paypal"
        transactionId String?
        paidAt        DateTime?
      }
      
      enum OrderStatus {
        PENDING
        PAID
        SHIPPED
        DELIVERED
        CANCELLED
      }
      
      enum PaymentStatus {
        PENDING
        COMPLETED
        FAILED
        REFUNDED
      }
      

      Next Steps

      You've mastered Prisma schema extensions with Antigravity! Now explore:

      1. Build APIs: How to Create API Routes with Antigravity
      2. CRUD Operations: How to Build CRUD with Antigravity
      3. Best Practices: Antigravity Best Practices
      4. Example Projects: Antigravity Projects Examples

      Conclusion

      Antigravity's Prisma MCP integration makes schema extension intelligent and safe:

      • Understands your existing structure
      • Suggests proper relations
      • Generates migrations automatically
      • Prevents common mistakes

      Start extending your Prisma schema with AI assistance today!


      Keywords: antigravity prisma example, extend prisma schema antigravity, prisma schema tutorial, antigravity database, prisma migrations antigravity, prisma relations

      Related Articles

      Tutorial

      Vibe Coding Sora AI: Copilot, Cursor & Prompt Engineering Tutorial

      1. Introduction: What Is Vibe Coding and Why It Matters 1.1 Defining "Vibe Coding" — From Natural Language to Working Software The term "vibe coding" was popula...

      October 28, 202610 min
      Tutorial

      Vibe Coding Tutorial: Sora AI, Copilot, Cursor & Prompt Engineering

      Introduction: Why Vibe Coding Is Changing How We Build Software Something fundamental has shifted in how developers write software. Instead of typing every line...

      October 21, 20269 min
      Tutorial

      AI Coding Tutorial: Vibe Coding with Copilot, Cursor & Google Antigravity

      Introduction: Why Vibe Coding Is Changing How We Build Software "Vibe coding" is the practice of describing what you want in plain natural language and letting ...

      September 30, 20268 min