Solo  当前访客:4 开始使用

hugh 的个人博客

uproject

Express + GraphQL构建简单的cms框架DEMO


1. 技术选择

Express + GraphQL + VUE + mysql

gitHub地址

gitee地址 

2. 实现目标

a. 实现创建cms系统及相应业务界面

b. 实现组件关联

c. 实现页面可编辑

 

3. 插件选择

a. express-graphql - 用于集成graphql到express框架中

b. vue-apollo & vue-apollo - 用于在client端调用graphql的接口

c. graphql-tools - 帮助我们使用js更方便的构建graphql schame

 

4. demo运行示意图

 

5. 具体实现分解

1. 首先分析graphql的特点

主要包含以下内容 1个schame, 5种基本类型+3中扩展类型+自定义类型, 2种主要操作query&mutation

2.  如何构建一个可用的schame,详情见第3步

如果使用官方的graphql库,示例操作如下

npm install graphql

构建schame如下

var {
  GraphQLList,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
  GraphQLInt,
  GraphQLFloat,
  GraphQLEnumType,
  GraphQLNonNull,
  GraphQLInterfaceType,
  GraphQLInputObjectType
} = require('graphql');

const componentsSchema = require('./components');
const themesSchema = require('./themes');

const testSchema = require('./test');

const Query=new GraphQLObjectType({
name:'CommonQuery',
description:'通用查询',
fields:()=>(Object.assign({},
componentsSchema.query,
themesSchema.query,
testSchema.query
))
});
const Mutation=new GraphQLObjectType({
name:'CommonMutation',
description:'通用编辑',
fields:()=>(Object.assign({},
componentsSchema.mutation
))
});
const schema = new GraphQLSchema({
query: Query,
mutation: Mutation
});

module.exports = schema;

 

或者

var { graphql, buildSchema } = require('graphql');

var schema = buildSchema(type Query { hello: String });

 

再配合express-graphql

var graphqlHTTP = require('express-graphql');

app.use('/graphql2', graphqlHTTP({
schema: schemas,
graphiql: true
}));

 

测试访问 http://localhost:9004/graphql2

 

3. 基于业务特点构建通用的schame模板

a. 基于单表的增删改查

 我们已经了解到graphql实现的是类似controller/action层的功能, 那单表增删改查操作其实就是普通的数据库操作了,利用mysql库, 可以轻松实现

b. 使用第2步介绍的库构建schema

首先定义一张组件表

 c. 定义针对组件表的查询、新增功能

var daoUtil = new BaseDao();
const tableName = "m_components";

const Components = new GraphQLObjectType({
name:'Components',
description:"组件对象POJO",
fields: () => {
return ({
id: {type: new GraphQLNonNull(GraphQLInt)},
name: {type: new GraphQLNonNull(GraphQLString)},
theme: {type: new GraphQLNonNull(GraphQLInt)},
frontShow: {type: GraphQLString},
backShow: {type: GraphQLString},
createAt: {type: GraphQLString},
createBy: {type: new GraphQLNonNull(GraphQLInt)},
});
}
});

module.exports = {
query: {
components_list: {
type: new GraphQLList(Components),
description: '查询组件列表',
resolve: async function(source) {
return (await daoUtil.queryAll(tableName));
}
}
},
mutation: {
components_add: {
type: Components,
description: '添加组件',
args: {
id: {type: GraphQLInt},
name: {type: new GraphQLNonNull(GraphQLString)},
theme: {type: new GraphQLNonNull(GraphQLString)},
},
resolve: async function(source, {id, name, theme}) {
var user = {
name: name,
theme: theme,
};
return await daoUtil.insert(tableName, user);
}
}
}
}

 

d. 使用自带的graphql编辑器测试

使用方法见: graphiql

4. 使用模板自动构建schame

常见的schame构建如上, 基于此,我们抽取出公共的部分, 使用模板生成对应对象的所有操作

a. 模板分析

模板中应该具备对象的可操作列表, 入参、返回

type Query {
 {{each $data}}
  {{$value.name}}_all: [{{$value.modal}}]
  {{$value.name}}_page(page: Int!,limit: Int!, where:[where_query]): {{$value.modal}}_page
  {{$value.name}}_byId(id: Int!): {{$value.modal}}
  {{$value.name}}_byField({{each $value.columns}}  {{$value.Field}}:{{$value.fieldType}}, {{/each}}): [{{$value.modal}}]
 {{/each}}
}

type Mutation {
{{each $data}}
{{$value.name}}_add( {{each $value.columns}} {{if !$value.unUserInMutation}} {{$value.Field}}:{{$value.Type}},{{/if}} {{/each}} ): {{$value.modal}}
{{$value.name}}_batch(list: [{{$value.modal}}_mutation]!): batch_result
{{$value.name}}_delete(id: Int!): batch_result
{{$value.name}}_updateById({{each $value.columns}} {{$value.Field}}:{{$value.fieldType}}, {{/each}}): {{$value.modal}}
{{/each}}
}

以上是对外提供的query和Mutation。 $data为所有对象的list

{{$value.modal}} 对应了创建的每个对象在schame中对应的可操作对象

type {{$value.modal}} {
  {{each $value.columns }}
    {{$value.Field}}:{{$value.Type}}
  {{/each}}
  {{each $value.relations relation}}
    {{relation.modal}}:{{if relation.type == 'o2o'}} {{relation.modal}} {{/if}}{{if relation.type == 'o2m'}} [{{relation.modal}}] {{/if}}
  {{/each}}
}

 {{$value.modal}}_page 对应了构建的分页对象在schame中对应的对象

type {{$value.modal}}_page {
  page: Int!,
  limit: Int!,
  list: [{{$value.modal}}],
  total: Int!
}

最后,通过以下代码封装操作

schema {
  query: Query
  mutation: Mutation
}

b. 调用模板方法渲染方法,同时构建schame对象

var gql = template(this.artPath, data);
      // console.log(gql)
      let typeDefs = `${gql}`
      let jsSchema = makeExecutableSchema({
        typeDefs,
        resolvers,
      });

5. 其他

a. 如何实现关联查询

通过定义一个配置文件, 管理对象间的关系

"m_components": [{
      "type": "o2m",
      "field": "id",
      "targetTable": "m_component_props",
      "targetField": "componentId"
    },{
      "type": "o2o",
      "field": "theme",
      "targetTable": "m_theme",
      "targetField": "id"
    }]

通过schame绑定查询关系

{{each $value.relations relation}}
    {{relation.modal}}:{{if relation.type == 'o2o'}} {{relation.modal}} {{/if}}{{if relation.type == 'o2m'}} [{{relation.modal}}] {{/if}}
  {{/each}}

b. 如何解决N+1查询问题

由于graphql会根据schame,一层层解析,最终返回结果,导致关联查询时,会出现N+1查询的问题

解决N+1查询的基本方法就是讲查询条件集中到一个sql中

在我们的范例中, 可以利用resolver中context对象在各个resolver间传递, 结合关系查询配置, 在父节点中集成查询,然后传递到子resolver中返回

resolver中接受参数如下

  • obj 上一级对象,如果字段属于根节点查询类型通常不会被使用。
  • args 可以提供在 GraphQL 查询中传入的参数。
  • context 会被提供给所有解析器,并且持有重要的上下文信息比如当前登入的用户或者数据库访问对象。
  • info 一个保存与当前查询相关的字段特定信息以及 schema 详细信息的值

 

============================完结==============

 


标题:Express + GraphQL构建简单的cms框架DEMO
作者: hugh0524
地址: https://blog.uproject.cn/articles/2019/04/03/1554276742410.html

, , , , hugh0524 0 0
« Graphiql使用
对象属性如何保证原样输出 »

目录

  • 1. 技术选择
  • 2. 实现目标
  • 3. 插件选择
  • 4. demo运行示意图
  • 5. 具体实现分解
  • 1. 首先分析graphql的特点
  • 2. 如何构建一个可用的schame,详情见第3步
  • 3. 基于业务特点构建通用的schame模板
  • a. 基于单表的增删改查
  • b. 使用第2步介绍的库构建schema
  • c. 定义针对组件表的查询、新增功能
  • d. 使用自带的graphql编辑器测试
  • 4. 使用模板自动构建schame
  • a. 模板分析
  • b. 调用模板方法渲染方法,同时构建schame对象
  • 5. 其他
  • a. 如何实现关联查询
  • b. 如何解决N+1查询问题

深圳SEO优化公司兰州企业网站建设果洛网站设计模板多少钱贵港外贸网站设计哪家好坂田网站优化报价仙桃关键词排名哪家好朔州设计网站价格榆林网络营销推荐清徐设计公司网站价格海口模板网站建设价格果洛关键词按天计费价格忻州网站推广价格东莞网站排名优化价格百色建站报价鹤壁网站改版多少钱深圳企业网站建设泉州关键词按天计费公司呼和浩特百度关键词包年推广中卫企业网站制作公司雅安网页制作哪家好松原模板推广报价模板网站建设哪家好来宾网站改版哪家好和田百姓网标王推广哪家好玉溪关键词排名包年推广价格淮南网络推广价格荷坳百度网站优化排名多少钱思茅百度竞价哪家好凉山百度网站优化价格绥化外贸网站建设价格果洛百度网站优化排名推荐歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化