Apollo GraphQL

12 篇文章 1 订阅

Apollo GraphQL

Apollo 是一个开源的 GraphQL 开发平台, 提供了符合 GraphQL 规范的服务端和客户端实现。使用 Apollo 可以帮助我们更方便快捷的开发使用 GraphQL。

基本用法

1、准备

# 创建项目目录
mkdir graphql-server-example
cd graphql-server-example

# 初始化 package.json 文件
npm init -y

# 安装依赖
npm install apollo-server graphql

# 创建 index.js
touch index.js

2、index.js

const { ApolloServer, gql } = require('apollo-server')

// 1. 定义 schema
const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`

const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin'
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster'
  }
]

// 2. 定义 resolver
const resolvers = {
  // 所有的 Query 都走这里
  Query: {
    books: () => books
  }
}

const server = new ApolloServer({ typeDefs, resolvers })

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`)
})

与 Node.js 中间件集成

Apollo Server 可以轻松地与几个流行的 Node.js 中间件库集成。要集成,请首先从下表中安装适当的软件包,而不是核心 apollo-server 软件包。

兼容的软件包
在这里插入图片描述
应用中间件
与中间件集成时,首先像往常一样初始化 Apollo Server,然后调用 applyMiddleware。

这是为 Hello 服务的基本 Express 示例。从 /graphql 以外的每个路径访问,该路径与 Apollo Server 一起提供GraphQL端点:

const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const { typeDefs, resolvers } = require('./schema');

const app = express();
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.applyMiddleware({ app });

app.use((req, res) => {
  res.status(200);
  res.send('Hello!');
  res.end();
});

app.listen({ port: 4000 }, () =>
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
)

解析器

基本用法
schema:

type Query {
  numberSix: Int! # Should always return the number 6 when queried
  numberSeven: Int! # Should always return 7
}

resolver:

const resolvers = {
  Query: {
    numberSix() {
      return 6;
    },
    numberSeven() {
      return 7;
    }
  }
};

处理参数
schema:

type User {
  id: ID!
  name: String
}

type Query {
  user(id: ID!): User
}

resolver:

const users = [
  {
    id: '1',
    name: 'Elizabeth Bennet'
  },
  {
    id: '2',
    name: 'Fitzwilliam Darcy'
  }
];

const resolvers = {
  Query: {
    user(parent, args, context, info) {
      return users.find(user => user.id === args.id);
    }
  }
}

解析器链
schema:

# A library has a branch and books
type Library {
  branch: String!
  books: [Book!]
}

# A book has a title and author
type Book {
  title: String!
  author: Author!
}

# An author has a name
type Author {
  name: String!
}

type Query {
  libraries: [Library]
}

有效的查询语法:

query GetBooksByLibrary {
  libraries {
    books {
      author {
        name
      }
    }
  }
}

此查询的结果解析器链与查询本身的层次结构匹配:
在这里插入图片描述
resolver 处理如下:

const { ApolloServer, gql } = require('apollo-server');

const libraries = [
  {
    branch: 'downtown'
  },
  {
    branch: 'riverside'
  },
];

const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin',
    branch: 'riverside'
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster',
    branch: 'downtown'
  },
];

// Schema definition
const typeDefs = gql`

# A library has a branch and books
  type Library {
    branch: String!
    books: [Book!]
  }

  # A book has a title and author
  type Book {
    title: String!
    author: Author!
  }

  # An author has a name
  type Author {
    name: String!
  }

  # Queries can fetch a list of libraries
  type Query {
    libraries: [Library]
  }
`;

// Resolver map
const resolvers = {
  Query: {
    libraries() {

      // Return our hardcoded array of libraries
      return libraries;
    }
  },
  Library: {
    books(parent) {

      // Filter the hardcoded array of books to only include
      // books that are located at the correct branch
      return books.filter(book => book.branch === parent.branch);
    }
  },
  Book: {

    // The parent resolver (Library.books) returns an object with the
    // author's name in the "author" field. Return a JSON object containing
    // the name, because this field expects an object.
    author(parent) {
      return {
        name: parent.author
      };
    }
  }

  // Because Book.author returns an object with a "name" field,
  // Apollo Server's default resolver for Author.name will work.
  // We don't need to define one.
};

// Pass schema definition and resolvers to the
// ApolloServer constructor
const server = new ApolloServer({ typeDefs, resolvers });

// Launch the server
server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

如果现在我们更新查询以同时询问每本书的标题:

query GetBooksByLibrary {
  libraries {
    books {

      title
      author {
        name
      }
    }
  }
}

解析器参数
在这里插入图片描述
context 参数
context 参数对于传递任何解析器可能需要的东西很有用,例如身份验证范围,数据库连接和自定义获取函数。如果您正在使用数据加载器跨解析器批处理请求,则也可以将它们附加到上下文。

解析器决不应破坏性地修改上下文参数。这样可以确保所有解析程序的一致性,并防止意外错误。

要为您的解析器提供上下文,请向 ApolloServer 构造函数添加上下文初始化函数。每个请求都会调用此函数,因此您可以根据请求的详细信息(例如HTTP标头)设置上下文。


// Constructor
const server = new ApolloServer({
  typeDefs,
  resolvers,
  // 任何 GraphQL 请求都会经过这里
  // 该函数接收一个参数:Request 请求对象
  context: (req) => ({// 返回对象,自定义数据,后续的每个 resolver 都可以直接获取
    authScope: getScope(req.headers.authorization)
  })
}));

// Example resolver
(parent, args, context, info) => {
  if(context.authScope !== ADMIN) throw new AuthenticationError('not admin');
  // Proceed
}

上下文初始化可以是异步的,从而可以完成数据库连接和其他操作:

context: async () => ({
  db: await client.connect(),
})

// Resolver
(parent, args, context, info) => {
  return context.db.query('SELECT * FROM table_name');
}

返回值
Apollo Server根据其类型对解析器函数的返回值进行不同的处理:
在这里插入图片描述
连接到数据源

连接到数据库

  • SQLDataSource from datasource-sql
  • MongoDataSource from apollo-datasource-mongodb

示例代码

index.js

const express = require('express')
const { ApolloServer, gql } = require('apollo-server-express')
const { User } = require('./models/')
const Users = require('./data-sources/user')

// 1. 定义 schema
const typeDefs = gql`
  type User {
    _id: ID!,
    name: String!,
    age: Int
  }

  type Query {
    users: [User!]
    user(id: ID!): User
  }
`

// 2. 定义 resolver
const resolvers = {
  // 所有的 Query 都走这里
  Query: {
    async users (parent, args, { dataSources }) {
      const users = await dataSources.users.getUsers()
      return users
    },
    async user (parent, { id }, { dataSources }) {
      const user = await dataSources.users.getUser(id)
      return user
    }
    // async users () {
    //   const users = await User.find()
    //   return users
    // },
    // async user (parent, { id }) {
    //   const user = await User.findById(id)
    //   return user
    // }
  }
}

const app = express()

const server = new ApolloServer({
  typeDefs,
  resolvers,
  // 任何 GraphQL 请求都会经过这里
  // 该函数接收一个参数:Request 请求对象
  context (req) {
    return { // 返回对象,自定义数据,后续的每个 resolver 都可以直接获取
      foo: 'bar'
    }
  },
  dataSources () {
    return {
      users: new Users(User)
    }
  }
})

// 将 Apollo-server 和 express 集合到一起
server.applyMiddleware({ app })

app.use((req, res) => {
  res.status(200)
  res.send('Hello!')
  res.end()
})

app.listen({ port: 4000 }, () =>
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
)

models/index.js

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/test', {
  useNewUrlParser: true,
  useUnifiedTopology: true
})

const db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error:'))
db.once('open', function () {
  // we're connected!
  console.log('连接数据库成功')
})

module.exports = {
  User: require('./user')
}

models/user.js

const mongoose = require('mongoose')

const userSchema = new mongoose.Schema({
  name: String,
  age: Number
})

module.exports = mongoose.model('User', userSchema)

data-sources/user.js

const { MongoDataSource } = require('apollo-datasource-mongodb')

class Users extends MongoDataSource {
  getUser(userId) {
    return this.findOneById(userId)

    // this.model // 访问数据模型对象
  }

  getUsers () {
    return this.model.find()
  }
}

module.exports = Users

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值