2

I'm fairly new to GraphQL and AWS AppSync, and I'm running into an issue downloading files (PDFs and PNGs) from a public S3 bucket via AWS AppSync. I've looked at dozens of tutorials and dug through a mountain of documentation, and I'm just not certain what's going on at this point. This may be nothing more than a misunderstanding about the nature of GraphQL or AppSync functionality, but I'm completely stumped.

For reference, I've heavily sourced from other posts like How to upload file to AWS S3 using AWS AppSync (specifically, from the suggestions by the accepted answer author), but none of the solutions (or the variations I've attempted) are working.

The Facts

  • S3 bucket is publicly accessible – i.e., included folders and files are not tied to individual users with Cognito credentials
  • Files are uploaded to S3 outside of AppSync (so there's no GraphQL mutation); it's a manual file upload
  • Schema works for all other queries and mutations
  • We are using AWS Cognito to authenticate users and queries

Abridged Schema and DynamoDB Items

Here's an abridged version of the relevant GraphQL schema types:

type MetroCard implements TripCard {
    id: ID!
    cardType: String!
    resIds: String!
    data: MetroData!
    file: S3Object
}

type MetroData implements DataType {
    sourceURL: String!
    sourceFileURL: String
    metroName: String!
}

type S3Object {
    bucket: String!
    region: String!
    key: String!
}

Metadata about the files is stored in DynamoDB and looks something like this:

{
    "data": {
        "metroName": "São Paulo Metro",
        "sourceFileURL": "http://www.metro.sp.gov.br/pdf/mapa-da-rede-metro.pdf",
        "sourceURL": "http://www.metro.sp.gov.br/en/your-trip/index.aspx"
    },
    "file": {
        "bucket": "test-images",
        "key": "some_folder/sub_folder/bra-sbgr-metro-map.pdf",
        "region": "us-east-1"
    },
    "id": "info/en/bra/sbgr/metro"
}

VTL Request/Response Resolvers

For our getMetroCard(id: ID!): MetroCard query, the mapping templates are pretty vanilla. The request template is a standard query on a DynamoDB table. The response template is a basic $util.toJson($ctx.result).

For the field-level resolver on MetroCard.file, we've attached a local data source with an empty {} payload for the request and the following for the response (see referenced link for reasoning):

$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file)) // we've played with this bit in a couple of ways, including simply returning $context.result but no change

Results

All of the query fields resolve appropriately; however, the file field inevitably always returns null no matter what the field-level resolver is mapped to. Interestingly, I've noticed in the CloudWatch logs the value of context.result does change from null to {} with the above mapping template.

Questions

Given the above, I have several questions:

  1. Does AppSync file download require files to be uploaded to S3 with user credentials through a mutation with a complex object handler in order to make them retrievable?
  2. What should a successful response look like in the AppSync console return – i.e., I have no client implementation (like a React Native app) to test successful file downloads? More directly, is it actually retrieving the files, and I just don't know it? (Note: I actually have tested it briefly with a React Native client, but nothing rendered so I've just been using the AppSync console returns as direction ever since.)
  3. Does it make more sense to remove the file download process entirely from our schema? (I'm assuming the answers I need reveal that AppSync just wasn't built for file transfer like this, and so we'll need to rethink our approach.)

Update

I've started playing around with the data source for MetroCard.file per the suggestion of this recent post https://stackoverflow.com/a/52142178/5989171. If I make the data source the same as the database storing the file metadata, I now get the error mentioned in the ref but his solution doesn't seem to be working for me. Specifically, I now get the following:

"message": "Value for field '$[operation]' not found."

Our Solution

For our use case, we've decided to go ahead and use the AWS Amplify Storage module as suggested here: https://twitter.com/presbaw/status/1040800650790002689. Despite that, I'm keeping this question open and unanswered, because I'm just genuinely curious about what I'm not understanding here, and I have a feeling I'm not the only one!

1 Answers1

0
$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file))

You can only use this if your DynamoDB save file field as format: {"s3":{"key":"file.jpg","bucket":"bucket_name/folder","region":"us-east-1"}}

Phan Việt
  • 1,253
  • 11
  • 11