自动化测试-学习

apollo-server 文档

2018-02-10  本文已影响0人  yanshaowen

目录

简介

apollo-server是一个在nodejs上构建grqphql服务端的web中间件。支持express,koa ,hapi等框架。
apollo-server官方文档

安装

根据不同的web框架进行安装安装

npm install graphql apollo-server-express

npm install graphql apollo-server-hapi

npm install graphql apollo-server-koa

npm install graphql apollo-server-restify

npm install graphql apollo-server-lambda

npm install graphql apollo-server-micro

npm install graphql apollo-server-azure-functions

npm install graphql apollo-server-adonis

快速使用

express

安装

npm install --save apollo-server-express graphql express body-parser

实例 源码

/**
 * Created by wenshao on 2018/2/10.
 */
'use strict';
const express = require('express');
const Body = require('body-parser');
const {graphqlExpress} = require('apollo-server-express');
const {
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLString,
    GraphQLInt
} = require('graphql');


const User = new GraphQLObjectType({
    name: 'User',
    description: 'User对象',
    fields: {
        id: {
            type: GraphQLInt
        },
        name: {
            type: GraphQLString
        },
    }
});

const Query = new GraphQLObjectType({
    name: 'Query',
    fields: {
        user: {
            type: User,
            args: {
                id: {
                    type: GraphQLInt
                }
            },
            resolve: function (root, args) {
                return {id: 1, name: '2'};
            }
        }
    }
});
const myGraphQLSchema = new GraphQLSchema({
    query: Query
});
const app = new express();
const PORT = 3000;

// Body is needed just for POST.
app.use(Body());

app.post('/graphql', graphqlExpress({schema: myGraphQLSchema}));
app.get('/graphql', graphqlExpress({schema: myGraphQLSchema}));
app.listen(PORT);

koa

安装

npm install --save apollo-server-koa graphql koa koa-bodyparser koa-router

实例 源码

/**
 * Created by wenshao on 2018/2/10.
 */
'use strict';
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const {graphqlKoa} = require('apollo-server-koa');
const {
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLString,
    GraphQLInt
} = require('graphql');


const User = new GraphQLObjectType({
    name: 'User',
    description: 'User对象',
    fields: {
        id: {
            type: GraphQLInt
        },
        name: {
            type: GraphQLString
        },
    }
});

const Query = new GraphQLObjectType({
    name: 'Query',
    fields: {
        user: {
            type: User,
            args: {
                id: {
                    type: GraphQLInt
                }
            },
            resolve: function (root, args) {
                return {id: 1, name: '2'};
            }
        }
    }
});
const myGraphQLSchema = new GraphQLSchema({
    query: Query
});
const app = new Koa();
const PORT = 3000;

// Body is needed just for POST.
app.use(Body());

router.post('/graphql', graphqlKoa({schema: myGraphQLSchema}));
router.get('/graphql', graphqlKoa({schema: myGraphQLSchema}));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);

其他框架参考官方文档

配置

Apollo server 传染对象进行配置

名称 类型 默认值 必填 描述
schema GraphQLSchema GraphQL的Schema对象
context Object {} 在GraphQL执行时候传递的上下文对象
rootValue Object undefined 第一个执行resolve时候的root对应的值
formatError Function 当执行resolve时出现异常则回调用这个函数
validationRules Function 添加额外的GraphQL验证规则应用到客户机指定查询
formatParams Function 如果有多个resolve则只在第一个时候执行
formatResponse Function 当执行完所有的resolve之后调用
tracing Boolean false 收集每个resolve执行的信息 包括执行时间 参数 返回值等信息
debug Boolean true 当前的环境 默认为true,执行resolve出错时会有错误信息,建议设置为false

简单的使用实例

源码

'use strict';
/**
 * Created by wenshao on 2018/2/10.
 */
'use strict';
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const {graphqlKoa} = require('apollo-server-koa');
const {
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLString,
    GraphQLInt
} = require('graphql');


const User = new GraphQLObjectType({
    name: 'User',
    description: 'User对象',
    fields: {
        id: {
            type: GraphQLInt
        },
        name: {
            type: GraphQLString,
            resolve(root, args, context) {
                console.log(root, context); // console: { id: 1, name: '2' } { test: true }
                return root.name;
            }

        },
    }
});

const Query = new GraphQLObjectType({
    name: 'Query',
    fields: {
        user: {
            type: User,
            args: {
                id: {
                    type: GraphQLInt
                }
            },
            resolve: function (root, args, context) {
                console.log(root,context);  // { test: false } { test: true }
                return {id: 1, name: 'wenshao'};
            }
        }
    }
});
const myGraphQLSchema = new GraphQLSchema({
    query: Query
});
const app = new Koa();
const PORT = 3000;

// Body is needed just for POST.
app.use(Body());

router.post('/graphql', graphqlKoa({
    schema: myGraphQLSchema,
    context: {
        test: true
    },
    rootValue: {
        test: false
    },
    formatError(error) {
        return error;
    },
    validationRules(validationContext) {
        return validationContext;
    },
    formatParams(params) {
        return params;
    },
    formatResponse(data, all) {
        // console.log(data);
        delete data.extensions;// 当加上 tracing: true 返回到前端的会有extensions对象的值 对前端来说这数据没有用 所有可以删除
        return data;    
    },
    tracing: true
}));
router.get('/graphql', graphqlKoa({
    schema: myGraphQLSchema
}));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);

自定义拦执行截器

源码

/**
 * Created by wenshao on 2018/2/10.
 */
'use strict';
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const {
    GraphQLObjectType,
    GraphQLSchema,
    GraphQLString,
    GraphQLInt
} = require('graphql');

/**
 * 自定义异常类
 */
class RequestException extends Error{
    constructor() {
        super();
        this.name = "RequestException";
        this.code = null;
        this.message = null;
        this.serverName = null;
        this.methodName = null;
        this.fullMessage = null;
    }
}


const reIpv4 = '.*:.*:.*:(.*)';
/**
 * 中间件
 * 1 自定义context 可以传入ctx对象
 * 2 增加resolve执行的信息
 * 3 自定义日志输出
 * 4 错误处理统一处理
 * @param options
 * @return {function(*=, *)}
 */
function graphqlKoaLog(options) {
    const {graphqlKoa} = require('apollo-server-koa');
    const logger = options.log && 'info' in options.log ? options.log : console;
    return async (ctx, next) => {
        await graphqlKoa({
            schema: options.schema,
            context: {  // 传入ctx  也可以增加其他值  如用户信息等
                ctx: ctx,
            },
            tracing: true,
            formatError(error){
                if (typeof error === 'object') {
                    if (typeof error.originalError === 'object'
                        &&
                        error.originalError.name === 'RequestException' ) { // 自定义的请求异常 则进行拦截
                        error.message = 'thrift error';     // 返回到前端message
                        return error;
                    }
                }
                return error;
            },
            formatResponse(data, all) { // data 为返回到前端的全部数据  all为执行resolve相关的信息 类似ctx
                let ipv4 = ctx.ip.match(reIpv4);
                if (ipv4 instanceof Array && ipv4.length === 2) ipv4 = ipv4[1];
                else if (ipv4 === null) ipv4 = ctx.ip;
                else ctx.ipv4 = ipv4;   // 找到ip
                if (ctx.method !== 'OPTIONS') logger.info(ipv4, `${data.extensions.tracing.duration / 1000}ms`,
                    '\n============query=======\n',all.query, '\n============variables=======\n', all.variables);
                delete data.extensions; // 前端不需要 则删除
                return data;
            }
        })(ctx);
    }
}


const User = new GraphQLObjectType({
    name: 'User',
    description: 'User对象',
    fields: {
        id: {
            type: GraphQLInt
        },
        name: {
            type: GraphQLString
        },
    }
});

const Query = new GraphQLObjectType({
    name: 'Query',
    fields: {
        user: {
            type: User,
            args: {
                id: {
                    type: GraphQLInt
                }
            },
            resolve: function (root, args, context) {
                const re = new RequestException();
                re.code = 1;
                re.message = '查询失败';
                throw re;
            }
        }
    }
});
const myGraphQLSchema = new GraphQLSchema({
    query: Query
});
const app = new Koa();
const PORT = 3000;

// Body is needed just for POST.
app.use(Body());

router.post('/graphql', graphqlKoaLog({
    schema: myGraphQLSchema,
}));
router.get('/graphql', graphqlKoaLog({
    schema: myGraphQLSchema
}));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);

graphql-tools快速构建

graphql-tools(官网)是使用graphql固定的语法构建schema。

安装

npm install graphql-tools 

实例

/**
 * Created by wenshao on 2018/2/10.
 */
'use strict';
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const {graphqlKoa} = require('apollo-server-koa');
const {makeExecutableSchema} = require('graphql-tools');
const typeDefs = `
    type User{
        id:Int!,
        name:String!
    }
    type Query {
        users: [User]
    }
    type Mutation {
        addUser(name:String!):User
    }
    schema {
        query: Query
        mutation: Mutation  
    }
`;
const resolvers = {
    Query: {    // 对应到typeDefs中的 type Query
        users(root, args, context) {
            return [{id: 1, name: 'wenshao'}];
        }
    },
    Mutation: { // 对应到typeDefs中的 Mutation
        addUser(root, args, context) {
            return {id: 2, name: 'wenshao'};
        }
    }
};


const myGraphQLSchema = makeExecutableSchema({
    typeDefs,
    resolvers
});
const app = new Koa();
const PORT = 3000;

// Body is needed just for POST.
app.use(Body());

router.post('/graphql', graphqlKoa({schema: myGraphQLSchema}));
router.get('/graphql', graphqlKoa({schema: myGraphQLSchema}));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);

graphql-schema类型

标量类型(Scalar Types)



typeDefs

#时间类型 
scalar Date

resolvers

const resolvers = {
    Date: {
        parseValue(value) {// 序列化
            return new Date(value);
        },
        serialize(value) {// 反序列化
            return value.getTime();
        }
    }
};

对象类型和字段(Object Types)

一个 GraphQL schema 中的最基本的组件是对象类型,它就表示你可以从服务上获取到什么类型的对象,以及这个对象有什么字段。使用 GraphQL schema language,我们可以这样表示它:

type Character {
  name: String!
  appearsIn: [Episode]!
}

参数(Arguments)

GraphQL 对象类型上的每一个字段都可能有零个或者多个参数,例如下面的 length 字段:

typeDefs

type Starship {
    id: ID!
    name: String!
    length(unit:Int=1): Float
}

resolvers

const resolvers = {
    Starship: {
            length(root, {unit}, context) {
                return unit === 1 ? root.length : root.length /1000;
            }
        }
}

查询和变更类型(The Query and Mutation Types)

一个schema 中大部分的类型都是普通对象类型,但是一个 schema 内有两个特殊类型,
每一个 GraphQL 服务都有一个 query 类型,可能有一个 mutation 类型。这两个类型和常规对象类型无差,但是它们之所以特殊,是因为它们定义了每一个 GraphQL 查询的入口。因此如果你看到一个像这样的查询:

typeDefs

type Query{
    test:Int
}
type Mutation{
    test:Int
}
schema {
  query: Query
  mutation: Mutation
}

枚举类型(Enumeration Types)

枚举类型限制了值在可选范围之内。枚举类型只能为String类型

typeDefs

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

列表和非空(Lists and Non-Null)

对象类型、标量以及枚举是 GraphQL 中你唯一可以定义的类型种类。但是当你在 schema 的其他部分使用这些类型时,或者在你的查询变量声明处使用时,你可以给它们应用额外的类型修饰符来影响这些值的验证。我们先来看一个例子:

typeDefs

type Character {
  name: String!
  appearsIn: [Episode]!
}

接口(Interfaces)

跟许多类型系统一样,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口。

typeDefs

interface Vehicle {
    maxSpeed: Int
}
type Airplane implements Vehicle {
    maxSpeed: Int
    wingspan: Int
}

type Car implements Vehicle {
    maxSpeed: Int
    licensePlate: String
}
type Query {
    vehicles:Vehicle
}

resolvers

const resolvers = {
    Query: {
        vehicles() {
            return {maxSpeed:1,licensePlate:'test'}
        }
    },
    Vehicle: {
        __resolveType(obj, context, info){
            if(obj.wingspan){
                return 'Airplane';
            }

            if(obj.licensePlate){
                return 'Car';
            }
            return null;
        }
    }
}

联合类型(Union Types)

联合类型和接口十分相似,但是它并不指定类型之间的任何共同字段。联合类型的成员需要是具体对象类型;你不能使用接口或者其他联合类型来创造一个联合类型。

typeDefs

union UnionVehicle = Airplane | Car
type Query {
    unionVehicles:UnionVehicle
}

resolvers

const resolvers = {
    Query: {
        unionVehicles() {
            return {maxSpeed:1,licensePlate:'test'}
        }
    },
    UnionVehicle: {
        __resolveType(obj, context, info){
            if(obj.wingspan){
                return 'Airplane';
            }

            if(obj.licensePlate){
                return 'Car';
            }
            return null;
        }
    }
}

输入类型(Input Types)

前端传入的对象,通过args进行传入到后端。和输出对象类似,但是关键字为input。输入对象和输出对象不能混用。

typeDefs

input ReviewInput {
    stars: Int!
    commentary: String
}
type Query {
    testInput(field:ReviewInput): Int
}

resolvers

const resolvers = {
    Query: {
         testInput(root, {field}, context) {
            console.log(field);
            return 1;
        }
    }
}

github-api-v4设计规范分析

github-api-v4采用的为graphql规范,之前的版本都为Rest规范。

文档地址 文档grapiql

grapiql地址 (需要使用github账号进行登录才能使用)

schema设计

type设计

命名规范

设计规范项目实例

根据上面的github api的设计规范可以得出适应自己项目的一些规范。下面为作者本人的项目的规范,可供参考。

目录结构

项目地址

https://github.com/wenshaoyan/apollo-server-example

上一篇下一篇

猜你喜欢

热点阅读