Windsurf
Windsurf Editorは、反復的なコーディングタスクを自動化することで生産性を向上させるように設計されたAI搭載のコードエディタです。堅牢で型安全なデータベースワークフロー用ツールキットであるPrismaと組み合わせることで、データベーススキーマ、クエリ、データシーディングの管理と最適化のための強力なソリューションとなります。
このガイドでは、PrismaとWindsurfを効果的に使用するための詳細な手順を説明します。
.windsurfrules
でプロジェクト固有のベストプラクティスを定義する。- Windsurfのコンテキスト認識機能を使用する。
- データベースに合わせてスキーマ、クエリ、シードデータを生成する。
このガイドはWindsurfに焦点を当てていますが、これらのパターンはどのAIエディタでも機能するはずです。お好みのツール向けにガイドを作成してほしい場合は、Xでお知らせください!
Prisma MCPサーバー
Prismaは独自のモデルコンテキストプロトコル(MCP)サーバーを提供しており、これによりPrisma Postgresデータベースの管理、データベーススキーマのモデリング、さらにはマイグレーションに関するチャットが可能です。
Windsurfプラグイン経由でPrisma MCPサーバーを追加
Windsurf MCPプラグインストアからPrisma MCPサーバーを追加できます。
新しいMCPプラグインは、Plugin Storeから追加できます。これは、Cascadeパネルの右上メニューにあるPluginsアイコンをクリックするか、Windsurf Settings > Cascade > Pluginsセクションからアクセスできます。Plugin StoreでPrismaを検索し、Prisma
プラグインをインストールしてください。
Prisma MCPサーバーを手動で追加することもできます。MCPサーバーをWindsurfに手動で追加する方法については、こちらで詳細を確認してください。
.windsurfrules
でプロジェクト固有のルールを定義する
Windsurfの.windsurfrules
ファイルを使用すると、Prismaプロジェクトに合わせたベストプラクティスと開発標準を適用できます。明確で一貫したルールを定義することで、Windsurfは手動での調整を最小限に抑えつつ、クリーンで保守可能、かつプロジェクト固有のコードを生成するようにできます。
これらのルールを実装するには、プロジェクトのルートに.windsurfrules
ファイルを作成します。以下は設定例です
.windsurfrules
ファイルの例
You are a senior TypeScript/JavaScript programmer with expertise in Prisma, clean code principles, and modern backend development.
Generate code, corrections, and refactorings that comply with the following guidelines:
TypeScript General Guidelines
Basic Principles
- Use English for all code and documentation.
- Always declare explicit types for variables and functions.
- Avoid using "any".
- Create precise, descriptive types.
- Use JSDoc to document public classes and methods.
- Maintain a single export per file.
- Write self-documenting, intention-revealing code.
Nomenclature
- Use PascalCase for classes and interfaces.
- Use camelCase for variables, functions, methods.
- Use kebab-case for file and directory names.
- Use UPPERCASE for environment variables and constants.
- Start function names with a verb.
- Use verb-based names for boolean variables:
- isLoading, hasError, canDelete
- Use complete words, avoiding unnecessary abbreviations.
- Exceptions: standard abbreviations like API, URL
- Accepted short forms:
- i, j for loop indices
- err for errors
- ctx for contexts
Functions
- Write concise, single-purpose functions.
- Aim for less than 20 lines of code.
- Name functions descriptively with a verb.
- Minimize function complexity:
- Use early returns.
- Extract complex logic to utility functions.
- Leverage functional programming techniques:
- Prefer map, filter, reduce.
- Use arrow functions for simple operations.
- Use named functions for complex logic.
- Use object parameters for multiple arguments.
- Maintain a single level of abstraction.
Data Handling
- Encapsulate data in composite types.
- Prefer immutability.
- Use readonly for unchanging data.
- Use as const for literal values.
- Validate data at the boundaries.
Error Handling
- Use specific, descriptive error types.
- Provide context in error messages.
- Use global error handling where appropriate.
- Log errors with sufficient context.
Prisma-Specific Guidelines
Schema Design
- Use meaningful, domain-driven model names.
- Leverage Prisma schema features:
- Use @id for primary keys.
- Use @unique for natural unique identifiers.
- Utilize @relation for explicit relationship definitions.
- Keep schemas normalized and DRY.
- Use meaningful field names and types.
- Implement soft delete with deletedAt timestamp.
- Use Prisma's native type decorators.
Prisma Client Usage
- Always use type-safe Prisma client operations.
- Prefer transactions for complex, multi-step operations.
- Use Prisma middleware for cross-cutting concerns:
- Logging
- Soft delete
- Auditing
- Handle optional relations explicitly.
- Use Prisma's filtering and pagination capabilities.
Database Migrations
- Create migrations for schema changes.
- Use descriptive migration names.
- Review migrations before applying.
- Never modify existing migrations.
- Keep migrations idempotent.
Error Handling with Prisma
- Catch and handle Prisma-specific errors:
- PrismaClientKnownRequestError
- PrismaClientUnknownRequestError
- PrismaClientValidationError
- Provide user-friendly error messages.
- Log detailed error information for debugging.
Testing Prisma Code
- Use in-memory database for unit tests.
- Mock Prisma client for isolated testing.
- Test different scenarios:
- Successful operations
- Error cases
- Edge conditions
- Use factory methods for test data generation.
- Implement integration tests with actual database.
Performance Considerations
- Use select and include judiciously.
- Avoid N+1 query problems.
- Use findMany with take and skip for pagination.
- Leverage Prisma's distinct for unique results.
- Profile and optimize database queries.
Security Best Practices
- Never expose raw Prisma client in APIs.
- Use input validation before database operations.
- Implement row-level security.
- Sanitize and validate all user inputs.
- Use Prisma's built-in protections against SQL injection.
Coding Style
- Keep Prisma-related code in dedicated repositories/modules.
- Separate data access logic from business logic.
- Create repository patterns for complex queries.
- Use dependency injection for Prisma services.
Code Quality
- Follow SOLID principles.
- Prefer composition over inheritance.
- Write clean, readable, and maintainable code.
- Continuously refactor and improve code structure.
Development Workflow
- Use version control (Git).
- Implement comprehensive test coverage.
- Use continuous integration.
- Perform regular code reviews.
- Keep dependencies up to date.
このファイルにより、一貫性があり保守しやすいコード生成が保証され、手動での介入を減らしつつプロジェクトの品質を向上させます。
Windsurfのコンテキスト認識機能を使用する
Windsurfのコンテキスト認識機能により、プロジェクトファイルと外部リソースの両方を活用して、AIのプロジェクト理解度を高めることができます。コンテキストにPrismaスキーマと関連ドキュメントを含めることで、Windsurfはデータベーススキーマに基づいて、より正確なクエリ、テスト、シードデータを生成できるようになります。
追加のPrismaリソースを含める
Windsurfには一般的なライブラリに関する組み込みの知識がありますが、外部のPrismaリソースを明示的に参照することで、その認識能力をさらに高めることができます。これは、最新の状態を保つためや、コード生成およびベストプラクティスに関する信頼できるコンテキストを提供するために特に役立ちます。
例えば、以下を参照できます
リクエストでリソースを参照する:
コード、説明、レビューを求める際には、関連するPrismaリソースへのリンクを含め、それを参照として使用するよう指定してください。
Generate a migration script using best practices from prisma.io/docs.
永続的な認識をリクエストする:
Windsurfに対し、プロジェクト内のすべてのPrisma関連作業において、特定の参照リソースを常に考慮するよう依頼してください。
Always use the Prisma Changelog at prisma.io/changelog for Prisma updates in this project.
定期的な更新を依頼する:
Windsurfに更新や新機能の確認をさせたい場合は、明示的にリクエストしてください。
Before suggesting Prisma code, check the latest changes from prisma.io/changelog.
リクエストやプロジェクトガイドラインで外部リソースを直接参照することで、Windsurfが最新かつ最も関連性の高いPrisma情報を活用するようにできます。
スキーマをコンテキストとして使用する
Windsurfは初期設定で、現在のファイル、他の開いているファイル、およびコードベースのインデックス化された部分をコンテキストとして自動的に考慮します。CascadeがPrismaスキーマを完全に活用できるようにするには、schema.prisma
ファイルを開いたままにするか、エディタにピン留めしてください。
Prismaスキーマの生成
Windsurfは、高レベルの記述からPrismaスキーマを生成でき、データベーススキーマの堅固な基盤を迅速に構築することを可能にします。明確で詳細なプロンプトを提供することで、Windsurfはあなたの要件に合わせたPrismaスキーマを作成します。一般的な基本スキーマが必要な場合でも、詳細なユースケースのための非常に具体的なスキーマが必要な場合でも、Windsurfは正確に提供できます。以下にプロンプトとその対応する結果の例を示します
LLMは、同じプロンプトでも毎回異なる結果を生成する場合があります。
- Prismaスキーマを生成するためのプロンプト
- 結果
"Create a Prisma schema for a SaaS app using PostgreSQL as a provider with `User`, `Organization`, and `Subscription` models, ensuring all models include `createdAt` and `updatedAt` DateTime fields with defaults, a soft-delete `deletedAt` field, and proper relationships between entities."
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int
role UserRole @default(MEMBER)
}
model Organization {
id Int @id @default(autoincrement())
name String
slug String @unique
subscription Subscription?
users User[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
model Subscription {
id Int @id @default(autoincrement())
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int @unique
plan SubscriptionPlan
status SubscriptionStatus @default(ACTIVE)
startDate DateTime
endDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
enum UserRole {
ADMIN
MEMBER
}
enum SubscriptionPlan {
FREE
BASIC
PREMIUM
ENTERPRISE
}
enum SubscriptionStatus {
ACTIVE
CANCELED
EXPIRED
PAST_DUE
}
Prismaクエリの生成
Windsurfは、基本的なデータ取得から複雑で最適化された操作まで、Prismaスキーマに合わせたクエリを作成するのに役立ちます。最良の結果を得るには、含めるフィールド、適用する条件、横断するリレーションシップなど、要件を定義する明確で具体的なプロンプトを作成することに焦点を当ててください。これにより、Windsurfは正確でパフォーマンスの高い型安全なクエリを生成します。以下にプロンプトとその結果のクエリの例を示します
- データベースクエリを生成するためのプロンプト
- 結果
"Generate a query to retrieve all User records associated with an Organization that has an `active` Subscription, while excluding any records where the `deletedAt` field is set."
const activeUsers = await prisma.user.findMany({
where: {
deletedAt: null,
organization: {
deletedAt: null,
subscription: {
deletedAt: null,
status: 'ACTIVE'
}
}
},
include: {
organization: {
include: {
subscription: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip: 0,
take: 10
});
Windsurfでシードファイルを作成する
シードスクリプトの作成は面倒なプロセスになることがありますが、Windsurfは構造化された整理されたシードコードを迅速に生成することでそれを簡素化します。明確な指示を提供することで、Windsurfがあなたのスキーマで定義されたリレーションシップや複雑な構造の処理を含め、現実的なデータでデータベースを投入するスクリプトを作成するように導くことができます。以下にプロンプトとその結果のシードスクリプトの例を示します
- データベースをシードするためのプロンプト
- 結果
"Generate code to populate the Prisma schema with realistic data for the User, Organization, and Subscription models, ensuring all necessary fields are included."
import {
PrismaClient,
UserRole,
SubscriptionPlan,
SubscriptionStatus,
} from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
// Clear existing data
await prisma.user.deleteMany();
await prisma.subscription.deleteMany();
await prisma.organization.deleteMany();
const organizations = [
{
name: "Tech Innovators",
slug: "tech-innovators",
subscription: {
plan: SubscriptionPlan.ENTERPRISE,
status: SubscriptionStatus.ACTIVE,
startDate: new Date("2024-01-01"),
},
users: [
{
name: "Emma Thompson",
email: "emma@techinnovators.com",
role: UserRole.ADMIN,
password: "password123",
},
{
name: "Michael Chen",
email: "michael@techinnovators.com",
role: UserRole.MEMBER,
password: "password123",
},
],
},
{
name: "Digital Solutions",
slug: "digital-solutions",
subscription: {
plan: SubscriptionPlan.PREMIUM,
status: SubscriptionStatus.ACTIVE,
startDate: new Date("2024-01-15"),
},
users: [
{
name: "Sarah Wilson",
email: "sarah@digitalsolutions.com",
role: UserRole.ADMIN,
password: "password123",
},
{
name: "James Miller",
email: "james@digitalsolutions.com",
role: UserRole.MEMBER,
password: "password123",
},
],
},
{
name: "Cloud Systems",
slug: "cloud-systems",
subscription: {
plan: SubscriptionPlan.BASIC,
status: SubscriptionStatus.ACTIVE,
startDate: new Date("2024-02-01"),
},
users: [
{
name: "David Garcia",
email: "david@cloudsystems.com",
role: UserRole.ADMIN,
password: "password123",
},
{
name: "Lisa Wang",
email: "lisa@cloudsystems.com",
role: UserRole.MEMBER,
password: "password123",
},
],
},
{
name: "Data Analytics Co",
slug: "data-analytics",
subscription: {
plan: SubscriptionPlan.PREMIUM,
status: SubscriptionStatus.ACTIVE,
startDate: new Date("2024-01-10"),
},
users: [
{
name: "Alex Johnson",
email: "alex@dataanalytics.com",
role: UserRole.ADMIN,
password: "password123",
},
{
name: "Rachel Kim",
email: "rachel@dataanalytics.com",
role: UserRole.MEMBER,
password: "password123",
},
],
},
{
name: "Smart Solutions",
slug: "smart-solutions",
subscription: {
plan: SubscriptionPlan.FREE,
status: SubscriptionStatus.ACTIVE,
startDate: new Date("2024-02-15"),
},
users: [
{
name: "Daniel Brown",
email: "daniel@smartsolutions.com",
role: UserRole.ADMIN,
password: "password123",
},
{
name: "Maria Rodriguez",
email: "maria@smartsolutions.com",
role: UserRole.MEMBER,
password: "password123",
},
],
},
];
for (const org of organizations) {
const createdOrg = await prisma.organization.create({
data: {
name: org.name,
slug: org.slug,
subscription: {
create: {
plan: org.subscription.plan,
status: org.subscription.status,
startDate: org.subscription.startDate,
},
},
},
});
for (const user of org.users) {
await prisma.user.create({
data: {
name: user.name,
email: user.email,
password: user.password,
role: user.role,
organizationId: createdOrg.id,
},
});
}
}
console.log("Seed data created successfully");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
追加リソース
WindsurfをPrismaと共に使用することで、クリーンで保守しやすいデータベースコードを確保しながら開発を加速できます。引き続き学習するには
Prismaとつながる
Prismaの旅を続けるには、以下とつながってください 活発なコミュニティ。最新情報を入手し、参加し、他の開発者と協力してください
- Xでフォローする アナウンス、ライブイベント、役立つヒントのために。
- Discordに参加する 質問をしたり、コミュニティと話したり、会話を通じて活発なサポートを得るために。
- YouTubeで購読する チュートリアル、デモ、ストリーミングのために。
- GitHubで参加する リポジトリにスターを付けたり、問題を報告したり、問題に貢献したりして。