热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用Node.js+Docker+GraphQL+MongoDB构建服务

用了GraphQL之后,就不想再用RESTful了。本文将使用Node.js+Docker+GraphQL+MongoDB构建一个具有CRUD功能的完整微服务。运行代码,需要安装d

用了GraphQL之后,就不想再用RESTful了。

本文将使用Node.js+Docker+GraphQL+MongoDB构建一个具有CRUD功能的完整微服务。

运行代码,需要安装docker和docker-compose。

完整代码见:leinue/node-mongodb-graphql-docker

注:阅读本文需要有GraphQL基础。

Docker

dockerfile

FROM node
WORKDIR /app
EXPOSE 5555

从node构建,将工作目录设置为/app,暴露5555端口

docker-compose

version: "3"
services:
user_service:
build: .
links:
- user_db
command: ["node", "index.js", ' && /bin/bash']
hostname: user_service_in_container
volumes:
- ./:/app
deploy:
replicas: 1
restart_policy:
condition: on-failure
ports:
- "5555:5555"
user_db:
image: mongo
volumes:
- "/tmp/db:/data/db"
restart: always

声明了以下任务:

  1. 声明user_service服务和user_db服务
  2. user_service:
    1. 从当前目录的dockerfile构建
    2. 与user_db连接
    3. 在启动时执行node index.js和/bin/bash
    4. 挂载当前目录到/app目录下
    5. 复制一份
    6. 在失败时重启
    7. 暴露容器内的5555端口到宿主机的5555端口
  3. user_db
    1. 从mongo构建
    2. 将容器内的/data/db挂载到宿主机上的/tmp/db上(数据持久化)
    3. 总是重启

运行命令

docker-compose up -d

实现代码

使用babel编译为ES6代码

事实上现在的node v8已经支持大部分ES6代码了,但是async仍不支持,为了使用async不得不使用babel编译。

.babelrc

{
"presets": ["es2015"],
"plugins": ["syntax-async-generators", "transform-async-generator-functions", "transform-regenerator"]
}

需要的babel包

{
"babel-plugin-syntax-async-generators": "^6.13.0",
"babel-plugin-transform-async-generator-functions": "^6.24.1",
"babel-plugin-transform-regenerator": "^6.26.0"
"babel-core": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
}

安装之后创建index.js

require('babel-core/register');
require("babel-polyfill");
require('./server.js');

这样就可以在server.js中使用es6的代码了。

server.js

server.js用来初始化graphql服务器、路由和连接mongodb。这里使用koa和graphql服务端的插件来初始化。

import koa from 'koa'; // koa@2 import koaRouter from 'koa-router'; // koa-router@next import koaBody from 'koa-bodyparser'; // koa-bodyparser@next import { graphqlKoa, graphiqlKoa } from 'apollo-server-koa';
import cors from 'koa-cors';
import convert from 'koa-convert';
import configs from './configs';
import mongoose from 'mongoose';
const app = new koa();
const router = new koaRouter();
const db = mongoose.createConnection(['mongodb://', configs.mongodb.ip, '/', configs.mongodb.dbname].join(''));
if(db) {
console.log('mongodb connected successfully');
global.db = db;
}else {
console.log('mongodb connected failed');
}
import schemaRouters from './routers/schemaRouters';
const schemas = schemaRouters().default;
router.post('/graphql', koaBody(), graphqlKoa({ schema: schemas.HelloSchema }));
router.get('/graphql', graphqlKoa({ schema: schemas.HelloSchema }));
router.get('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }));
app.use(convert(cors(configs.cors)));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(configs.port, () => {
console.log('app started successfully, listening on port ' + configs.port);
});

这段初始化代码将/graphql作为graphql数据收发的路由地址,服务将启动在5555端口。

构建MongoDB数据类型

import { Schema } from 'mongoose';
var helloSchema = new Schema({
email: String,
lastIP: String,
});
export default global.db.model('Hello', helloSchema);

初始化一个helloSchema,并将这个model命名为hello

还需要一个index.js将数据类型全部引入:

import HelloModel from './HelloModel'
export default {
HelloModel,
}

构建GraphQL Schema

一个schema需要分成三部分:

  1. mutations
    1. 修改/删除/增加操作
  2. queries
    1. 查询操作
  3. types
    1. 数据类型定义(输入/输出)

以HelloSchema为例,其文件结构如下:

.
├── index.js
├── mutations
├── add.js
├── index.js
├── remove.js
└── update.js
├── queries
├── hello.js
└── index.js
└── types
├── Hello.js
├── HelloAddInput.js
├── HelloFields.js
└── HelloUpdateInput.js

各文件作用如下:

  • index.js
    • 初始化query和mutation
  • mutations
    • add.js
      • 执行增加操作
    • index.js
      • 将增加/修改/删除操作引用到一起
    • remove.js
      • 执行删除操作
    • update.js
      • 执行更新操作
  • queries
    • hello.js
      • 执行查询操作
    • index.js
      • 将查询操作引用到一起
  • types
    • Hello.js
      • 定义返回结果的数据类型
    • HelloAddInput.js
      • 定义增加操作时输入结构的数据类型
    • HelloUpdateInput.js
      • 定义更新操作时输入结构的数据类型
    • HelloFields.js
      • 定义HelloSchema的通用数据结构(和HelloModel内容相同)

index.js

初始化query和mutation

import {
GraphQLObjectType,
GraphQLSchema,
GraphQLList
} from 'graphql';
import mutations from './mutations';
import queries from './queries';
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: queries
}),
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: mutations
})
});
export default schema;

queries/hello.js

执行查询操作

import { GraphQLList, GraphQLString } from 'graphql';
import HelloType from '../types/Hello.js';
import HelloModel from '../../../models/HelloModel';
const hello = {
type: new GraphQLList(HelloType),
async resolve (root, params, options) {
var hello = await HelloModel.find({});
return hello;
}
}
export default hello;

定义了返回结果是一个HelloType的数组列表 ,resolve中使用了async函数进行mongodb异步查询。

mutations/add.js

执行增加操作

import { GraphQLNonNull } from 'graphql';
import HelloType from '../types/Hello.js';
import HelloAddInput from '../types/HelloAddInput.js';
import HelloModel from '../../../models/HelloModel';
const add = {
type: HelloType,
args: {
info: {
name: 'info',
type: new GraphQLNonNull(HelloAddInput)
}
},
async resolve (root, params, options) {
const HelloModel = new HelloModel(params.info);
const newHello = await HelloModel.save();
if (!newHello) {
return false;
}
return newHello;
}
};
export default add;

注意其中args,其类型是HelloAddInput

HelloAddInput定义如下:

import {
GraphQLInputObjectType,
GraphQLString,
GraphQLID,
GraphQLNonNull
} from 'graphql';
export default new GraphQLInputObjectType({
name: 'HelloAddInput',
fields: {
email: {
type: GraphQLString
},
lastIP: {
type: GraphQLString
}
}
});

声明了在执行添加操作时需要输入email和lastIP参数。

mutations/update.js

执行更新操作

import { GraphQLNonNull } from 'graphql';
import HelloType from '../types/Hello.js';
import HelloModel from '../../../models/HelloModel';
import HelloUpdateInput from '../types/HelloUpdateInput.js';
const update = {
type: HelloType,
args: {
options: {
name: 'options',
type: new GraphQLNonNull(HelloUpdateInput)
}
},
async resolve (root, params, options) {
const updated = await HelloModel.findOneAndUpdate({
_id: params.options._id
}, params.options);
const hello = await HelloModel.findOne({
_id: params.options._id
});
return hello;
}
};
export default update

注意其中的参数options,其数据类型为HelloUpdateInput。

HelloUpdateInput定义如下:

import {
GraphQLObjectType,
GraphQLInputObjectType,
GraphQLNonNull,
GraphQLString,
GraphQLID,
GraphQLInt,
GraphQLBoolean
} from 'graphql';
import HelloFields from './HelloFields';
export default new GraphQLInputObjectType({
name: 'HelloUpdateInput',
fields: HelloFields
});

定义了在更新字段时可以使用HelloFields内的任意字段,HelloFields定义如下:

import {
GraphQLString,
GraphQLInt,
GraphQLBoolean
} from 'graphql';
export default {
_id: {
type: GraphQLString
},
email: {
type: GraphQLString
},
lastIP: {
type: GraphQLString
}
}

其和model是一样的

mutations/remove.js

执行删除操作

import { GraphQLList, GraphQLString } from 'graphql';
import HelloType from '../types/Hello.js';
import HelloModel from '../../../models/HelloModel';
const remove = {
type: new GraphQLList(HelloType),
args: {
ids: {
name: 'ids',
type: new GraphQLList(GraphQLString)
}
},
async resolve (root, params, options) {
let removedList = [];
for (var i = 0; i < params.ids.length; i++) {
const _id = params.ids[i];
const removed = await HelloModel.findOneAndRemove({
_id
});
if(removed) {
removedList.push(removed)
}
};
return removedList;
}
}
export default remove

注意其中的ids是一个GraphQLList字符串数组,返回结果是HelloType对象。

执行GraphQL查询

启动程序

docker-compose up -d

打开GraphQL测试界面:http://localhost:5555/graphiql

先来执行一个查询:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

可以看到结果返回空。

再执行一个增加操作:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

可以看到右侧返回了新增的数据及其id

我们再将emai修改为“fuck_shit”:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

右侧成功返回了修改之后的数据。

最后,我们删除这条数据:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

返回了被删除的id。

再最后执行一次查询:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

可以看到结果又变为空了。

至此,整个GraphQL+MongoDB的CRUD操作测试成功。

小Tips

graphql的测试器右侧可以查看数据类型:

《使用Node.js+Docker+GraphQL+MongoDB构建服务》
《使用Node.js+Docker+GraphQL+MongoDB构建服务》

《使用Node.js+Docker+GraphQL+MongoDB构建服务》

《使用Node.js+Docker+GraphQL+MongoDB构建服务》

完整代码见:leinue/node-mongodb-graphql-docker


推荐阅读
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • .NetCoreWebApi生成Swagger接口文档的使用方法
    本文介绍了使用.NetCoreWebApi生成Swagger接口文档的方法,并详细说明了Swagger的定义和功能。通过使用Swagger,可以实现接口和服务的可视化,方便测试人员进行接口测试。同时,还提供了Github链接和具体的步骤,包括创建WebApi工程、引入swagger的包、配置XML文档文件和跨域处理。通过本文,读者可以了解到如何使用Swagger生成接口文档,并加深对Swagger的理解。 ... [详细]
  • 本文介绍了在Android Studio中使用命令行build gradle的方法,并解决了一些常见问题,包括手动配置gradle环境变量和解决External Native Build Issues的方法。同时提供了相关参考文章链接。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 本文介绍了一种求解最小权匹配问题的方法,使用了拆点和KM算法。通过将机器拆成多个点,表示加工的顺序,然后使用KM算法求解最小权匹配,得到最优解。文章给出了具体的代码实现,并提供了一篇题解作为参考。 ... [详细]
  • Hello.js 是一个用于连接OAuth2服务的JavascriptRESTFULAPI库,如Go ... [详细]
  • systemd-nspawn可以创建最轻量级的容器(ns的意思就是namespace),本文的实验平台是Ubuntu16.04,x86_64机器。本文的目的是:在Ubuntu中用syst ... [详细]
  • 1223  drf引入以及restful规范
    [toc]前后台的数据交互前台安装axios插件,进行与后台的数据交互安装axios,并在main.js中设置params传递拼接参数data携带数据包参数headers中发送头部 ... [详细]
author-avatar
守望天空2502870383
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有