什么是GraphQL

GraphQL 是一种面向 API 的查询语言。在互联网早期,需求都以 Web 为主,那时候数据和业务需求都不复杂,所以用 RestAPI 的方式完全可以满足需求。但是随着互联网的发展,数据量增大,业务需求多变。还有各种客户端需要接口适配,基于 RestAPI 的方式,显得越来呆板,因此 GraphQL 便应运而生。它至少可以提供以下三个方面的优势

  1. GraphQL 提供更方便的 API 查询

不同的客户端有时候需要返回的数据格式不同,之前使用 RestAPI 的方式,需要后端针对每一个客户端提供单独的接口。随着业务需求的增加,维护的成本随机呈指数级跃升。而使用 GraphQL 就比较开心了,只需要写一套接口即可

  1. 解决前后端过于依赖

在开发的过程中,前端需要和后端反反复复确认各个字段,防止到时候开发到一半,因为没有对好字段,要大块大块地改代码。现在有 GraphQL 就比较方便了,你需要什么类型的字段,就自己写对应的查询语法

  1. 节约网络和计算机内存资源

之前通过 RestAPI 的方式写接口,有一个很大的问题在于,对于接口的定义,需要前期做大量的工作,针对接口做各种力度的拆分,但即使这样,也没办法应对需求的风云突变。有时候需要返回的仅仅是某个用户的某一类型的数据,但不得不把该用户的其他信息也一并返回来,这既浪费了网络的资源,也消耗了计算机的性能。显然不够优雅,GraphQL 再一次证明了它的强大,它能够提供 DIY 获取所需要的数据,用多少,拿多少,可以说是相当环保了

PS : 更多 GraphQL 的介绍可以看文末的参考资料

介绍

这篇文章,我将用一个具体的 Todo List 实例,和大家一起,一步步手动搭建一个 GraphQL + MongoDB 的项目实例。我们将会在其中用到以下库,开始之前需要提前安装好:

  1. graphene_mongo
  2. graphene
  3. mongoengine
  4. flask_graphql
  5. Flask

在开始之前,我们来梳理一下我们的核心需求,我们要建立一个 Todo List 产品,我们核心的表只有两个,一个是用户表,存储所有的用户信息,另外一个是任务表,存储着所有用户的任务信息。任务表通过用户 id 与对应的用户关联。表结构对应的是一对多的关系,核心的数据字段如下:

task表

  1. {
  2. "_id" : ObjectId("5c353fd8771502a411872712"),
  3. "_in_time" : "2019-01-09 08:26:53",
  4. "_utime" : "2019-01-09 09:26:39",
  5. "task" : "read",
  6. "start_time" : "2019-01-09 08:26:53",
  7. "end_time" : "2019-01-09 08:26:53",
  8. "repeat" : [
  9. "Wed"
  10. ],
  11. "delete_flag" : NumberInt(0),
  12. "user" : "1"
  13. }

user表

  1. {
  2. "_id" : "1",
  3. "_in_time" : "2019-01-09 08:39:16",
  4. "_utime" : "2019-01-09 09:23:25",
  5. "nickname" : "xiao hong",
  6. "sex" : "female",
  7. "photo": "http://xh.jpg",
  8. "delete_flag" : NumberInt(0)
  9. }

项目结构

一图胜千言,为更清晰的了解项目的整体结构,我将项目的整体目录结构打印下来,小伙伴们可以参照着目录结构,看接下来的搭建步骤

  1. ----task_graphql\
  2. |----api.py
  3. |----database\
  4. | |----__init__.py
  5. | |----base.py
  6. | |----model_task.py
  7. | |----model_user.py
  8. |----requirements.txt
  9. |----schema.py
  10. |----schema_task.py
  11. |----schema_user.py
 
pic_1.png
  • user_model 和 task_model 定义数据模块,直接数据库 mongo 对接
  • 上层定义的 schema 操作 shema_user 和 schema_task 对数据 model 进行增删改查操作
  • 最后 flask 搭建对外的 api 服务实现和外界的请求交互

创建数据模型

我们的数据模型结构非常简单

  • user_model 列出所有的用户信息
  • task_model 列出所有的任务信息,通过user字段与用户表关联,表示该任务归属于哪一个用户
 
pic_2.png
base.py
  1. from mongoengine import connect
  2. connect("todo_list", host="127.0.0.1:27017")

只需要通过调用 mongoengine 的 connect 指定对应的数据库链接信息和数据库即可,后面直接引入至Flask模块会自动识别连接

model_user.py
  1. import sys
  2. sys.path.append("..")
  3. from mongoengine import Document
  4. from mongoengine import (StringField, IntField)
  5. from datetime import datetime
  6. class ModelUser(Document):
  7. meta = {"collection": "user"}
  8. id = StringField(primary_key=True)
  9. _in_time = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
  10. _utime = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
  11. nickname = StringField(required=True)
  12. sex = StringField(default="unknown", required=True)
  13. delete_flag = IntField(default=0, required=True)

所要定义的数据文档都通过 mongoengine 的 Document 继承,它可以将对应字段转换成类属性,方便后期对数据进行各种操作,meta 字段指定对应的你需要链接的是哪张 mongo 表

model_task.py
  1. import sys
  2. sys.path.append("..")
  3. from mongoengine import Document
  4. from mongoengine import (StringField, ListField, IntField, ReferenceField)
  5. from .model_user import ModelUser
  6. from datetime import datetime
  7. class ModelTask(Document):
  8. meta = {"collection": "task"}
  9. _in_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
  10. _utime = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
  11. task = StringField(default="", required=True)
  12. start_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
  13. end_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
  14. repeat = ListField(StringField(required=True))
  15. delete_flag = IntField(default=0, required=True)
  16. user = ReferenceField(ModelUser, required=True)

其中 required 表示这个字段是必须字段,default 可以设置该字段的默认值。ReferenceField 可以指定和哪个模型相关联,这里指定的是 ModelUser 字段,关联默认为对应 mongo 表中的 _id 字段

创建GraphQL查询

现在我们已经将数据库和模型部分的连接功能完成了,接下来创建 API 部分,在我们的 task_graphql 目录下,有两个文件,schema_task.py 和 schema_user.py 分别将 model_task 和 model_user 类映射成 Graphene schema对象

schema_task.py
  1. from database.model_task import ModelTask
  2. from graphene_mongo import MongoengineObjectType
  3. import graphene
  4. import schema_user
  5. from datetime import datetime
  6. class TaskAttribute:
  7. id = graphene.ID()
  8. _in_time = graphene.String()
  9. _utime = graphene.String()
  10. task = graphene.String()
  11. start_time = graphene.String()
  12. end_time = graphene.String()
  13. repeat = graphene.List(graphene.String)
  14. delete_flag = graphene.Int()
  15. user = graphene.String()
  16. class Task(MongoengineObjectType):
  17. class Meta:
  18. model = ModelTask
  19. class TaskNode(MongoengineObjectType):
  20. class Meta:
  21. model = ModelTask
  22. interfaces = (graphene.relay.Node, )
schema_user.py
  1. from database.model_task import ModelTask
  2. from graphene_mongo import MongoengineObjectType
  3. import graphene
  4. from datetime import datetime
  5. class TaskAttribute:
  6. id = graphene.ID()
  7. _in_time = graphene.String()
  8. _utime = graphene.String()
  9. task = graphene.String()
  10. start_time = graphene.String()
  11. end_time = graphene.String()
  12. repeat = graphene.List(graphene.String)
  13. delete_flag = graphene.Int()
  14. user = graphene.String()
  15. class Task(MongoengineObjectType):
  16. class Meta:
  17. model = ModelTask
  18. class TaskNode(MongoengineObjectType):
  19. class Meta:
  20. model = ModelTask
  21. interfaces = (graphene.relay.Node, )

现在我们创建一个 schema.py 的文件,把刚才定义好的 schema_task.py 和 schema_user.py 文件引入进来,定义两个对外访问的接口

  • tasks: 查询所有任务信息,返回一个list
  • users: 查询所有用户信息,返回一个list
  1. import schema_user
  2. import schema_task
  3. import graphene
  4. from graphene_mongo.fields import MongoengineConnectionField
  5. class Query(graphene.ObjectType):
  6. node = graphene.relay.Node.Field()
  7. tasks = MongoengineConnectionField(schema_task.TaskNode)
  8. users = MongoengineConnectionField(schema_user.UserNode)
  9. schema = graphene.Schema(query=Query)

创建 Flask 应用

在主目录下创建一个 api.py 文件,将我们之前定义好的数据库连接和 schema 引入进来,用 Flask 的 add_url_rule 方法将两者关联起来,为了方便访问,我们通过引入 flask_graphql 的 GraphQLView 方法,将接口可视化出来,方便调试

  1. from flask import Flask
  2. from schema import schema
  3. from flask_graphql import GraphQLView
  4. from database.base import connect
  5. from logger import AppLogger
  6. log = AppLogger("task_graphql.log").get_logger()
  7. app = Flask(__name__)
  8. app.debug = True
  9. app.add_url_rule("/graphql", view_func=GraphQLView.as_view("graphql", schema=schema, graphiql=True))
  10. if __name__ == '__main__':
  11. app.run()

到这里,我们就已经用 graphql 成功创建了一个可查询的 Todo List 接口,接下来。我们可以用它来测试一下查询接口吧。然后在开始查询之前大家需要自己 mock 点数据到 mongo 里面

 
pic_3.png

我们访问接口地址(http://127.0.0.1:5000/graphql),来查询一下看看效果

 
pic_4.png

添加 GraphQL 更新方法(mutation)

GraphQL 官方将更新创建操作,全部整合在 mutation 下,它包含了插入和更新数据功能,接下来我们就继续上面的操作,将这部分功能完善

schema_task.py
  1. from database.model_task import ModelTask
  2. from graphene_mongo import MongoengineObjectType
  3. import graphene
  4. from datetime import datetime
  5. class TaskAttribute:
  6. id = graphene.ID()
  7. _in_time = graphene.String()
  8. _utime = graphene.String()
  9. task = graphene.String()
  10. start_time = graphene.String()
  11. end_time = graphene.String()
  12. repeat = graphene.List(graphene.String)
  13. delete_flag = graphene.Int()
  14. user = graphene.String()
  15. class Task(MongoengineObjectType):
  16. class Meta:
  17. model = ModelTask
  18. class TaskNode(MongoengineObjectType):
  19. class Meta:
  20. model = ModelTask
  21. interfaces = (graphene.relay.Node, )
  22. class CreateTaskInput(graphene.InputObjectType, TaskAttribute):
  23. pass
  24. class CreateTask(graphene.Mutation):
  25. task = graphene.Field(lambda: TaskNode)
  26. class Arguments:
  27. input = CreateTaskInput(required=True)
  28. def mutate(self, info, input):
  29. task = ModelTask(**input)
  30. task.save()
  31. return CreateTask(task=task)
  32. class UpdateTask(graphene.Mutation):
  33. task = graphene.Field(lambda: TaskNode)
  34. class Arguments:
  35. input = CreateTaskInput(required=True)
  36. def mutate(self, info, input):
  37. id = input.pop("id")
  38. task = ModelTask.objects.get(id=id)
  39. task._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  40. task.update(**input)
  41. task.save()
  42. return UpdateTask(task=task)
schema_user.py
  1. from database.model_user import ModelUser
  2. from graphene_mongo.types import MongoengineObjectType
  3. import graphene
  4. from datetime import datetime
  5. class UserAttribute:
  6. id = graphene.String()
  7. _in_time = graphene.String()
  8. _utime = graphene.String()
  9. nickname = graphene.String()
  10. photo = graphene.String()
  11. sex = graphene.String()
  12. delete_flag = graphene.Int()
  13. class User(MongoengineObjectType):
  14. class Meta:
  15. model = ModelUser
  16. class UserNode(MongoengineObjectType):
  17. class Meta:
  18. model = ModelUser
  19. interfaces = (graphene.relay.Node, )
  20. class CreateUserInput(graphene.InputObjectType, UserAttribute):
  21. pass
  22. class CreateUser(graphene.Mutation):
  23. user = graphene.Field(lambda: UserNode)
  24. class Arguments:
  25. input = CreateUserInput(required=True)
  26. def mutate(self, info, input):
  27. user = ModelUser(**input)
  28. user.save()
  29. return CreateUser(user=user)
  30. class UpdateUser(graphene.Mutation):
  31. user = graphene.Field(lambda: UserNode)
  32. class Arguments:
  33. input = CreateUserInput(required=True)
  34. def mutate(self, info, input):
  35. id = input.pop("id")
  36. user = ModelUser.objects.get(id=id)
  37. user._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  38. user.update(**input)
  39. user.save()
  40. return UpdateUser(user=user)

一看代码便知,我们将需要添加的信息,通过input传入进来,然后将对应的参数进行映射即可。我们再通过实例看下创建数据的效果

 
pic_5.png

我们再来试下修改数据的操作,like this

 
pic_6.png

bingo!!!

至此,我们通过 GraphQL 搭配 MongoDB 的操作就完美收关了。

完整项目请查看 github: https://github.com/hacksman/task_graphql_demo

以上都是自己一路踩过了很多坑之后总结出的方法,如有疏漏,还望指正

作者:鸡仔说
链接:https://www.jianshu.com/p/4481a9a791fe
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

GraphQL搭配MongoDB入门项目实战的更多相关文章

  1. python操作三大主流数据库(10)python操作mongodb数据库④mongodb新闻项目实战

    python操作mongodb数据库④mongodb新闻项目实战 参考文档:http://flask-mongoengine.readthedocs.io/en/latest/ 目录: [root@n ...

  2. 基于renren-fast的快速入门项目实战(实现报表增删改查)

    基于renren-fast的快速入门项目实战(实现报表增删改查) 说明:renren-fast是一个开源的基于springboot的前后端分离手脚架,当前版本是3.0 官方开发文档需付费,对于新手而言 ...

  3. 《Node+MongoDB+React 项目实战开发》已出版

    前言 从深圳回长沙已经快4个月了,除了把车开熟练了外,并没有什么值得一提的,长沙这边要么就是连续下一个月雨,要么就是连续一个月高温暴晒,上班更是没啥子意思,长沙这边的公司和深圳落差挺大的,薪资也是断崖 ...

  4. 史上最强NDK入门项目实战

    目标: 利用NDK生成SO库,使用SO库进行JNI调用,在Android sdcard创建文件并写入数据. 工具: NDK1.5 R1, android SDK1.5 R1, SDCARD, Ecli ...

  5. MyBatisPLus入门项目实战各教程目录汇总

    https://blog.csdn.net/BADAO_LIUMANG_QIZHI/column/info/37194 http://www.imooc.com/article/details/id/ ...

  6. Linux运维企业架构项目实战系列

    Linux运维企业架构项目实战系列 项目实战1—LNMP的搭建.nginx的ssl加密.权限控制的实现 项目实战2—LVS.nginx实现负载均衡系列2.1 项目实战2.1—实现基于LVS负载均衡集群 ...

  7. GraphQL + React Apollo + React Hook + Express + Mongodb 大型前后端分离项目实战之后端(19 个视频)

    GraphQL + React Apollo + React Hook + Express + Mongodb 大型前后端分离项目实战之后端(19 个视频) GraphQL + React Apoll ...

  8. MongoDB入门必读(概念与实战并重)

    MongoDB入门必读(概念与实战并重) 一.概述 MongoDB是一个基于分布式文件存储的数据库开源项目.由C++语言编写.旨在为WEB应用提供可护展的高性能数据存储解决方案. MongoDB是一个 ...

  9. 零基础入门Python实战:四周实现爬虫网站 Django项目视频教程

    点击了解更多Python课程>>> 零基础入门Python实战:四周实现爬虫网站 Django项目视频教程 适用人群: 即将毕业的大学生,工资低工作重的白领,渴望崭露头角的职场新人, ...

随机推荐

  1. 用less编写百度搜索静态效果

    效果图 html页面 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  2. centos7 下zookeeper 部署 单机多实例模式

    centos7 下zookeeper 部署 本文参考https://www.linuxidc.com/Linux/2016-09/135052.htm 1.创建/usr/local/zookeeper ...

  3. node.js学习5--------------------- 返回html内容给浏览器

    /** * http服务器的搭建,相当于php中的Apache或者java中的tomcat服务器 */ // 导包 const http=require("http"); cons ...

  4. Python 习题一

    1.使用while循环输入 1 2 3 4 5 6 8 9 10 # Author:Tony.lou i = 1 while i < 11: if i == 7: pass else: prin ...

  5. 原生Ajax--XmlHttpRequest对象和jQuery.ajax()

    Ajax主要就是使用 [XmlHttpRequest]对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE) 1.XmlHttpRequest对象介绍 XmlHttpRequest对象的主要 ...

  6. 【译】如何编写“移动端优先”CSS

    原文链接:https://zellwk.com/blog/how-to-write-mobile-first-css/ 构建响应式网站是如今前端开发者的必备技能,当我们谈到响应式网站时,“移动端优先” ...

  7. jquery tmpl生成导航

    引入<script src="jquery.tmpl.min.js"></script> html<ul class="nav" ...

  8. 一个http请求从用户输入网址开始到结束都发生了什么

    一个http请求从用户输入网址开始到结束都发生了什么   一.一个http请求从开始到Django后台,到结束发生了什么 通过用户输入的域名解析出IP地址 TCP/IP 三次握手 进入nginx--- ...

  9. JAVA高级-面试题总结

    最近面试了一些公司,针对面试中遇到的问题在此记录,提升自己,造福大家 一.java源码相关 ArrayList创建和add等各种api使用原理 HashMap 的创建,put原理,和HashTable ...

  10. 简单学完HTML+CSS+JS,现在开始看算法(第四版)----欧几里得算法

    欧几里得算法 package euclidean_algorithm; import java.util.Scanner; /** * @author ALazy_cat * 欧几里得算法的自然语言描 ...