如何自定义 GraphQL 请求

在开发过程中,有时需要自定义 GraphQL 请求来实现更灵活的数据查询和操作。本文将介绍两种主要的自定义 GraphQL 请求方式:手写 GraphQL 请求和调用平台 API。

方式一:手写 GraphQL 请求

手写 GraphQL 请求是一种直接编写查询或变更语句的方式,适用于更复杂或特定的业务需求。以下分别是 querymutation 请求的示例。

1. 手写 Query 请求

以下是一个自定义 query 请求的示例,用于查询某个资源的语言信息列表。

const customQuery = async () => {
  const query = `{
    resourceLangQuery {
      queryListByEntity(query: {active: ACTIVE, installState: true}) {
        id
        name
        active
        installState
        code
        isoCode
      }
    }
  }`;

  const result = await http.query('resource', query);
  this.list = result.data['resourceLangQuery']['queryListByEntity'];
};

说明:

  • query 语句定义了一个请求,查询 resourceLangQuery 下的语言信息。
  • 查询的条件是 activeinstallState,只返回符合条件的结果。
  • 查询结果包括 id、name、active、installState 等字段。

2. 手写 Mutation 请求

以下是一个 mutation 请求的示例,用于创建新的资源分类信息。


const customMutation = async () => {
  const code = Date.now()
  const name = `测试${code}`

  const mutation = `mutation {
    resourceTaxKindMutation {
      create(data: {code: "${code}", name: "${name}"}) {
        id
        code
        name
        createDate
        writeDate
        createUid
        writeUid
      }
    }
  }`;

  const res = await http.mutate('resource', mutation);
  console.log(res);
};

说明:

  • mutation 语句用于创建一个新的资源分类。
  • create 操作的参数是一个对象,包含 code 和 name 字段。
  • 返回值包括 id、createDate 等字段。

方式二:调用平台的 API

平台 API 提供了简化的 GraphQL 调用方法,可以通过封装的函数来发送 query 和 mutation 请求。这种方式减少了手写 GraphQL 语句的复杂性,更加简洁和易于维护。

1. 调用平台的 Mutation API

使用平台的 customMutation 方法可以简化 Mutation 请求。

/**
 * 自定义请求方法
 * @param modelModel 模型编码
 * @param method 方法名或方法对象
 * @param records 请求参数,可以是单体对象或者对象的列表
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customMutation = async (
  modelModel: string,
  method: string | { name: string; argumentName: string },
  records: ObjectValue | ListValue,
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<any>
调用代码示例
// 1.customMutation 调用示例
const createTaxKind = async () => {
  const response = await customMutation(
    '模型编码',
    '模型方法',
    { code: '003', name: '测试3' }
  );
  console.log(response);
};

const createTaxKind2 = async () => {
  const response = await customMutation(
    '模型编码',
    {name:  '方法名', argumentName: '参数名'},
    { code: '003', name: '测试3' }
  );
  console.log(response);
};

2. 调用平台的 Query API

普通查询数据方法customQuery

/**
 * 自定义查询方法
 * @param modelModel 模型编码
 * @param method 方法名
 * @param record 请求参数,可以是单体对象或者对象的列表
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customQuery = async <T>(
  modelModel: string,
  method: string,
  record: ObjectValue | ListValue | string = {},
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<T>
调用代码示例
const fetchResourceLanguages = async () => {
  const response = await customQuery(
    '模型编码',
    '模型方法',
    { active: true },
  );
  console.log(response);
};

自定义分页类型接口查询方法

/**
 * 自定义分页类型接口查询方法
 * @param modelModel 模型编码
 * @param methodName 方法名
 * @param option 查询条件
 * @param requestFields 请求的字段配置,不传就是解析record内的所有字段
 * @param responseFields 响应的字段配置,不传就是所有字段都返回
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const customQueryPage = async <T = Record<string, unknown>>(
  modelModel: string,
  methodName: string,
  option: IQueryPageOption,
  requestFields?: IModelField[],
  responseFields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<IQueryPageResult<T>>
调用代码示例
// 存储字段的rsql查询条件
const rsql = `num > 1 and name =like='关键字'`;
// 查询条件对象
const condition = new Condition(rsql);
// 非存储字段的queryData查询条件
const queryData = { type: 'B2C' };
condition.setConditionBodyData(queryData)
const option = {
  // 当前页码
  currentPage: 1,
  // 每页条数
  pageSize: 20,
  // 查询条件对象,也可以是rsql字符串
  condition,
  // 自定义排序
  sort: [{sortField: 'id', direction: EDirection.ASC} as ISort]
} as IQueryPageOption;

const variables = {
  // 当调用的接口有权限相关提示可以设置该属性
  path: getSessionPath()
};
const page = await customQueryPage(
  'demo.demoItem',
  'queryPage',
  option,
  [],
  undefined,
  variables,
  { maxDepth: 1 }
);

标准分页接口查询方法

/**
 * 标准分页接口查询方法
 * @param modelModel 模型编码
 * @param option 查询条件
 * @param fields 请求和响应字段配置,不传就取当前模型内的所有字段
 * @param variables 变量参数
 * @param context 上下文,其中的maxDepth属性表示查询的最大深度
 */
const queryPage = async <T = Record<string, unknown>>(
  modelModel: string,
  option: IQueryPageOption,
  fields?: IModelField[],
  variables?: ObjectValue,
  context: ObjectValue = {}
): Promise<IQueryPageResult<T>> => {
  // 内部实际调用的也是customQueryPage
  return customQueryPage(modelModel, 'queryPage', option, fields, fields, variables, context);
};
调用方法示例

参考customQueryPage的调用示例

查询方法关键类型的定义

/**
 * 分页查询条件
 */
interface IQueryPageOption {
  pageSize?: number;
  currentPage?: number;
  sort?: ISort | ISort[];
  condition?: Condition | string;
  // condition 和 record 只能二选一
  record?: ObjectValue | ListValue;
  maxDepth?: number;
}

interface IQueryPageResult<T> {
  content: T[];
  totalElements: number;
  size: number;
  totalPages: number;
}

通用注意事项

默认情况下,平台 API 请求只会查询两层,如果要查询第三层,则需要传递往下查询深度的context.maxDepth属性,maxDepth=1为一共查询两层,maxDepth=2为一共查询三层;在平台底层maxDepth是从0开始的,0代表了第一层级,所以为1的时候,就是查两层。

/**
 * 响应字段的配置,不配置会返回模型下所有字段,
 * 建议按需求配置需要返回哪些字段,
 * 下面的配置等同于手写gql的
 * user {
 *   id
 *   name
 * }
 */
const responseFields = [
      {
        name: 'user',
        ttype: ModelFieldType.ManyToOne,
        modelFields: [
          { name: 'id', ttype: ModelFieldType.Long },
          { name: 'name', ttype: ModelFieldType.String }
        ] as IModelField[]
      }
    ] as IModelField[];

customMutation(
    '模型编码',
    '模型方法',
    { code: '003', name: '测试3' },
    undefined,
    responseFields,
    undefined,
    {
      maxDepth: 2
    }
  )

customQuery(
    '模型编码',
    '模型方法',
    { active: true },
    undefined,
    responseFields,
    undefined,
    {
      maxDepth: 2
    }
  )

对比

  • 手写 GraphQL 请求适用于请求参数比较简单的请求
  • 调用平台 API 适用于请求参数过于复杂、手动很难写

调用平台 API的弊端

调用平台 API会导致gql的请求体很大,因为底层会把当前模型所有的字段都作为响应体返回。如果请求的层级越深,那么gql请求体越大。

如果想通过手写Graphql的方法拼接复杂的请求参数,可以参考这边文章,里面有详细的讲解

Oinone社区 作者:汤乾华原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/17638.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
汤乾华的头像汤乾华数式员工
上一篇 2024年9月21日 pm4:17
下一篇 2024年9月26日 am9:19

相关推荐

  • 【前端】登录页面扩展点

    登录页面扩展点 场景 1: 登录之前需要二次确认框2: 前端默认的错误提示允许被修改3: 后端返回的错误提示允许被修改4: 登录后跳转到自定义的页面 方案 前端默认错误可枚举 errorMessages: { loginEmpty: '用户名不能为空', passwordEmpty: '密码不能为空', picCodeEmpty: '图形验证码不能为空', phoneEmpty: '手机号不能为空', verificationCodeEmpty: '验证码不能为空', picCodeError: '图形验证码错误', inputVerificationCodeAlign: '请重新输入验证码' } 登录按钮添加拓展点beforeClick、afterClick 代码 新增一个ts文件,继承平台默认的LoginPageWidget @SPI.ClassFactory(RouterWidget.Token({ widget: 'Login' })) export class CustomLoginPageWidget extends LoginPageWidget { constructor() { super(); // 修改前端默认的错误文案 this.errorMessages.loginEmpty = '登录用户名不能为空'; } /** * 用来处理点击「登录」之前的事件,可以做二次确定或者其他的逻辑 * 只有return true,才会继续往下执行 */ public beforeClick(): Promise<Boolean | null | undefined> { return new Promise((resolve) => { Modal.confirm({ title: '提示', content: '是否登录?', onOk: () => { resolve(true); } }); }); } /** * * @param result 后端接口返回的数据 * * 用来处理「登录」接口调用后的逻辑,可以修改后端返回的错误文案,也可以自定义 * * 只有return true,才会执行默认的跳转事件 */ public afterClick(result): Promise<any | null | undefined> { // if(result.redirect) { // 自定义跳转 //return false //} if (result.errorCode === 20060023) { result.errorMsg = '手机号不对,请联系管理员'; } return result; } }

    2023年11月1日
    1.1K00
  • 前端-如何修改指定页面的内组件的css样式

    为组件加自定义class,用该class作为父选择器写特定的css样式 以form为例,自定义了以下class <view/>标签的表单视图(FormView)组件 <element/>标签的form(FormWidget)组件 <element/>标签的actionBar(ActionBarWidget)组件 import { registerLayout, ViewType } from '@kunlun/dependencies'; export const install = () => { registerLayout( ` <view type="FORM" class="my-form-view"> <element widget="form" slot="form" class="my-form-widget"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" class="my-action-bar" slotSupport="action" > <xslot name="actions" slotSupport="action" /> </element> </view> `, { viewType: ViewType.Form, model: 'resource.k2.Model0000000109', actionName: 'uiViewb2de116be1754ff781e1ffa8065477fa' } ); }; install(); 查看修改后的页面html结构 编写样式的css .my-form-view .oio-form { /** TODO **/ } .my-form-widget .oio-row { /** TODO **/ } .my-action-bar .oio-col { /** TODO **/ }

    2024年6月17日
    1.0K00
  • 表格主题配置(v4)

    TableThemeConfig /** * 表格主题配置 */ export interface TableThemeConfig { border: boolean | string; stripe: boolean; isCurrent: boolean; isHover: boolean; /** * 表格列主题配置 */ column: Partial<TableColumnThemeConfig>; } /** * 表格列主题配置 */ export interface TableColumnThemeConfig { /** * <h3>最小宽度</h3> * <ul> * <li>boolean: enabled column width auto compute</li> * <li>number: using css width (default: px)</li> * <li>string: using css width</li> * <li> * object: auto compute width for label by default function * <ul> * <li>min: min min width (default: 120)</li> * <li>max: max min width (default: 432)</li> * <li>chineseWidth: chinese width (default: 14 -> fontSize: 14px)</li> * <li>otherWidth: non chinese width (default: 9 -> fontSize: 14px)</li> * <li>sortableFixWidth: sortable handle width (default: 40)</li> * <li>nonSortableFixWidth: non sortable fix width (default: 22)</li> * </ul> * </li> * <li>function: auto compute width for label by function</li> * </ul> */ minWidth: boolean | number | string | Partial<TableColumnMinWidthComputeConfig> | TableColumnMinWidthComputeFunction; /** * 操作列 */ operation: { /** * 宽度 (default: 165) */ width?: number | string; /** * 最小宽度 (default: 120) */ minWidth?: number | string; }; } export interface TableColumnMinWidthComputeConfig { min: number;…

    2023年11月6日
    92200
  • 列表页内上下文无关的动作如何添加自定义上下文

    场景 在界面设计器,可以配置当前列表页从上个页面带的上下文参数,现在需要传递这个上下文到下个页面,设计器没有配置入口,我们可以通过自定义改动作来解决 示例代码 import { ActionType, ActionWidget, RouterViewActionWidget, SPI, ViewActionTarget } from '@kunlun/dependencies'; @SPI.ClassFactory( ActionWidget.Token({ actionType: [ActionType.View], target: [ViewActionTarget.Router], // 模型编码 model: 'module.model', // 动作名称 name: 'actionName' }) ) export class DemoRouterViewActionWidget extends RouterViewActionWidget { protected async clickAction(): Promise<void> { // initialContext内是上个页面传来的上下文,手动将值传递到下个页面的上下文 // 这里假设需要传递的字段名为type this.action.context = { type: this.initialContext.type }; return super.clickAction(); } }

    2024年8月20日
    1.2K00
  • 表格主题配置(v4)

    TableThemeConfig /** * 表格主题配置 */ export interface TableThemeConfig { border: boolean | string; stripe: boolean; isCurrent: boolean; isHover: boolean; /** * 表格列主题配置 */ column: Partial<TableColumnThemeConfig>; } /** * 表格列主题配置 */ export interface TableColumnThemeConfig { /** * <h3>最小宽度</h3> * <ul> * <li>boolean: enabled column width auto compute</li> * <li>number: using css width (default: px)</li> * <li>string: using css width</li> * <li> * object: auto compute width for label by default function * <ul> * <li>min: min min width (default: 120)</li> * <li>max: max min width (default: 432)</li> * <li>chineseWidth: chinese width (default: 14 -> fontSize: 14px)</li> * <li>otherWidth: non chinese width (default: 9 -> fontSize: 14px)</li> * <li>sortableFixWidth: sortable handle width (default: 40)</li> * <li>nonSortableFixWidth: non sortable fix width (default: 22)</li> * </ul> * </li> * <li>function: auto compute width for label by function</li> * </ul> */ minWidth: boolean | number | string | Partial<TableColumnMinWidthComputeConfig> | TableColumnMinWidthComputeFunction; /** * 操作列 */ operation: { /** * 宽度 (default: 165) */ width?: number | string; /** * 最小宽度 (default: 120) */ minWidth?: number | string; }; } export interface TableColumnMinWidthComputeConfig { min: number;…

    2023年11月1日
    93800

Leave a Reply

登录后才能评论