CodingYang

vuePress-theme-reco Rackar    2018 - 2024
CodingYang CodingYang

Choose mode

  • dark
  • auto
  • light
首页
类别
  • 技术
  • 个人
  • 思考
  • 儿童
标签
时间线
联系
  • 关于
  • RSS订阅 (opens new window)
  • GitHub (opens new window)
  • 简书 (opens new window)
  • CSDN (opens new window)
  • WeChat (opens new window)
GitHub (opens new window)
author-avatar

Rackar

67

文章

44

标签

首页
类别
  • 技术
  • 个人
  • 思考
  • 儿童
标签
时间线
联系
  • 关于
  • RSS订阅 (opens new window)
  • GitHub (opens new window)
  • 简书 (opens new window)
  • CSDN (opens new window)
  • WeChat (opens new window)
GitHub (opens new window)
  • apollo 使用 VUE 示例

    • 简介
      • 服务器端起步
        • 新建模式
        • 连接数据

    apollo 使用 VUE 示例

    vuePress-theme-reco Rackar    2018 - 2024

    apollo 使用 VUE 示例


    Rackar 2020-01-13 Node.js Vue.js

    # 简介

    Apollo 是 GraphQL 的一种实现,解决 RESTful API 的一些问题。

    官网教程:https://www.apollographql.com/docs/tutorial/introduction/

    前端库: npm install --save vue-apollo apollo-client

    我们按照官网示例做一遍教程,构建一个预订 SpaceX 发射的小应用。主要包括:

    • 登录页
    • 即将发射清单
    • 发射细节
    • 用户资料页
    • 购物车

    其他参考链接 https://dev.to/alvarojsnish/graphql-mongodb-the-easy-way-ngc

    https://stackoverflow.com/questions/52382639/connect-apollo-server-with-mongodb-mongoos/52462031

    # 服务器端起步

    下载教程代码:

    git clone https://github.com/apollographql/fullstack-tutorial.git

    起步使用 start 目录,完成教程的代码参考 final 目录。

    先编辑 server 目录,也就是服务器。

    新建模式之前,先设置 API 服务器。Apollo 服务器是一个连接数据的库。先安装依赖:

    cd start/server && npm install

    其中核心的依赖是:apollo-server 和 graphql 这两个包。

    先修改入口 src/index.js 文件:

    const { ApolloServer } = require("apollo-server");
    const typeDefs = require("./schema");
    
    const server = new ApolloServer({ typeDefs });
    
    1
    2
    3
    4

    这里导入了 ApolloServer 类,并用编写的模式新建服务器实例。下面我们编写模式。

    # 新建模式

    可以把模式(schema)当做所有数据及关系的蓝图。模式还定义了我们可以通过查询获取哪些数据以及更新哪些数据。模式是强类型的。

    官方文档认为是模式是前端和后端团队进行协作的理想结合点,建议团队练习 Schema First Development。

    参照我们的目标,应用需要:

    • 获取所有即将到来的火箭发射
    • 通过其 ID 获取特定的发射
    • 登录用户
    • 预订发射(登录状态下)
    • 取消发射(登录状态下)

    基于这些要求去构建模式。修改 src/schema.js:

    const { gql } = require("apollo-server");
    
    const typeDefs = gql`
      # Your schema will go here
    `;
    
    module.exports = typeDefs;
    
    1
    2
    3
    4
    5
    6
    7

    导入 gql。并用示例的方式写入模式代码。

    语言使用的是 SDL 语言(GraphQL's schema definition language)。看着有一点像 TypeScript。

    从查询类型 (Query type) 开始,查询类型主要是用来描述我们的数据,是模式的入口点。现在我们加入一些代码:

    const { gql } = require("apollo-server");
    
    const typeDefs = gql(`
      type Query {
        launches: [Launch]!
        launch(id: ID!): Launch
        # Queries for the current user
        me: User
      }
    `);
    
    module.exports = typeDefs;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    定义了非空数组 launches,定义了通过 id 查询单个 launch,还定义了 me 获取用户信息。这里我们使用 Launch 和 User 两个对象类型。我们下面来定义一下他们。

    type Launch {
      id: ID!
      site: String
      mission: Mission
      rocket: Rocket
      isBooked: Boolean!
    }
    
    type Rocket {
      id: ID!
      name: String
      type: String
    }
    
    type User {
      id: ID!
      email: String!
      trips: [Launch]!
    }
    
    type Mission {
      name: String
      missionPatch(size: PatchSize): String
    }
    
    enum PatchSize {
      SMALL
      LARGE
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    确实很像 TS,有很多基础类型如 ID, String, Boolean, Int 等,也可以使用自定义类型。

    missionPatch 这里也加入了参数,因为 GraphQL 非常灵活,可以在任何字段里包含参数。还可以定义枚举类型。

    详细用法参考 (opens new window)

    然后是 Mutation type 变动类型。主要是修改数据的入口点。

    type Mutation {
      # if false, booking trips failed -- check errors
      bookTrips(launchIds: [ID]!): TripUpdateResponse!
    
      # if false, cancellation failed -- check errors
      cancelTrip(launchId: ID!): TripUpdateResponse!
    
      login(email: String): String # login token
    }
    
    type TripUpdateResponse {
      success: Boolean!
      message: String
      launches: [Launch]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    bookTrips 和 cancelTrip 这两个变动都会接受一个参数,并返回一个更新响应 TripUpdateResponse。在响应里不光包含了状态和信息,还包括了更新后的数据。这是一种好习惯,可以让客户端自动缓存更新。

    现在设置服务器监听,修改 index.js:

    const { ApolloServer } = require("apollo-server");
    const typeDefs = require("./schema");
    
    const server = new ApolloServer({ typeDefs });
    
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
      console.log(`GraphQL Playground run at ${url}graphql`);
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    运行 npm start,服务器默认端口 4000。

    默认 Apollo 开发环境会开启 GraphQL Playground (opens new window)。这是一个交互式浏览器 IDE,可以用来浏览和测试。生产环境自动关闭。

    点击右侧的 schema 按钮和 docs 按钮,可以直接浏览自动生成文档和模式代码。

    # 连接数据

    现在我们可以通过建立的模式来连接数据源了。我们的数据源可以是任意服务,比如业务逻辑,REST APIs, 数据库, 或者 gRPC 服务。

    下面我们会试试连接 REST API 和 SQL 数据库。新建src/datasources/launch.js

    const { RESTDataSource } = require("apollo-datasource-rest");
    
    class LaunchAPI extends RESTDataSource {
      constructor() {
        super();
        this.baseURL = "https://api.spacexdata.com/v2/";
      }
    }
    
    module.exports = LaunchAPI;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    设定了 api 基本地址。RESTDataSource 还设置了内存缓存,无需额外设置即可缓存来自 REST 资源的响应。我们称此为部分查询缓存。此缓存的优点在于,您可以重用 REST API 公开的现有缓存逻辑。

    然后写数据拉取方法,首先添加一个获取所有发射:

    async getAllLaunches() {
      const response = await this.get('launches');
      return Array.isArray(response)
        ? response.map(launch => this.launchReducer(launch))
        : [];
    }
    
    1
    2
    3
    4
    5
    6

    get 这句代码就是发送了 get 请求到https://api.spacexdata.com/v2/launches。获取的数据数组又经过了一个map遍历方法,也就是对每个数据进行了操作,我们接着写这个方法的实现:

    launchReducer(launch) {
      return {
        id: launch.flight_number || 0,
        cursor: `${launch.launch_date_unix}`,
        site: launch.launch_site && launch.launch_site.site_name,
        mission: {
          name: launch.mission_name,
          missionPatchSmall: launch.links.mission_patch_small,
          missionPatchLarge: launch.links.mission_patch,
        },
        rocket: {
          id: launch.rocket.rocket_id,
          name: launch.rocket.rocket_name,
          type: launch.rocket.rocket_type,
        },
      };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    这个方法类似一个重映射,将 api 里的字段映射到了我们定义的 schema 中的字段,同时还做了数据的精简和组织。

    然后再增加两个通过 id 或 id 列表来获取特定发射数据的方法:

    async getLaunchById({ launchId }) {
      const response = await this.get('launches', { flight_number: launchId });
      return this.launchReducer(response[0]);
    }
    
    getLaunchesByIds({ launchIds }) {
      return Promise.all(
        launchIds.map(launchId => this.getLaunchById({ launchId })),
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    通过 id 获取数据,如果是数组,则分别调用单个 id 获取的方法。现在 rest 方式已经成功啦。

    user.js 在示例中已经提供了,可以先读一下。数据结构已经准备好了。

    然后开始添加到 server,修改 index.js

    const { ApolloServer } = require("apollo-server");
    const typeDefs = require("./schema");
    const { createStore } = require("./utils");
    
    const LaunchAPI = require("./datasources/launch");
    const UserAPI = require("./datasources/user");
    
    const store = createStore();
    
    const server = new ApolloServer({
      typeDefs,
      dataSources: () => ({
        launchAPI: new LaunchAPI(),
        userAPI: new UserAPI({ store })
      })
    });
    
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    参与编辑此文章 (opens new window)
    更新于: 3/22/2020, 11:16:45 PM