Một GraphQL fetcher cho web là một field GraphQL được định nghĩa bằng metadata schema và đoạn JavaScript xử lý dữ liệu. Thay vì sửa Java code và deploy lại, developer có thể dùng MCP tool để tạo draft, kiểm tra schema, validate script, lưu bản nháp, rồi publish khi sẵn sàng. Fetcher sau khi publish sẽ được gọi từ website qua endpoint POST /graphql.

Kiến Trúc Tổng Quát

flowchart LR
    A[AI / Developer] --> B[MCP tools]
    B --> C[Tạo draft fetcher]
    C --> D[Viết schema JSON]
    C --> E[Viết JavaScript content]
    D --> F[Validate và preview schema]
    E --> F
    F --> G[Lưu draft hoặc publish]
    G --> H[Web GraphQL runtime]
    H --> I[POST /graphql]
    I --> J[JavaScript fetcher chạy bằng Rhino]
    J --> K[Trả response cho frontend]
Một fetcher web có bốn phần chính:
  • slug: tên field GraphQL, ví dụ latest_posts.
  • group: nhóm GraphQL, mặc định thường là default.
  • summary: JSON mô tả arguments và response schema.
  • content: JavaScript được runtime thực thi khi field được gọi.
Fetcher web khác fetcher admin. Web fetcher phục vụ website visitor tại /graphql, nên khi cần gọi bean/service hãy ưu tiên các bean scope web, ví dụ webPostService, webGraphQLDataService. Không dùng bean admin trong web fetcher vì có thể vô tình bỏ qua rule hiển thị public.

Workflow Với MCP Tool

Quy trình nên đi theo thứ tự này:
sequenceDiagram
    participant Dev as Developer / AI
    participant MCP as MCP Server
    participant Store as Post-based Fetcher Store
    participant Web as Web GraphQL Runtime

    Dev->>MCP: read guide://graphql-fetcher-authoring
    Dev->>MCP: get_graphql_fetcher_summary_schema
    Dev->>MCP: get_graphql_fetcher_runtime_catalog
    Dev->>MCP: list_graphql_fetchers
    Dev->>MCP: create_graphql_fetcher_draft
    Dev->>MCP: validate_graphql_fetcher
    Dev->>MCP: preview_graphql_fetcher_schema
    Dev->>MCP: save_graphql_fetcher publish=false/true
    MCP->>Store: Lưu summary + content
    Web->>Store: Nạp fetcher đã publish
    Dev->>Web: POST /graphql
Các tool quan trọng:
  • get_graphql_fetcher_summary_schema: lấy format JSON hợp lệ cho summary.
  • get_graphql_fetcher_runtime_catalog: xem runtime JavaScript có global nào, nên gọi bean nào, endpoint client là gì.
  • get_web_singleton_schema: lấy schema singleton web để tra bean/method có thật trước khi dùng getBean.
  • list_graphql_fetchers: kiểm tra slug đã tồn tại hay chưa.
  • get_graphql_fetcher_by_name: xem lại fetcher cũ nếu slug đã tồn tại.
  • create_graphql_fetcher_draft: tạo draft deterministic gồm slug, summary, content, schemaPreview, exampleQuery.
  • validate_graphql_fetcher: kiểm tra schema và content.
  • preview_graphql_fetcher_schema: xem SDL preview trước khi lưu.
  • save_graphql_fetcher: lưu draft hoặc publish fetcher web.

Tạo Draft Fetcher

Ví dụ muốn tạo field latest_posts nhận limit và trả về danh sách bài viết:
{
  "queryName": "latest_posts",
  "group": "content",
  "description": "List latest published posts for website",
  "responseTypeName": "latest_posts_response",
  "arguments": [
    { "name": "limit", "type": "integer", "description": "Maximum number of posts" }
  ],
  "responseProperties": [
    {
      "name": "posts",
      "type": "array",
      "items": {
        "name": "post",
        "type": "object",
        "properties": [
          { "name": "id", "type": "long" },
          { "name": "title", "type": "string" },
          { "name": "slug", "type": "string" },
          { "name": "summary", "type": "string" },
          { "name": "publishedAt", "type": "long" }
        ]
      }
    }
  ]
}
create_graphql_fetcher_draft sẽ trả về draft ban đầu. Sau đó chỉnh content để gọi service thật. Runtime chạy trên Rhino JavaScript, không phải Node.js, nên không dùng require, import, npm package hoặc async/await.

Viết JavaScript Content

Trong web fetcher, đọc argument bằng queryArguments.get('name'). Không dùng queryArguments.namequeryArguments là Java Map.
Ví dụ content:
var limit = queryArguments.get('limit') || 10;

var postService = getBean('webPostService');
if (!postService) {
    throw 'webPostService bean is not available';
}

var posts = postService
    .getLatestPublishedPostsByTypeSortByPublishedAtDescIdDesc(
        'POST',
        limit
    );

var items = [];
for (var i = 0; i < posts.size(); ++i) {
    var post = posts.get(i);
    items.push({
        id: post.getId(),
        title: post.getTitle(),
        slug: post.getSlug(),
        summary: post.getSummary(),
        publishedAt: post.getPublishedAt()
    });
}

({
    posts: items
});
Các global hữu ích trong runtime:
  • queryArguments: arguments của GraphQL field.
  • query: định nghĩa query hiện tại.
  • requestArguments: dữ liệu request-level.
  • userId: ID user hiện tại, null hoặc 0 nếu chưa đăng nhập.
  • userRoles: role proxy của user hiện tại.
  • getBean(name): lấy bean theo lower-camel name.
  • properties: application properties.
  • logger: ghi log theo level.

Validate Và Lưu

Trước khi lưu, luôn gọi:
validate_graphql_fetcher
preview_graphql_fetcher_schema
Nếu hợp lệ, gọi save_graphql_fetcher.
Dùng publish=false để lưu bản nháp:
{
  "slug": "latest_posts",
  "group": "content",
  "summary": "{...}",
  "content": "...",
  "publish": false
}
Chỉ dùng publish=true khi đã muốn field xuất hiện ở web runtime. Runtime web chỉ nạp fetcher đã publish; draft chưa được gọi từ /graphql.

Gọi Fetcher Từ Frontend

Endpoint web là:
POST /graphql
Content-Type: application/json
Ví dụ browser fetch:
fetch('/graphql', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        query: '{ latest_posts(limit: $limit) { * } }',
        variables: {
            limit: 5
        }
    })
});
Lưu ý cú pháp query: parser custom không hỗ trợ khai báo kiểu biến kiểu query($limit: Int). Hãy truyền biến qua variables và tham chiếu trực tiếp bằng $limit trong argument. Dạng { * } là cách được khuyến nghị để lấy toàn bộ field response.

Bảo Mật Và Giới Hạn

Authentication đi theo session/cookie bình thường của website. Nếu fetcher cần user đăng nhập, hãy kiểm tra userId trong script. Nếu cần kiểm tra quyền, null-check userRoles trước khi gọi isAccessible.
Fetcher web nên dùng service web để giữ đúng visibility rule public. Không trộn admin service vào web fetcher. Ngoài ra, MCP tool giúp tạo, validate và lưu fetcher, nhưng không biến GraphQL thành REST API riêng; frontend vẫn gọi qua /graphql.

Kết Luận

MCP tool biến việc tạo GraphQL fetcher thành một workflow có kiểm soát: đọc contract, kiểm tra schema runtime, tạo draft, sửa JavaScript, validate, preview, rồi lưu draft hoặc publish. Cách làm này giúp tạo field GraphQL nhanh cho web mà vẫn giữ được ranh giới runtime, bảo mật, và khả năng review trước khi đưa field ra production.