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

参考资料

GraphQL基础概念

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

GraphQL相比REST API 的好处如下

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

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

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-klQaAYsT-1678607218291)(assets/image-20230306103904158.png)]

使用GraphQL的步骤

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

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

图片来自,https://mp.weixin.qq.com/s/i7ZhYNWEteEAkfUMoCgoWw
在这里插入图片描述

关于GraphQL支持的数据操作

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

关于GraphQL的图表模式(Schema)

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

  • type类似golang中的结构体

    图片

  • 类型修饰符。类似于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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6exijCwg-1678607218295)(assets/image-20230306113844956.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRyuxR8Z-1678607218296)(assets/image-20230306114315803.png)]

在控制台测试createevent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rwYu3wBQ-1678607218297)(assets/image-20230311232959220.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8JFSNdX-1678607218298)(assets/image-20230311232302842.png)]

$ 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
在这里插入图片描述

查看dynamodb数据写入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXVhYBzU-1678607218299)(assets/image-20230311232617939.png)]

关于入门示例的内容,之后详细分析。作者接触过一点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的客户端请求即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值