Lambda Function에서 시작하여 Amplify GraphiQL API 작업

23893 단어 AWSGraphQLAmplifytech
안녕하세요.
계속해서 윗글은 AWS Amplify에 관한 글입니다.
램바다 펀션에서 앰플리프에서 준비한 그래픽QL API를 조작하는 방법을 이번에 총결산했다.
나 혼자 하고 있어. 까다로워. 같은 상황에 있는 사람에게 참고를 해줄 수 있다면 기쁠 거야!

Lambda에서 GraphiQL API를 실행하는 것은 무엇입니까?


AWS Amplify의 GraphiQL API와 관련해서는 자세한 기사가 많기 때문에 맡겼는데, 왜 이런 방법을 쓰는지 간단히 설명해 드리겠습니다.
다음은 제가 이번에 사용한 GraphiQL 모드의 일부분입니다.
type RequestRecord @model @auth(rules: [
  { allow: owner, ownerField: "User" },
  { allow: private, provider: iam }
]){
  RequestId: String! @primaryKey
  Status: Int!
  User: String
}
사용자가 데이터를 업로드한 후 클라우드에서 데이터를 편집하고 다운로드하는 절차.따라서 Status 관리 파일의 처리 상황은 사용자 측면의 화면에 수시로 반영된다.따라서 처리 진행 상황에 따라 다시 작성Status해야 하며, 이는 정면부터 시작할 수 없기 때문에 람바다 함수부터 조작해야 한다.
이처럼 람바다의 변화가 사용자 측에 반영될 때 필요하기 때문에 사용 빈도가 높다.
다음은 이 모드를 사용하여 설명합니다.또한 다음 설정은 완성을 전제로 한다.
  • 프로젝트 전체 유효성 Amplify: amplify init
  • GraphiQL API가 설정됨: amplify add api
  • Function 추가됨: amplify add function
  • Lambda를 사용한 Graph QL 작업에 필요한 권한


    GraphiQL과 Function 두 가지 측면에서 권한의 설정을 볼 수 있습니다.다음은 Lambda 함수를 Amplify의 자원 이름 Function이라고 합니다.

    GraphiQL 면


    Amplify의 GraphiQL에는 몇 가지 인증 방법이 준비되어 있습니다.
  • Cognito User Pool (userPools)
  • IAM Policy (iam)
  • OpenID Connect (oidc)
  • API Key (apiKey)
  • Lambda Authorizer (lambda)
  • https://docs.amplify.aws/cli/graphql/authorization-rules/#authorization-strategies
    Amplify에 Auth가 설정된 경우 기본적으로 인증을 위해 Cognito User Pool이 사용되지만 Function에서 사용할 수 없습니다.Function에서 유효한 인증 방법은 다음 두 가지입니다.
  • API Key
  • IAM Policy
  • !
    Lambda Authorizer는 스스로 인증 방법을 준비한다는 뜻으로 Function용 인증이 아닙니다!
    이 가운데 API 키는 유효기간이 가장 길고 365일로 제한돼 있어 장기사용에 적합하지 않다.따라서 IAM Policy를 사용하는 방법이 편리합니다.
    GraphiQL은 여러 인증 방법을 설정할 수 있기 때문에 Cognito User Pool과 IAM Policy를 한 모델에 추가할 수 있습니다!
    다음은 방법입니다.
  • GraphiQL 모드 편집
    IAM 인증이 필요한 모델@auth에 IAM 인증{ allow: private, provider: iam }을 추가한다.
  • https://docs.amplify.aws/cli/graphql/authorization-rules/#configure-multiple-authorization-rules
  • GraphiQL 설정 업데이트
    다음과 같이 IAM 인증을 GraphiQL 설정에 추가합니다.
  • $ amplify update api
    
    ? Select a setting to edit.
    > Authorization modes
    ? Configure additional auth types?
    > yes
    ? Choose the additional authorization types you want to configure for the API.
    > IAM
    
    ✅ Successfully updated resource
    
  • 리소스 업데이트: amplify push
  • Function 측


    그런 다음 Function 면을 설정합니다.다음은 펀션 이름gql-function, API 이름gql-api이다.
    CLI에서 API에 액세스할 수 있습니다.
    $ amplify update function
    
    ? Select the Lambda function you want to update.
    > gql-function
    ? Which setting do you want to update?
    > Resource access permissions
    ? Select the categories you want this function to have access to.
    > API
    ? Select the operations you want to permit on gql-api.
    > Query, Mutate
    
    You can access the following resource attributes as environment variables from your Lambda function
      API_GQL-API_GRAPHQLAPIENDPOINTOUTPUT
      API_GQL-API_GRAPHQLAPIIDOUTPUT
    
    이렇게 되면 펀션은 GraphiQL에 대한 접근 허가를 받게 된다.마지막으로 GraphiQL 끝점을 저장하는 환경 변수 이름이 표시됩니다.

    Function 설치 방법


    그런 다음 IAM 인증을 사용하여 Lambda에서 GraphiQL에 액세스할 코드를 준비합니다.이번에는 예를 들어 이미 존재하는 기록 내용을 덮어쓰는 함수를 준비한다.
    다음은 프로젝트의 노선을 ~로 표시한다.
    설치에 필요한 패키지를 먼저 설치합니다.
    package.json
    {
      "name": "gql-function",
      "dependencies": {
        "aws-appsync": "^4.0.0",
        "aws-sdk": "^2.1084.0",
        "graphql": "^15.7.0",
        "graphql-tag": "^2.12.6",
        "isomorphic-fetch": "^3.0.0",
      }
    }
    
    이따가 나올 거예요. 포장 버전이 중요해요.
    다음은GraphiQL의 함수를 조작할 준비를 합니다.이번 모드에서는 RequestId가 주요 열쇠이기 때문에 Mutation이 지정해야 한다.
    graphqlOperations.ts
    import * as AWS from 'aws-sdk';
    import { gql } from 'graphql-tag';
    import { print } from 'graphql';
    import AWSAppSyncClient from 'aws-appsync';
    import 'isomorphic-fetch';
    
    // デフォルトで~/src/graphql/mutation.ts に作成されるクエリを
    // そのまま持ってきています。
    // gqlにテンプレートリテラルとして読み込ませます。
    const updateRecord = gql`
      mutation UpdateRequestRecord(
        $input: UpdateRequestRecordInput!
        $condition: ModelRequestRecordConditionInput
      ) {
        updateRequestRecord(input: $input, condition: $condition) {
          RequestId
          Status
          User
          createdAt
          updatedAt
        }
      }
    `;
    
    const gqlUpdate = async (
      url: string,
      region: string,
      RequestId: string,
      Record: object
    ) => {
      const client = new AWSAppSyncClient({
        url,
        region,
        auth: {
          type: 'AWS_IAM',
          credentials: (AWS.config.credentials ?? null)
        },
        disableOffline: true
      });
      try {
        await client.mutate({
          mutation: updateRecord,
          variables: {
            input: {
              RequestId,
              ...Record
            }
          }
        });
      } catch (error) {
        console.error(error);
      }
    };
    
    export default gqlUpdate;
    
    마지막으로 상기 함수update를 호출하는 함수를 만듭니다.
    index.ts
    import gqlUpdate from './graphqlOperations';
    
    export const handler = async () => {
      // Any statements here...
      const requestId = 'hogehoge';
      await gqlUpdate(
        process.env['API_GQL-API_GRAPHQLAPIENDPOINTOUTPUT'],
        process.env['REGION'],
        requestId,
        { Status: 0 }
      );
    };
    
    이상은 완성!!
    실제로 여기까지의 내용은 어떤 기사에도 적혀 있다.

    여기서부터 힘들었어...


    너무 고생 많았어요.다음은 중점을 열거해 봅시다.

    TypeError: Cannot convert undefined or null to object


    이상의 코드는 모두 썼지만 음반은 조금도 업데이트되지 않았다.일지를 확인하고 본 적 없는 실수를 토해냈다.
    {
        "errorType": "TypeError",
        "errorMessage": "Cannot convert undefined or null to object",
        "stack": [
            "TypeError: Cannot convert undefined or null to object",
            "    at Function.keys (<anonymous>)",
            "    at /var/task/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:331:12",
            "    at /var/task/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:2:68",
            "    at Object.<anonymous> (/var/task/node_modules/apollo-cache-inmemory/lib/bundle.umd.js:5:2)",
            "    at Module._compile (internal/modules/cjs/loader.js:1085:14)",
            "    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)",
            "    at Module.load (internal/modules/cjs/loader.js:950:32)",
            "    at Function.Module._load (internal/modules/cjs/loader.js:790:12)",
            "    at Module.require (internal/modules/cjs/loader.js:974:19)",
            "    at require (internal/modules/cjs/helpers.js:101:18)"
        ]
    }
    
    "undefined나null값을object로 변환할 수 없습니다...??사실대로 말하지만 undefined/null로 변하는 값TS 군은 본래 나에게 일깨워 주었기 때문에 내 코드는 문제가 없을 것이다.
    잘못된 추적으로만 봐apollo-cache-inmemory에는 문제가 있는 것 같지만, 그래픽QL 포장에 사용된 의존 모듈이어서 원래 괴롭힘을 당하지 않는다.
    이건 다음 이슈로 해결했습니다.
    https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/691
    즉, 새로운 GraphiQL 버전이 AWS AppSync Client와 대응하지 않아 오류가 발생할 수 있습니다!
    설치[email protected]는 이 오류를 피할 수 있다[1]고 적혀 있다.

    fetch is not found globally and no fetcher passed


    다음에 당할 실수는 여기에 있다.Fetch 모듈 오류가 없습니다.
    잘못된 메시지에 해결 방법이 진지하게 적혀 있다.
    Error: fetch is not found globally and no fetcher passed,
    to fix pass a fetch for your environment like https://www.npmjs.com/package/node-fetch."
    
    For example:
    import fetch from 'node-fetch';
    import { createHttpLink } from 'apollo-link-http';
    const link = createHttpLink({ uri: '/graphql', fetch: fetch });
    
    상기 코드와 같이 이 오류는 가져오기isomorphic-fetch를 통해 피할 수 있다.또한 오류 메시지에 node-fetch를 넣어 피할 수도 있다.

    Not Authorized to access ** on type Mutation


    여러 Function에서 GraphiQL API에 액세스할 수 있도록 허용할 때 토출된 오류입니다.
    ERROR	ApolloError: GraphQL error: Not Authorized to access ** on type Mutation
      graphQLErrors: [
        {
          path: [Array],
          data: null,
          errorType: 'Unauthorized',
          errorInfo: null,
          locations: [Array],
          message: 'Not Authorized to access ** on type Mutation'
        }
      ],
      networkError: null,
      extraInfo: undefined
    }
    
    주로 권한이 없는 오류입니다.그러나 위의 Function 측 권한 설정은 모든 Function에 대해 수행됩니다.Lambda 콘솔에서도 AppSync에 액세스할 수 있습니다.왜...
    이 문제는 아래의 이슈로 해결했다.
    https://github.com/aws-amplify/amplify-cli/issues/9866#issuecomment-1074698348 ~/amplify/backend/api/(API名)/ 안에 다음 파일을 만들면 오류가 발생하지 않습니다!
    custom-roles.json
    {
      "adminRoleNames": ["arn:aws:sts::(アカウント番号):assumed-role"]
    }
    
    그런데 이게 도대체 어떻게 된 일입니까?문제의 근원은 IAM의 구조 깊은 곳에 뿌리를 두고 있다.
    그래피QL의 IAM 인증은 앰플리프에서 제작된 IAM 롤에만 유효하지만, 앰플리프 이외에 제작된 IAM 롤에 권한을 부여하는 방법도 있다.다음 글에는 그 방법이 쓰여 있다.
    https://docs.amplify.aws/cli/graphql/authorization-rules/#use-iam-authorization-within-the-appsync-console
    이 권한을 부여한 IAM Role에 Asseumed-Role을 더하는 것이 위 Issue에 적힌 방법이다.'지정된 계정 내에서 앱Sync 권한을 가진 폴리시'를 포함한 롤의 모든 권한에 대한 접근[2]이 허용된다는 것이다.
    조금 더 쉽게 깨물어라.
    Lambda 함수에 대해 일반적으로 Lambda Execution Role을 설정하지만, Role에 Policy를 추가하여 구체적인 접근 가능한 서비스를 선택합니다.Lambda ExecutionRole은 운행 시 일정 기간 신용장을 필요로 하기 때문에 ARNarn:aws:sts::(アカウント番号):assumed-role/LambdaExecutionRole/(セッション名)을 보유한 사용자를 제공할 것이다.
    이 ARN은 매번 변하기 때문에 아까custom-roles.json에 지정할 수 없습니다.
    따라서 지정assumed-role을 통해 계정 내의 AssumeRole에서 만든 사용자의 접근을 허용할 수 있다.
    그러나 주의해야 할 것은 원래의 Policy가 AppSync의 허가 없이는 접근할 수 없기 때문에 모든 사용자가 허용하는 것은 아니다.
    Role·Policy·AsseumeRole의 관계, 아래의 기사는 매우 이해하기 쉽다!
    https://dev.classmethod.jp/articles/iam-role-passrole-assumerole/

    총결산


    결국 모든 잘못이 해결되었으니 활용할 수 있다!
    램바다 함수에서 앰플리프를 조작한 그래피큐어 API의 실례는 인터넷 기사에도 기재되지 않아 상당히 당혹스러웠다.
    같은 실수를 하는 분들에게 참고가 된다면 정말 기쁘겠습니다!
    각주
    집필할 때의 최신 버전은[email protected]네.↩︎
    이 방면의 해석은 나도 매우 이해하기 어려우니, 만약 틀린 곳이 있으면 나에게 알려주세요.↩︎

    좋은 웹페이지 즐겨찾기