aws appsync GraphQL基础概念和appsync快速入门示例

参考资料

  • What is AWS AppSync?
  • RESTful真垃圾?试试 GraphQL?
  • 什么是 GraphQL?核心概念解析

GraphQL基础概念

GraphQL是由Facebook开发并开源的一种新的API标准,它提供了一种比REST更有效、更强大和更灵活的替代方案。GraphQL是数据库无关的,可以理解为GraphQL是基于API之上的一层封装,目的是为了更好,更灵活的适用于业务的需求变化

GraphQL相比REST API 的好处如下

REST API GraphQL
灵活性 接口灵活性差、接口操作流程繁琐 声明式数据获取,数据查询流程简洁
拓展性 不断编写新接口(依赖于服务端) 一个微服务仅暴露一个 GraphQL 层,消除了服务器对数据格式的硬性规定
强依赖 基于HTTP协议,不能灵活选择网络协议 传输层无关、数据库技术无关,获得更灵活的技术栈选择

REST和GraphQL数据的请求过程的区别

图片来自 RESTful真垃圾?试试 GraphQL?

与REST多个endpoint不同,每一个的 GraphQL 服务其实对外只提供了一个用于调用内部接口的端点,所有的请求都访问这个暴露出来的唯一端点。

多个请求变成了一个请求的不同字段,从原有的分散式请求变成了集中式的请求,因此GraphQL又可以被看成是图数据库的形式

aws appsync GraphQL基础概念和appsync快速入门示例_第1张图片

使用GraphQL的步骤

  • 设计数据模型,描述数据对象
  • 前端使用模式查询语言(Schema)进行声明式数据获取
  • 后端根据前端请求自动组装字段

下图是GraphQL 应用的基本架构,客户端只和 GraphQL 层进行 API 交互

图片来自,https://mp.weixin.qq.com/s/i7ZhYNWEteEAkfUMoCgoWw
aws appsync GraphQL基础概念和appsync快速入门示例_第2张图片

关于GraphQL支持的数据操作

  • 查询(Query): 获取数据的基本查询
  • 变更(Mutation): 支持对数据的增删改等操作
  • 订阅(Subscription): 用于监听数据变动、并靠websocket等协议推送变动的消息给对方

关于GraphQL的图表模式(Schema)

  • schema即GraphQL的语法,指定了数据类型(type)的定义和支持

  • type类似golang中的结构体

    aws appsync GraphQL基础概念和appsync快速入门示例_第3张图片

  • 类型修饰符。类似于java修饰符,控制数据类型的定义和表现

    • 列表:[Type]
    • 非空:Type!
    • 列表非空:[Type]!
    • 非空列表,列表内容类型非空:[Type!]!

关于GraphQL接入架构

AWS AppSync快速入门示例

AWS AppSync是一个完全托管的 GraphQL 服务,包含实时订阅、离线编程和同步、企业级安全特性以及细粒度的授权控制

来看看chatgpt如何解释appsync和GraphQL的关系

AWS AppSync is a managed service by Amazon Web Services (AWS) that provides a serverless GraphQL API to interact with data sources like AWS DynamoDB, AWS Lambda, HTTP data sources, and more. On the other hand, GraphQL is a query language for APIs, which can be used with various backend technologies, including AWS AppSync.

One major difference between GraphQL and AWS AppSync is that AWS AppSync includes additional functionality beyond the core GraphQL specification. For example, it supports real-time data synchronization and offline capabilities through its built-in support for AWS DynamoDB, which allows clients to interact with data even when they are offline.

Another difference is that AWS AppSync is tightly integrated with other AWS services, allowing developers to easily build and deploy GraphQL APIs that interact with other AWS services like Amazon S3, Amazon Cognito, and AWS Lambda.

从官方文档我们也能看到appsync提供的额外功能

  • GraphQL编辑架构,自动ddb生成GraphQL
  • 缓存数据
  • 认证服务集成,细粒度访问控制

下面做一下快速入门的示例

从现有 Amazon DynamoDB 表导入以创建实时和脱机 GraphQL API

官方给了一个快读启动的项目demo

https://github.com/amazon-archives/aws-mobile-appsync-events-starter-react#readme

This is a Starter React application for using the Sample app in the AWS AppSync console when building your GraphQL API

aws appsync GraphQL基础概念和appsync快速入门示例_第4张图片

示例程序会自动创建两张dynamodb table作为数据源

aws appsync GraphQL基础概念和appsync快速入门示例_第5张图片

在控制台测试createevent

aws appsync GraphQL基础概念和appsync快速入门示例_第6张图片

拉取示例程序,将aws-export.js文件中的内容替换为以下

aws appsync GraphQL基础概念和appsync快速入门示例_第7张图片

$ cat src/aws-exports.js 
const awsmobile =  {
    "aws_appsync_graphqlEndpoint": "https://2xxxxxxxxca.appsync-api.cn-north-1.amazonaws.com.cn/graphql",
    "aws_appsync_region": "cn-north-1",
    "aws_appsync_authenticationType": "API_KEY",
    "aws_appsync_apiKey": "da2-xxxxxxxxxxxxxxxxxxxwm",
};

启动服务,之前在控制台测试的数据My First Event已经同步到页面上

$ npm run start
You can now view aws-mobile-appsync-events-starter-react in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://172.31.18.4:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

尝试在前端页面创建一个新的event
aws appsync GraphQL基础概念和appsync快速入门示例_第8张图片

查看dynamodb数据写入

aws appsync GraphQL基础概念和appsync快速入门示例_第9张图片

关于入门示例的内容,之后详细分析。作者接触过一点vue,硬着头皮看看这个react项目

https://github.com/amazon-archives/aws-mobile-appsync-events-starter-react/blob/master/README.md

入口文件

  • 加载样式和依赖
  • 挂载组件到root上
  • 注册了一个registerServiceWorker
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

查看APP文件,忽略样式表部分

  • 加载路由等依赖

  • 导入aws配置文件appSyncConfig,这是app连接appsync的配置,aws_appsync_apiKey类似于密钥

  • Apollo 是在应用中使用 GraphQL 的一套工具,apollo在vue中的用法可以借鉴一下

    还提供了一个示例的vue项目解释apollo的用法

  • 注册了一个全局的appsync客户端client

  • 之后我们主要关注AllEvents,NewEvent和ViewEvent组件即可

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import "semantic-ui-css/semantic.min.css";
import 'react-datepicker/dist/react-datepicker.css';

import appSyncConfig from "./aws-exports";
import { ApolloProvider } from "react-apollo";
import AWSAppSyncClient, { defaultDataIdFromObject } from "aws-appsync";
import { Rehydrated } from "aws-appsync-react";

import './App.css';
import AllEvents from './Components/AllEvents';
import NewEvent from './Components/NewEvent';
import ViewEvent from './Components/ViewEvent';

const Home = () => (
  <div className="ui container">
    <AllEvents />
  </div>
);

// 注册路由,分别对应主页,查看enent和创建event的组件
const App = () => (
  <Router>
    <div>
      <Route exact={true} path="/" component={Home} />
      <Route path="/event/:id" component={ViewEvent} />
      <Route path="/newEvent" component={NewEvent} />
    </div>
  </Router>
);

// 创建appsync客户端
const client = new AWSAppSyncClient({
  url: appSyncConfig.aws_appsync_graphqlEndpoint,
  region: appSyncConfig.aws_appsync_region,
  auth: {
    type: appSyncConfig.aws_appsync_authenticationType,
    apiKey: appSyncConfig.aws_appsync_apiKey,
  },
  cacheOptions: {
    dataIdFromObject: (obj) => {
      let id = defaultDataIdFromObject(obj);

      if (!id) {
        const { __typename: typename } = obj;
        switch (typename) {
          case 'Comment':
            return `${typename}:${obj.commentId}`;
          default:
            return id;
        }
      }

      return id;
    }
  }
});

const WithProvider = () => (
  <ApolloProvider client={client}>
    <Rehydrated>
      <App />
    </Rehydrated>
  </ApolloProvider>
);

export default WithProvider;

查看allevent组件

  • 使用appsync客户端执行查询操作
// src/Components/AllEvents.js
import React, { Component } from "react";
import { Link } from "react-router-dom";

import { graphql, compose, withApollo } from "react-apollo";
import QueryAllEvents from "../GraphQL/QueryAllEvents";
import MutationDeleteEvent from "../GraphQL/MutationDeleteEvent";

import moment from "moment";

class AllEvents extends Component {

    state = {
        busy: false,
    }

    static defaultProps = {
        events: [],
        deleteEvent: () => null,
    }

    async handleDeleteClick(event, e) {
        e.preventDefault();

        if (window.confirm(`Are you sure you want to delete event ${event.id}`)) {
            const { deleteEvent } = this.props;

            await deleteEvent(event);
        }
    }
	
    handleSync = async () => {
        const { client } = this.props;
        const query = QueryAllEvents;
        
        // import gql from "graphql-tag";
        // export default gql(`
        // query {
        //   listEvents(limit: 1000) {
        //     items {
        //       id
        //       name
        //       where
        //       when
        //       description
        //       comments {
        //         items {
        //           commentId
        //         }
        //       }
        //     }
        //   }
        // }

        this.setState({ busy: true });
		// 执行查询操作
        await client.query({
            query,
            fetchPolicy: 'network-only',
        });

        this.setState({ busy: false });
    }

    renderEvent = (event) => (
        ...
    }

export default withApollo(compose(
    graphql(
        QueryAllEvents,
        {
            options: {
                fetchPolicy: 'cache-first',
            },
            props: ({ data: { listEvents = { items: [] } } }) => ({
                events: listEvents.items
            })
        }
    ),
    graphql(
        MutationDeleteEvent,
        {
            options: {
                update: (proxy, { data: { deleteEvent } }) => {
                    const query = QueryAllEvents;
                    const data = proxy.readQuery({ query });

                    data.listEvents.items = data.listEvents.items.filter(event => event.id !== deleteEvent.id);

                    proxy.writeQuery({ query, data });
                }
            },
            props: (props) => ({
                deleteEvent: (event) => {
                    return props.mutate({
                        variables: { id: event.id },
                        optimisticResponse: () => ({
                            deleteEvent: {
                                ...event, __typename: 'Event', comments: { __typename: 'CommentConnection', items: [] }
                            }
                        }),
                    });
                }
            })
        }
    )
)(AllEvents));

在newevent也同样使用graphql封装graphql查询,并监听

export default graphql(
    MutationCreateEvent,
    // import gql from "graphql-tag";

    // export default gql(`
    // mutation($name: String! $when: String! $where: String! $description: String!) {
    //   createEvent(
    //     name: $name
    //     when: $when
    //     where: $where
    //     description: $description
    //   ) {
    //     id
    //     name
    //     where
    //     when
    //     description
    //     comments {
    //       items {
    //         commentId
    //       }
    //     }
    //   }
    // }`);

    {
        props: (props) => ({
            createEvent: (event) => {
                return props.mutate({
                    update: (proxy, { data: { createEvent } }) => {
                        // Update QueryAllEvents
                        const query = QueryAllEvents;
                        const data = proxy.readQuery({ query });

                        data.listEvents.items = [...data.listEvents.items.filter(e => e.id !== createEvent.id), createEvent];

                        proxy.writeQuery({ query, data });

                        // Create cache entry for QueryGetEvent
                        const query2 = QueryGetEvent;
                        const variables = { id: createEvent.id };
                        const data2 = { getEvent: { ...createEvent } };

                        proxy.writeQuery({ query: query2, variables, data: data2 });
                    },
                    variables: event,
                    optimisticResponse: () => ({
                        createEvent: {
                            ...event, id: uuid(), __typename: 'Event', comments: { __typename: 'CommentConnection', items: [] }
                        }
                    }),
                })
            }
        })
    }
)(NewEvent);

由此可知,前端框架通过apollo工具解析gql语句,进行graphql风格的增删改查请求,而appsync客户端被apollo调用,因此我们可以只关注appsync的客户端请求即可

你可能感兴趣的:(AWS,aws,graphql,java)