メインコンテンツまでスキップ

Cursor での GraphQL API 開発

GraphQL API Development in Cursor

GraphQL は、クライアントが必要なデータを正確にリクエストできるようにすることで、API の構築方法を革命的に変えました。Cursor(AI 駆動の IDE エディタ)と組み合わせることで、GraphQL 開発ワークフローは大幅に高速化され、よりインテリジェントになります。このガイドでは、スキーマ設計から本番環境対応のリゾルバ実装まで、Cursor の高度な機能を使用してすべてをカバーします。

Cursor での GraphQL 入門​

Cursor は、インテリジェントなコード補完、AI 駆動のコード生成、およびスキーマ関係の深い理解を通じて、GraphQL 開発に優れた環境を提供します。新しい API をゼロから構築する場合でも、既存の API をメンテナンスする場合でも、Cursor の機能により、より少ない労力でより良い GraphQL コードを作成できます。

Cursor と GraphQL ツールの統合により、以下が可能になります:

  • TypeScript 型からスキーマを自動生成する
  • リゾルバ実装のインテリジェントな提案を受け取る
  • スキーマに対してクエリをリアルタイムで検証する
  • AI アシスタンスを活用して大規模なスキーマをリファクタリングする
  • コードから直接ドキュメントを生成する

実装の詳細に入る前に、ワークスペースに以下のツールがインストールされていることを確認してください:

  • Node.js 18 以上
  • Cursor IDE(最新バージョンを推奨)
  • クエリテスト用の GraphQL クライアント拡張機能

Cursor で GraphQL プロジェクトをセットアップする​

構造化されたプロジェクトは、スケーラブルな GraphQL 開発に不可欠です。Cursor は、最初からコードベースを整理するのに優れています。

新しいプロジェクトディレクトリを作成し、お好みのパッケージマネージャーで初期化してください。このディレクトリで Cursor を開き、以下のフォルダ構造を作成します:

src/
schema/
typeDefs.ts
resolvers/
index.ts
userResolver.ts
postResolver.ts
prisma/
schema.prisma
server.ts

この構造は、関心事を明確に分離し、Cursor の AI がスキーマ定義、リゾルバロジック、およびデータベースモデルの境界を理解できるようにします。この構造を Cursor で開くと、エディタは自動的にすべての型関係をインデックス化し、ファイル間の提案を提供します。

Apollo Server と Prisma セットアップのコア依存関係をインストールします:

npm install @apollo/server graphql
npm install @prisma/client
npm install -D prisma typescript ts-node @types/node

インストール後、プロジェクト内で Prisma を初期化します。Cursor のターミナル統合により、このプロセスがシームレスになり、セットアップ中に発生する設定の問題を AI がデバッグするのを助けてくれます。

ヒント

Cursor のインラインチャットを使用して、パッケージの互換性について質問してください。例えば、package.json を選択して、「これらの GraphQL 依存関係は最新の Apollo Server バージョンと互換性がありますか?」と尋ねると、Cursor はバージョンを分析し、更新を提案します。

AI アシスタンスを使用したスキーマ設計​

スキーマ設計は、あらゆる GraphQL API の基盤です。Cursor の AI 機能は、アプリケーションの要件に基づいて型、フィールド、および関係を提案することで、このプロセスをより直感的にします。

型定義の作成​

src/schema/typeDefs.ts でコア型を定義することから始めます。Cursor は、組み込みスカラー、ディレクティブ、およびカスタム型を含む GraphQL SDL(スキーマ定義言語)構文に対して、インテリジェントな自動補完を提供します。

export const typeDefs = `#graphql
type User {
id: ID!
email: String!
name: String
posts: [Post!]!
createdAt: String!
updatedAt: String!
}

type Post {
id: ID!
title: String!
content: String!
published: Boolean!
author: User!
createdAt: String!
updatedAt: String!
}

type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
post(id: ID!): Post
}

type Mutation {
createUser(email: String!, name: String): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
publishPost(id: ID!): Post!
deletePost(id: ID!): Post!
}
`;

Cursor で型定義を入力すると、エディタは GraphQL 構文を認識し、コンテキスト認識の提案を提供します。AI は型間の関係を理解し、新しいエンティティを定義する際に欠落しているフィールドを提案できます。

Prisma モデルからスキーマを生成する​

Cursor で最も強力なワークフローの一つは、Prisma モデルから直接 GraphQL スキーマを生成することです。prisma/schema.prisma でデータベーススキーマを定義した後、Cursor の AI チャットを使用して、これらのモデルを GraphQL 型定義に変換できます。

以下は Prisma スキーマの例です:

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

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

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

model Post {
id String @id @default(uuid())
title String
content String
published Boolean @default(false)
authorId String
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

Cursor で Prisma スキーマを選択し、AI チャット機能を使用して、「これらの Prisma モデルに対応する GraphQL 型定義を生成してください。Query 型と Mutation 型も含めてください。」のようなプロンプトを入力します。Cursor は、データベース構造を反映した正確な SDL を生成します。

警告

AI が生成したスキーマは、ビジネスロジックの要件がデータベースモデルに捉えられていない可能性があるため、常に確認してください。AI は構造を理解しますが、アプリケーションに必要なドメイン固有の検証ルールやカスタムスカラーを見落とす可能性があります。

Cursor でのリゾルバ実装​

リゾルバは、GraphQL API が機能する場所です。Cursor の TypeScript 統合により、型安全なリゾルバの作成が大幅に効率化されます。

型安全なリゾルバの記述​

完全な TypeScript サポートを備えたリゾルバファイルを作成します。Cursor はスキーマとリゾルバ型の関係を理解し、親オブジェクト、引数、およびコンテキストの自動補完を提供します。

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export const userResolver = {
Query: {
users: async () => {
return await prisma.user.findMany({
include: { posts: true },
});
},

user: async (_: unknown, { id }: { id: string }) => {
return await prisma.user.findUnique({
where: { id },
include: { posts: true },
});
},
},

User: {
posts: async (parent: { id: string }) => {
return await prisma.post.findMany({
where: { authorId: parent.id },
});
},
},

Mutation: {
createUser: async (
_: unknown,
{ email, name }: { email: string; name?: string }
) => {
return await prisma.user.create({
data: { email, name },
});
},
},
};

Cursor の IntelliSense は、スキーマ定義に基づいてリゾルバ引数に正確な型情報を提供します。パラメータや戻り値にマウスオーバーすると、TypeScript にマッピングされた正確な GraphQL 型が表示されます。

AI を使用した複雑なリゾルバの生成​

データ変換、集計、またはサードパーティ API 呼び出しを含む複雑なリゾルバロジックの場合、Cursor の AI 生成は非常に貴重です。スキーマの一部を選択し、対応するリゾルバ実装を Cursor に生成するように依頼します。

例えば、以下の高度なスキーマ定義が与えられた場合:

type Query {
searchPosts(query: String!, filters: PostFilters): SearchResult!
}

input PostFilters {
publishedOnly: Boolean
dateFrom: String
dateTo: String
authorIds: [ID!]
}

type SearchResult {
posts: [Post!]!
totalCount: Int!
hasMore: Boolean!
}

Cursor に「searchPosts のリゾルバを実装してください。提供された条件でフィルタリングし、デフォルトの制限 20 でページネーションをサポートし、totalCount と hasMore フラグを返します。」とプロンプトできます。AI は Prisma のフィルタリング API を使用して完全な実装を生成します。

searchPosts: async (
_: unknown,
{
query,
filters,
}: {
query: string;
filters?: {
publishedOnly?: boolean;
dateFrom?: string;
dateTo?: string;
authorIds?: string[];
};
},
context: { userId?: string }
) => {
const limit = 20;
const where: any = {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ content: { contains: query, mode: 'insensitive' } },
],
};

if (filters?.publishedOnly) {
where.published = true;
}

if (filters?.dateFrom || filters?.dateTo) {
where.createdAt = {};
if (filters.dateFrom) where.createdAt.gte = new Date(filters.dateFrom);
if (filters.dateTo) where.createdAt.lte = new Date(filters.dateTo);
}

if (filters?.authorIds?.length) {
where.authorId = { in: filters.authorIds };
}

const [posts, totalCount] = await Promise.all([
prisma.post.findMany({
where,
take: limit,
include: { author: true },
}),
prisma.post.count({ where }),
]);

return {
posts,
totalCount,
hasMore: totalCount > limit,
};
},
ヒント

複雑なリゾルバを生成する際は、常に Prisma クエリに N+1 問題がないか確認してください。Cursor の AI は、関連エンティティのバッチロードに DataLoader パターンを含まない、基本的な実装を生成することがよくあります。

Apollo Server 統合​

Cursor のアシスタンスにより、スキーマとリゾルバを Apollo Server に接続することは簡単です。エディタは、適切な TypeScript 型とミドルウェアを使用してサーバーを設定するのを助けます。

サーバーのセットアップ​

Apollo Server 4 の構成で server.ts ファイルを作成します:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema/typeDefs';
import { resolvers } from './schema/resolvers';

interface MyContext {
token?: string;
userId?: string;
}

const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});

const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }): Promise<MyContext> => {
const token = req.headers.authorization || '';
// ここに認証ロジックを追加
return { token };
},
});

console.log(`Server ready at: ${url}`);

Cursor は Apollo Server API の自動補完を提供し、構成全体で正しい型付けを維持するのを助けます。AI は、認証、ロギング、およびエラーハンドリングのミドルウェアパターンも提案できます。

カスタムスカラーとディレクティブの追加​

高度な GraphQL API では、特殊なデータ型にカスタムスカラーが必要になることがよくあります。Cursor は、完全な型安全性を持ってこれらを実装するのを助けます。

import { GraphQLScalarType, Kind } from 'graphql';

export const dateScalar = new GraphQLScalarType({
name: 'Date',
description: 'Custom Date scalar',
serialize(value: unknown) {
if (value instanceof Date) {
return value.toISOString();
}
throw Error('GraphQL Date Scalar serializer expected a Date object');
},
parseValue(value: unknown) {
if (typeof value === 'string') {
return new Date(value);
}
throw new Error('GraphQL Date Scalar parser expected a string');
},
parseLiteral(ast) {
if (ast.kind === Kind.STRING) {
return new Date(ast.value);
}
return null;
},
});

カスタムスカラーを定義する際、Cursor の TypeScript 言語サーバーは、シリアライズ関数とパース関数が正しい型を処理することを保証し、ランタイムエラーを防ぎます。

Prisma 統合のベストプラクティス​

Cursor での Prisma と GraphQL の組み合わせは、強力なフルスタック開発体験を生み出します。このセクションでは、これらのテクノロジーを効果的に統合するためのベストプラクティスをカバーします。

Prisma Client 型の生成​

Prisma スキーマを変更した後、generate コマンドを実行します。Cursor のターミナル統合は出力をキャプチャし、エラーがあれば AI がスキーマ構文の問題を修正するのを助けてくれます。

npx prisma generate

生成された Prisma Client 型は、Cursor の IntelliSense とシームレスに統合されます。リゾルバを記述する際、モデルフィールド、フィルタオプション、およびリレーションクエリの完全な自動補完が得られます。

DataLoader を使用したリレーションの処理​

GraphQL リゾルバでの N+1 クエリ問題を回避するために、DataLoader を Cursor プロジェクトに統合します。AI は、Prisma が効率的に実行するバッチロード関数のセットアップを助けてくれます。

import DataLoader from 'dataloader';
import { PrismaClient, User } from '@prisma/client';

const prisma = new PrismaClient();

export const createUserLoader = () => {
return new DataLoader<string, User | null>(async (userIds) => {
const users = await prisma.user.findMany({
where: { id: { in: [...userIds] } },
});

const userMap = new Map(users.map((user) => [user.id, user]));
return userIds.map((id) => userMap.get(id) || null);
});
};

サーバーコンテキストで、ローダーを初期化し、リゾルバに渡します:

interface MyContext {
userLoader: ReturnType<typeof createUserLoader>;
postLoader: ReturnType<typeof createPostLoader>;
}

const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async (): Promise<MyContext> => {
return {
userLoader: createUserLoader(),
postLoader: createPostLoader(),
};
},
});
警告

DataLoader インスタンスは、異なるユーザー間のキャッシング問題を避けるために、リクエストごとに作成する必要があります。Cursor の AI は、ローダーのシングルトンパターンを提案することがあります。必ず実装が各 GraphQL リクエストに対して新しいインスタンスを作成することを確認してください。

AI クエリ生成とテスト​

Cursor の目立つ機能の一つは、スキーマに基づいて GraphQL クエリを生成およびテストできることです。これにより、開発とデバッグのワークフローが加速されます。

スキーマからクエリを生成する​

Cursor で型定義を選択し、AI にサンプルクエリとミューテーションを生成するように依頼します。これは、利用可能な API サーフェスを理解する必要があるフロントエンド開発者にとって特に有用です。

上記の User および Post スキーマに対して、Cursor は以下を生成する可能性があります:

query GetUserWithPosts($userId: ID!) {
user(id: $userId) {
id
email
name
posts {
id
title
published
createdAt
}
}
}

mutation CreateNewPost($title: String!, $content: String!, $authorId: ID!) {
createPost(title: $title, content: $content, authorId: $authorId) {
id
title
author {
id
name
}
}
}

これらの生成されたクエリは、スキーマに対して完全に型付けされ検証されるため、フロントエンド統合エラーの可能性が減少します。

生成されたクエリを使用したリゾルバのテスト​

Cursor の AI を使用して、リゾルバの包括的なテストスイートを生成します。リゾルバ関数を選択し、「モック Prisma 呼び出しとエッジケースを含む、このリゾルバの Jest テストを生成してください。」とプロンプトします。

import { userResolver } from './userResolver';
import { PrismaClient } from '@prisma/client';

jest.mock('@prisma/client', () => {
const mockPrisma = {
user: {
findMany: jest.fn(),
findUnique: jest.fn(),
create: jest.fn(),
},
};
return { PrismaClient: jest.fn(() => mockPrisma) };
});

describe('User Resolver', () => {
const prisma = new PrismaClient();

beforeEach(() => {
jest.clearAllMocks();
});

it('should return all users with their posts', async () => {
const mockUsers = [
{ id: '1', email: '[email protected]', posts: [] },
{ id: '2', email: '[email protected]', posts: [] },
];
(prisma.user.findMany as jest.Mock).mockResolvedValue(mockUsers);

const result = await userResolver.Query.users();

expect(prisma.user.findMany).toHaveBeenCalledWith({
include: { posts: true },
});
expect(result).toEqual(mockUsers);
});

it('should return a single user by id', async () => {
const mockUser = { id: '1', email: '[email protected]', posts: [] };
(prisma.user.findUnique as jest.Mock).mockResolvedValue(mockUser);

const result = await userResolver.Query.user(null, { id: '1' });

expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: '1' },
include: { posts: true },
});
expect(result).toEqual(mockUser);
});
});

エラーハンドリングと検証​

堅牢な GraphQL API には包括的なエラーハンドリングが必要です。Cursor は、リゾルバ全体で一貫したエラーパターンを実装するのを助けます。

カスタムエラークラス​

Apollo の GraphQL エラータイプを拡張するカスタムエラークラスを定義します:

import { GraphQLError } from 'graphql';

export class AuthenticationError extends GraphQLError {
constructor(message: string = 'Not authenticated') {
super(message, {
extensions: { code: 'UNAUTHENTICATED', http: { status: 401 } },
});
}
}

export class ValidationError extends GraphQLError {
constructor(message: string) {
super(message, {
extensions: { code: 'VALIDATION_FAILED', http: { status: 400 } },
});
}
}

export class NotFoundError extends GraphQLError {
constructor(resource: string) {
super(`${resource} not found`, {
extensions: { code: 'NOT_FOUND', http: { status: 404 } },
});
}
}

Cursor の自動補完サポートを活用して、リゾルバでこれらのエラーを使用します:

user: async (_: unknown, { id }: { id: string }) => {
const user = await prisma.user.findUnique({
where: { id },
include: { posts: true },
});

if (!user) {
throw new NotFoundError('User');
}

return user;
},
ヒント

Apollo Server 用のエラープラグインを作成して、エラーをログに記録し、クライアント用にフォーマットします。Cursor は、カスタムエラークラスに基づいてこのプラグインを生成できます。

結論​

Cursor での GraphQL API 開発は、モダンな IDE のパワーとインテリジェントな AI アシスタンスを組み合わせます。スキーマ設計からリゾルバ実装、テスト、デプロイメントまで、Cursor は GraphQL 開発のあらゆる側面を効率化します。

このガイドの重要なポイント:

  • Cursor の AI を使用して、Prisma モデルと TypeScript 型からスキーマを生成する
  • 型安全なリゾルバ開発のために TypeScript 統合を活用する
  • N+1 クエリを防ぐために DataLoader パターンを実装する
  • スキーマ定義から自動的にテストとクエリを生成する
  • カスタムエラークラスで一貫したエラーハンドリングを維持する

GraphQL と Cursor を使った構築を続けるにつれて、大規模なスキーマのリファクタリング、データベースクエリの最適化、およびドキュメント生成のために AI チャット機能を試してください。人間の専門知識と AI アシスタンスの組み合わせは、堅牢で高性能、かつメンテナンス可能な API を生み出します。