如何自定义 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: 元数据 视图的元数据 在日常开发中,我们会经常遇到自定义的字段、动作、视图需要界面设计器配置的数据,这些数据可能是当前页面的字段,也有可能动作,那么如何获取呢? 视图元数据分为两种:1: 当前视图(metadataRuntimeContext)2: 根视图(rootRuntimeContext) 那么这两种类型怎么区分呢? 举个例子:1: 如果当前字段是在表单中,那么当前视图就是表单,根视图就表单的父级视图,如果只有一个表单视图,那么当前视图就是根视图。2: 如果当前视图是表单,但是表单里面有个表格,对于表格字段而言,当前视图就是表格,根视图就是表单。 当前视图的元数据(metadataRuntimeContext) 在前端,我们通过 metadataRuntimeContext 来获取视图的元数据,例如: export class CustomFormStringFieldSingleWidget extends FormStringFieldSingleWidget { protected mounted(): void { console.log(this.metadataRuntimeContext); } /** * 界面设计器配置的动作 */ @Widget.Reactive() protected get modelActions() { return this.metadataRuntimeContext.model.modelActions } /** * 界面设计器配置的字段 */ @Widget.Reactive() protected get modelFields() { return this.metadataRuntimeContext.model.modelFields } } 属性名 类型 可选性 描述 viewAction RuntimeViewAction 是 运行时跳转动作(通过跳转动作创建的运行时上下文具备该属性) module RuntimeModule 否 运行时模块 model RuntimeModel 否 运行时模型 view RuntimeView 否 运行时视图 viewLayout DslDefinition \| undefined 否 视图布局 DSL,从运行时视图解析获得 viewDsl DslDefinition \| undefined 否 视图模板 DSL,从运行时视图解析获得 viewTemplate DslDefinition 否 视图最终执行 DSL,从运行时视图解析获得或根据布局 DSL 和模板 DSL 合并生成 getModel (model: string, isBelong?: boolean) => GetModelResult \| undefined 否 获取模型,返回获取的模型和所在的运行时上下文 getModelField (data: string, isBelong?: boolean) => GetModelFieldResult \| undefined 否 获取模型字段,返回获取的模型字段和所在的运行时上下文 getRequestModelFields (options?: GetRequestModelFieldsOptions) => RequestModelField[] 否 获取请求字段 getDefaultValue () => Promise<Record<string, unknown>> 否 获取默认值 getInitialValue () => Promise<Record<string, unknown>> 否 获取初始值 运行时模型(model) 属性名 类型 可选性 描述 id string 是 模型 id model string 否 模型编码 name string 否 技术名称 modelFields RuntimeModelField[] 否 模型字段 modelActions RuntimeAction[] 否 模型动作 type ModelType 是 模型类型 module string 是 模块编码 moduleName string 否 模块名称 moduleDefinition RuntimeModule 是…

    2024年10月8日
    3.0K00
  • 【路由】浏览器地址栏url参数介绍

    介绍 浏览器地址栏url为路由类型的视图动作(viewAction)的访问url 详情页示例url https://one.oinone.top/page;module=resource;viewType=DETAIL;model=resource.ResourceDistrict;action=redirectDetailPage;scene=redirectDetailPage;target=ROUTER;menu=%7B%22selectedKeys%22:%5B%22%E5%8C%BA%22%5D,%22openKeys%22:%5B%22%E5%9C%B0%E5%9D%80%E5%BA%93%22,%22%E5%9C%B0%E5%8C%BA%22%5D%7D;id=575733837679260950;path=%2Fresource%2F%E5%8C%BA%2FACTION%23resource.ResourceDistrict%23redirectDetailPage 通过调试工具查看解析后的信息 参数介绍 module 动作所在模块名称 viewType 视图类型 model 动作所在模型的编码 action 动作名称 target 动作打开方式,ROUTER为当前路由打开,OPEN_WINDOW为新窗口打开 menu 【选填】菜单栏控制参数,该参数不影响页面的业务逻辑,仅影响菜单栏展开哪些菜单项(通过openKeys属性),选中哪些菜单项(通过selectedKeys属性)),该参数经过JSON.stringify(menu)方式处理过 # 示例参数 { "selectedKeys": ["区"], "openKeys": ["地址库", "地区"] } id 【选填】详情、编辑等单行数据页面的数据id searchBody 列表页搜索区域的搜索条件,该参数在前端经过encodeURIComponent(JSON.stringify(searchBody))方式处理过 # 示例参数 { "code": "11" } searchConditions 列表页高级搜索条件,用于处理searchBody之外的复杂搜索条件,日常开发中无需关心该参数encodeURIComponent(JSON.stringify(searchConditions))方式处理过 # 示例参数 [ { "leftValue":["sourceType"], "operator":"==", "right":"GD" } ] context 上下文参数,该参数经过JSON.stringify(menu)方式处理过 列表页的此参数会填充到搜索区域的字段中作为默认的查询条件,详情 详情页和表单页此参数会作为页面加载函数的入参 # 示例参数 { “cateId”: “61723712399821” } path 权限验证路径,父页面编译的时候自动加上该参数,在父页面点击当前动作的时候会自动拼该参数 scene 【选填】动作场景值 代码中如何获取 这里介绍在组件内如何获取 import { BaseElementWidget } from ‘@kunlun/dependencies’; export class DemoElementWidget extends BaseElementWidget { protected test() { const { module, model, action } = this.urlParameters; } } 推荐阅读相关文档 上下文在字段和动作中的应用 如何实现页面间的跳转

    2024年8月19日
    3.8K00
  • 「前端」关闭源码依赖

    问题背景 在 5.x 版本的 oinone 前端架构中,我们开放了部分源码,但是导致以下性能问题: 1.构建耗时增加​​:Webpack 需要编译源码文件 2.​​加载性能下降​​:页面需加载全部编译产物 3.​​冷启动缓慢​​:开发服务器启动时间延长 以下方案通过关闭源码依赖。 操作步骤 1. 添加路径修正脚本 1: 在当前项目工程根目录中的scripts目录添加patch-package-entry.js脚本,内容如下: const fs = require('fs'); const path = require('path'); const targetPackages = [ '@kunlun+vue-admin-base', '@kunlun+vue-admin-layout', '@kunlun+vue-router', '@kunlun+vue-ui', '@kunlun+vue-ui-antd', '@kunlun+vue-ui-common', '@kunlun+vue-ui-el', '@kunlun+vue-widget' ]; // 递归查找目标包的 package.json function findPackageJson(rootDir, pkgName) { const entries = fs.readdirSync(rootDir); for (const entry of entries) { const entryPath = path.join(rootDir, entry); const stat = fs.statSync(entryPath); if (stat.isDirectory()) { if (entry.startsWith(pkgName)) { const [pkGroupName, name] = pkgName.split('+'); const pkgDir = path.join(entryPath, 'node_modules', pkGroupName, name); const pkgJsonPath = path.join(pkgDir, 'package.json'); if (fs.existsSync(pkgJsonPath)) { return pkgJsonPath; } } // 递归查找子目录 const found = findPackageJson(entryPath, pkgName); if (found) return found; } } return null; } // 从 node_modules/.pnpm 开始查找 const pnpmDir = path.join(__dirname, '../', 'node_modules', '.pnpm'); for (const pkgName of targetPackages) { const packageJsonPath = findPackageJson(pnpmDir, pkgName); if (packageJsonPath) { try { const packageJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); if (packageJSON.main === 'index.ts') { const libName = packageJSON.name.replace('@', '').replace('/', '-'); packageJSON.main = `dist/${libName}.esm.js`; packageJSON.module = `dist/${libName}.esm.js`; const typings = 'dist/types/index.d.ts'; packageJSON.typings = typings; const [pwd] = packageJsonPath.split('package.json'); const typingsUrl = path.resolve(pwd, typings); const dir = fs.existsSync(typingsUrl); if (!dir)…

    2025年4月17日
    58700
  • 表格字段配置Switch开关

    在业务开发中,我们经常会遇到表格中有个Switch开关组件: 那么如何通过界面设计器配置这个组件呢,下面让我们一起来学习下吧。 设计布尔类型的组件 1: 首先在界面设计器的组件区域添加一个组件。 2: 我们给这个组件添加一个元件,元件的配置必须跟下面的一致 3: 给元件添加属性配置 拖拽一个单行文本字段, 字段编码必须是truthyAction,代表的是该字段为true的时候要执行的动作 再拖拽一个单行文本字段, 字段编码必须是falsyAction,代表的是该字段为false的时候要执行的动作 4: 发布元件,然后再去对应的界面设计器页面,将对应的字段切换成刚刚设计的组件 5: 发布界面设计器,染红我们可以在运行时的页面看到效果 当switch切换的时候,会执行对应的action

    2023年11月21日
    2.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日
    1.3K00

Leave a Reply

登录后才能评论