GraphQL API开发 logo

GraphQL API开发

Organization
microwind
GraphQL API开发

当开发GraphQL API时,分析查询策略,优化API性能,解决数据获取问题。验证GraphQL架构,设计查询模式,和最佳实践。

Overview

Publishermicrowind
Repositoryai-skills
Skill nameGraphQL API开发
Stars
93
Forks
25
Bundled files
2
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 2 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by microwind on GitHub. Read the source before you install it.

Installation

Install the GraphQL API开发 AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/microwind/ai-skills/tree/main/backend/graphql-api
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/microwind/ai-skills.git /tmp/ai-skills
mkdir -p .claude/skills
cp -r /tmp/ai-skills/backend/graphql-api .claude/skills/microwind-graphql-api
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable GraphQL API开发 in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads GraphQL API开发 on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so GraphQL API开发 is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

GraphQL API开发技能

概述

GraphQL是一种现代化的API查询语言,提供了比REST更灵活的数据获取方式。不当的GraphQL设计会导致性能问题、安全漏洞和复杂性增加。需要建立完善的GraphQL开发规范。

核心原则: 好的GraphQL设计应该高效、安全、易于维护。坏的GraphQL设计会导致N+1查询问题、性能下降和安全风险。

何时使用

始终:

  • 设计新GraphQL API时
  • 优化GraphQL查询性能时
  • 解决N+1查询问题时
  • 设计GraphQL schema时
  • 处理GraphQL安全问题时
  • 实现GraphQL订阅时

触发短语:

  • "GraphQL查询太慢了"
  • "如何设计GraphQL schema?"
  • "N+1查询问题怎么解决?"
  • "GraphQL安全怎么保证?"
  • "如何实现GraphQL订阅?"
  • "GraphQL和REST怎么选?"

GraphQL API技能功能

Schema设计分析

  • 类型定义设计
  • 查询和变更定义
  • 订阅类型设计
  • 接口和联合类型
  • 输入类型设计

查询优化策略

  • N+1查询问题解决
  • 数据加载器优化
  • 查询复杂度分析
  • 分页策略设计
  • 缓存机制实现

安全检查机制

  • 查询深度限制
  • 查询复杂度限制
  • 查询超时设置
  • 权限验证
  • 敏感数据保护

常见GraphQL设计问题

N+1查询问题

问题:
查询列表时,每个项目都触发额外的数据库查询

后果:
- 数据库压力大
- 响应时间长
- 性能线性下降
- 服务器资源浪费

解决方案:
- 数据加载器(DataLoader)
- 批量查询优化
- 预加载关联数据
- 查询合并策略

查询复杂度问题

问题:
客户端发送过于复杂的嵌套查询

后果:
- 服务器性能下降
- 内存占用过高
- 响应超时
- 拒绝服务攻击风险

解决方案:
- 查询深度限制
- 复杂度分析
- 查询超时设置
- 白名单字段限制

Schema设计问题

问题:
Schema设计过于复杂或不一致

后果:
- 开发困难
- 维护成本高
- 客户端困惑
- 版本管理困难

解决方案:
- 简化Schema设计
- 统一命名规范
- 模块化设计
- 版本控制策略

GraphQL Schema设计

基础Schema结构

graphql
# 用户类型
type User {
  id: ID!
  username: String!
  email: String!
  profile: Profile
  posts: [Post!]!
  createdAt: DateTime!
}

# 用户资料类型
type Profile {
  id: ID!
  firstName: String!
  lastName: String!
  avatar: String
  bio: String
}

# 文章类型
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  tags: [String!]!
  createdAt: DateTime!
}

# 查询根类型
type Query {
  user(id: ID!): User
  users(limit: Int, offset: Int): [User!]!
  post(id: ID!): Post
  posts(limit: Int, offset: Int): [Post!]!
  search(query: String!): [SearchResult!]!
}

# 变更根类型
type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  createPost(input: CreatePostInput!): Post!
  deletePost(id: ID!): Boolean!
}

# 订阅根类型
type Subscription {
  postCreated: Post!
  userUpdated(userId: ID!): User!
}

# 输入类型
input CreateUserInput {
  username: String!
  email: String!
  password: String!
  profile: ProfileInput
}

input ProfileInput {
  firstName: String!
  lastName: String!
  avatar: String
  bio: String
}

# 联合类型
union SearchResult = User | Post | Comment

# 枚举类型
enum UserRole {
  ADMIN
  MODERATOR
  USER
}

# 标量类型
scalar DateTime
scalar Upload

接口设计

graphql
# 可评论接口
interface Commentable {
  id: ID!
  comments: [Comment!]!
}

# 实现接口
type Post implements Commentable {
  id: ID!
  title: String!
  content: String!
  comments: [Comment!]!
}

type Video implements Commentable {
  id: ID!
  title: String!
  url: String!
  duration: Int!
  comments: [Comment!]!
}

代码实现示例

Python GraphQL实现

python
import graphene
from graphene_django import DjangoObjectType
from django.contrib.auth.models import User
from datetime import datetime
import promise

class ProfileType(graphene.ObjectType):
    id = graphene.ID()
    first_name = graphene.String()
    last_name = graphene.String()
    avatar = graphene.String()
    bio = graphene.String()

class UserType(DjangoObjectType):
    profile = graphene.Field(ProfileType)
    posts = graphene.List('PostType')
    
    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'date_joined')
    
    def resolve_profile(self, info):
        if hasattr(self, 'profile'):
            return self.profile
        return None
    
    def resolve_posts(self, info):
        # 使用DataLoader解决N+1问题
        return info.context.loaders.post_loader.load(self.id)

class PostType(graphene.ObjectType):
    id = graphene.ID()
    title = graphene.String()
    content = graphene.String()
    author = graphene.Field(UserType)
    comments = graphene.List('CommentType')
    tags = graphene.List(graphene.String)
    created_at = graphene.DateTime()
    
    def resolve_author(self, info):
        return info.context.loaders.user_loader.load(self.author_id)
    
    def resolve_comments(self, info):
        return info.context.loaders.comment_loader.load(self.id)

class Query(graphene.ObjectType):
    user = graphene.Field(UserType, id=graphene.ID(required=True))
    users = graphene.List(UserType, limit=graphene.Int(), offset=graphene.Int())
    post = graphene.Field(PostType, id=graphene.ID(required=True))
    posts = graphene.List(PostType, limit=graphene.Int(), offset=graphene.Int())
    
    def resolve_user(self, info, id):
        return info.context.loaders.user_loader.load(int(id))
    
    def resolve_users(self, info, limit=None, offset=None):
        queryset = User.objects.all()
        if offset:
            queryset = queryset[offset:]
        if limit:
            queryset = queryset[:limit]
        return queryset
    
    def resolve_post(self, info, id):
        return info.context.loaders.post_loader.load(int(id))
    
    def resolve_posts(self, info, limit=None, offset=None):
        queryset = Post.objects.all()
        if offset:
            queryset = queryset[offset:]
        if limit:
            queryset = queryset[:limit]
        return queryset

class CreateUser(graphene.Mutation):
    user = graphene.Field(UserType)
    success = graphene.Boolean()
    errors = graphene.List(graphene.String)
    
    class Arguments:
        username = graphene.String(required=True)
        email = graphene.String(required=True)
        password = graphene.String(required=True)
        first_name = graphene.String()
        last_name = graphene.String()
    
    def mutate(self, info, username, email, password, first_name=None, last_name=None):
        try:
            user = User.objects.create_user(
                username=username,
                email=email,
                password=password
            )
            
            # 创建用户资料
            if first_name or last_name:
                Profile.objects.create(
                    user=user,
                    first_name=first_name,
                    last_name=last_name
                )
            
            return CreateUser(user=user, success=True, errors=[])
        
        except Exception as e:
            return CreateUser(user=None, success=False, errors=[str(e)])

class Mutation(graphene.ObjectType):
    create_user = CreateUser.Field()

# DataLoader实现
from promise import Promise
from promise.dataloader import DataLoader

class UserLoader(DataLoader):
    def batch_load_fn(self, keys):
        users = User.objects.filter(id__in=keys)
        user_dict = {user.id: user for user in users}
        return Promise.resolve([user_dict.get(key) for key in keys])

class PostLoader(DataLoader):
    def batch_load_fn(self, keys):
        posts = Post.objects.filter(author_id__in=keys)
        posts_dict = {}
        for post in posts:
            posts_dict.setdefault(post.author_id, []).append(post)
        return Promise.resolve([posts_dict.get(key, []) for key in keys])

# 查询复杂度限制
from graphql.validation.rules import QueryComplexityRule
from graphql import GraphQLSchema, validate

class QueryComplexityLimiter:
    def __init__(self, max_complexity=100):
        self.max_complexity = max_complexity
    
    def analyze_query(self, query, schema):
        complexity = 0
        
        def calculate_complexity(node, key=None):
            nonlocal complexity
            if hasattr(node, 'selection_set'):
                for selection in node.selection_set.selections:
                    complexity += 1
                    calculate_complexity(selection)
        
        try:
            import graphql
            ast = graphql.parse(query)
            calculate_complexity(ast)
        except Exception:
            pass
        
        return complexity <= self.max_complexity

# GraphQL视图
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json

@csrf_exempt
def graphql_view(request):
    if request.method == 'GET':
        return JsonResponse({'message': 'GraphQL API'})
    
    if request.method == 'POST':
        try:
            data = json.loads(request.body)
            query = data.get('query')
            variables = data.get('variables', {})
            
            # 查询复杂度检查
            complexity_limiter = QueryComplexityLimiter(max_complexity=100)
            if not complexity_limiter.analyze_query(query, schema):
                return JsonResponse({
                    'errors': [{'message': 'Query complexity too high'}]
                }, status=400)
            
            # 设置DataLoader
            loaders = {
                'user_loader': UserLoader(),
                'post_loader': PostLoader(),
                'comment_loader': CommentLoader()
            }
            
            context = {'loaders': loaders}
            result = schema.execute(query, variables=variables, context=context)
            
            return JsonResponse({
                'data': result.data,
                'errors': [str(error) for error in result.errors] if result.errors else None
            })
        
        except Exception as e:
            return JsonResponse({
                'errors': [{'message': str(e)}]
            }, status=500)

schema = graphene.Schema(query=Query, mutation=Mutation)

JavaScript Apollo Client实现

javascript
import { ApolloClient, InMemoryCache, gql, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

// HTTP链接
const httpLink = createHttpLink({
  uri: 'http://localhost:8000/graphql/',
});

// 认证链接
const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('authToken');
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    }
  }
});

// Apollo客户端
const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
  defaultOptions: {
    watchQuery: {
      errorPolicy: 'all',
    },
    query: {
      errorPolicy: 'all',
    },
  },
});

// GraphQL查询
const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      username
      email
      profile {
        firstName
        lastName
        avatar
      }
      posts {
        id
        title
        content
        createdAt
      }
    }
  }
`;

const GET_USERS = gql`
  query GetUsers($limit: Int, $offset: Int) {
    users(limit: $limit, offset: $offset) {
      id
      username
      email
      profile {
        firstName
        lastName
      }
    }
  }
`;

const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      username
      email
      profile {
        firstName
        lastName
      }
    }
  }
`;

// 查询函数
export const getUser = async (id) => {
  try {
    const response = await client.query({
      query: GET_USER,
      variables: { id },
    });
    return response.data.user;
  } catch (error) {
    console.error('获取用户失败:', error);
    throw error;
  }
};

export const getUsers = async (variables = {}) => {
  try {
    const response = await client.query({
      query: GET_USERS,
      variables,
    });
    return response.data.users;
  } catch (error) {
    console.error('获取用户列表失败:', error);
    throw error;
  }
};

export const createUser = async (input) => {
  try {
    const response = await client.mutate({
      mutation: CREATE_USER,
      variables: { input },
    });
    return response.data.createUser;
  } catch (error) {
    console.error('创建用户失败:', error);
    throw error;
  }
};

// React Hook
import { useQuery, useMutation } from '@apollo/client';

export const useUser = (id) => {
  const { loading, error, data } = useQuery(GET_USER, {
    variables: { id },
    skip: !id,
  });
  
  return {
    user: data?.user,
    loading,
    error,
  };
};

export const useCreateUser = () => {
  const [createUserMutation, { loading, error }] = useMutation(CREATE_USER, {
    update: (cache, { data: { createUser } }) => {
      // 更新缓存
      cache.modify({
        fields: {
          users: (existing = []) => [...existing, createUser]
        }
      });
    },
  });
  
  const createUser = async (input) => {
    const result = await createUserMutation({
      variables: { input },
    });
    return result.data.createUser;
  };
  
  return {
    createUser,
    loading,
    error,
  };
};

// 分页Hook
export const useUsersPagination = (pageSize = 10) => {
  const [page, setPage] = useState(0);
  const [users, setUsers] = useState([]);
  
  const { loading, error, data, fetchMore } = useQuery(GET_USERS, {
    variables: {
      limit: pageSize,
      offset: page * pageSize,
    },
  });
  
  useEffect(() => {
    if (data?.users) {
      if (page === 0) {
        setUsers(data.users);
      } else {
        setUsers(prev => [...prev, ...data.users]);
      }
    }
  }, [data, page]);
  
  const loadMore = () => {
    if (!loading && data?.users?.length === pageSize) {
      setPage(prev => prev + 1);
    }
  };
  
  return {
    users,
    loading,
    error,
    loadMore,
    hasMore: data?.users?.length === pageSize,
  };
};

性能优化策略

DataLoader优化

  1. 批量查询: 将多个单独查询合并为批量查询
  2. 缓存机制: 在请求范围内缓存查询结果
  3. 预加载: 根据访问模式预加载相关数据
  4. 延迟加载: 按需加载数据,减少不必要查询

查询优化

  1. 字段选择: 只查询需要的字段
  2. 分页限制: 限制返回数据量
  3. 查询分析: 分析查询性能瓶颈
  4. 索引优化: 优化数据库索引

安全最佳实践

查询限制

  1. 深度限制: 限制查询嵌套深度
  2. 复杂度限制: 限制查询复杂度
  3. 超时设置: 设置查询执行超时
  4. 频率限制: 限制查询频率

权限控制

  1. 字段级权限: 控制字段访问权限
  2. 类型级权限: 控制类型访问权限
  3. 操作权限: 控制查询、变更、订阅权限
  4. 数据过滤: 过滤敏感数据

相关技能

  • api-validator - API接口验证和设计
  • database-query-analyzer - 数据库查询性能分析
  • security-scanner - 安全漏洞扫描
  • caching-strategies - 缓存策略和实现

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the GraphQL API开发 AI skill do?

当开发GraphQL API时,分析查询策略,优化API性能,解决数据获取问题。验证GraphQL架构,设计查询模式,和最佳实践。

Why use GraphQL API开发 on TypingMind?

Because you install it once and use it with any model. GraphQL API开发 is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install GraphQL API开发 in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/microwind/ai-skills/tree/main/backend/graphql-api. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use GraphQL API开发?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with GraphQL API开发?

As many as you like. As long as a model supports skills, you can use GraphQL API开发 with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the GraphQL API开发 AI skill free?

Yes. It is published on GitHub by microwind under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇