Amplify-js: S3Object doesn't upload file from client

Created on 2 Aug 2019  路  14Comments  路  Source: aws-amplify/amplify-js

First, when the docs say:

input CreateTodoInput {
  id: ID
  name: String!
  description: String
  file: S3ObjectInput # This input type will be generated for you
}

I get an error Type "S3ObjectInput" not found in document. and I have to add S3ObjectInput manually.

This is my schema (the docs are not very clear on it so I put it together from similar questions)

type Picture @model {
  id: ID!
  file: S3Object!
  url: String!
  rating: Int
  appearedForRanking: Int
}

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

input CreatePictureInput {
  id: ID
  file: S3ObjectInput!
  url: String!
  rating: Int
  appearedForRanking: Int
}

input S3ObjectInput {
  bucket: String!
  region: String!
  localUri: String
  visibility: Visibility
  key: String
  mimeType: String
}

enum Visibility {
  public
  protected
  private
}

And this is the client code (with React)

class PictureUpload extends Component {

  state = { fileUrl: '', file: '', filename: '' }

  handleChange = e => {
    let file = e.target.files[0]
    let filext = file.name.split('.').pop()
    let filename = uuid() + '.' + filext

    this.setState({
      fileUrl: URL.createObjectURL(file),
      filename: filename
    })
  }

  saveFile = async () => {
    let visibility = 'public'

    let fileObj = {
      bucket: awsConfig.aws_user_files_s3_bucket,
      region: awsConfig.aws_user_files_s3_bucket_region,
      key: visibility + '/' + this.state.filename,
      mimeType:'image/jpeg',
      localUri: this.state.fileUrl,
      visibility: visibility
    }

    try {
      const picture = await API.graphql(
        graphqlOperation(mutations.createPicture, {
          input: {
            url: this.state.filename,
            file: fileObj
          }
        })
      )

The problem is that the mutation runs without errors, setting the DB records, but the file does not appear in S3. The docs say the SDK uploads the file to Amazon S3 for you. so I don't think I forgot to add something.

Any idea why the upload doesn't happen?

API GraphQL question

Most helpful comment

@ilyador

I see how this can be confusing, I'll be sharing this feedback with the team to improve the docs.

To try to clarify a little bit further:

  • AWS Amplify JS

    • This library

  • AWS AppSync

    • The AWS managed GraphQL service with real-time data

  • API category

    • One of the AWS Amplify JS categories. It provides support for REST and GraphQL apis.

  • AWS AppSync SDK

This means that you have two ways to interact with AWS AppSync GraphQL Apis:

  • Using Amplify JS API category
  • Using the aws-appsync sdk

On the TOC we have this structure that kind of mimics what I wrote, but yeah, it can of course be improved

image

All 14 comments

@ilyador thanks for your question, what you mention is correct the sdk uploads the file, but that is on AWS AppSync SDK section, on Amplify client you have to used Storage category to upload the file before sending the mutation.

@elorzafe quick follow up question on this. So you would send the file using Storage, wait for the response, and then if it was successfully uploaded you would then send the mutation to create the item with GraphQL?

@elorzafe I don't understand the difference. What do you mean by AWS AppSync SDK section? This is part of the Amplify documentation. Is it possible or not to upload a file this way?

  • Do you mean I need to use AWSAppSyncClient instead of API.graphql?
  • Is there an advantage uploading the file this way and not with Storage?

    - Can I use Amplify and the AWSAppSyncClient together in the same client?

@packerfan626 I thought I don't need to use storage together with S3Object and actually do only 1 mutation that will also upload the file, but you can see @elorzafe 's answer. I'm still not clear on that...

@ilyador that is what I had originally thought too, but it seems like you need to actually upload the file to S3 first and then use the S3 object to reference it. But I could be missing the point here, I've been struggling with this concept a bit.

Hi @packerfan626

So you would send the file using Storage, wait for the response, and then if it was successfully uploaded you would then send the mutation to create the item with GraphQL?

This is correct when using Amplify (without aws-appsync).

Hi @ilyador

I don't understand the difference. What do you mean by AWS AppSync SDK section?

The Amplify team provides support to interact with GraphQL apis in two packages, the Amplify API category, and the the aws-appsync package.

aws-appsync is a full fledged graphql client for more complex use cases, it is based on the Apollo Client and gives you support for offline-first apps, optimistic UIs, automatic uploading of complex objects, a cache and lots of more features that might be overkill for some scenarios.

For simpler use cases, Amplify is the recommended way to go.

Do you mean I need to use AWSAppSyncClient instead of API.graphql?

The automatic upload of complex objects is a feature of the aws-appsync package. The docs you looked at were probably for this package. You can look at this sample app for usage.

The way that aws-appsync determines if it needs to upload a file, is that it looks for a field with this shape in the mutation:

input S3ObjectInput {
  bucket: String!
  region: String!
  localUri: String
  visibility: Visibility
  key: String
  mimeType: String
}

If you don't want to use the apollo based client (aws-appsync), you need to upload the file yourself using the Storagecategory. Please note that we have a feature request that we are tracking to add automatic upload in Amplify too #2706

Is there an advantage uploading the file this way and not with Storage?

The advantage is that the sdk uploads the file for you automatically, but you might not want/need all the extra features (and potential increase in bundle size) in your scenario.

Can I use Amplify and the AWSAppSyncClient together in the same client?

Yes! You can take a look at the sample app I mentioned earlier, it uses both. Take a look specifically at https://github.com/aws-samples/aws-amplify-graphql/blob/7b3da9f221c02b1363f8d784863f30bd696ea8e0/src/Components/AddPhoto.js#L36-L67

Hey @manueliglesias ,

Thank you for your reply! I understand the apollo based client might have several extra features we might not need. But would it be better practice to use it over the Amplify method of storing to S3 first and if successfully uploaded then storing the key into a database for reference?

Hey @manueliglesias ,

Thank you for your reply! I understand the apollo based client might have several extra features we might not need. But would it be better practice to use it over the Amplify method of storing to S3 first and if successfully uploaded then storing the key into a database for reference?

I'd say stay with Amplify, we have some info around your question in the Using AWS Appsync section in the docs

The Amplify GraphQL client is a lighter weight option if you鈥檙e looking for a simple way to leverage GraphQL features and do not need the offline capabilities or caching of the Apollo client. If you need those features, choose the AWS AppSync SDK.

Regarding the upload use case with Amplify, take a look at this great post explaining how to do uploads and mutations with Amplify

The relevant piece is this: (notice how the upload happens first, then the mutation)

  // upload the image to S3 and then save it in the GraphQL API
  async function createProduct() {
    if (file) {
      const extension = file.name.split(".")[1]
      const { type: mimeType } = file
      const key = `images/${uuid()}${productName}.${extension}`      
      const url = `https://${bucket}.s3.${region}.amazonaws.com/public/${key}`
      const inputData = { name: productName , image: url }

      try {
        await Storage.put(key, file, {
          contentType: mimeType
        })
        await API.graphql(graphqlOperation(CreateProduct, { input: inputData }))
      } catch (err) {
        console.log('error: ', err)
      }
    }
  }

@ilyador

To amend my response from earlier today, what could have we done better in the docs to avoid potential confusions?

@manueliglesias I greatly appreciate your answer, this definitely clears up a lot of questions I had and I hope it was helpful to @ilyador too.

@manueliglesias Thank you, this definitely answers my question.

This is the problem as I see it: you do bait-and-switch in the docs 馃槅In the API section I expect to understand how to use the API, but then you talk about the SDK client which is a different thing. They may have similar functionalities, but they are two different packages.

Screen Shot 2019-08-08 at 12 13 57

Basically, after this seaction I am really confused what are we talking about. Since API is one of the 2 ways to use GraphQL I think you should rethink the structure of the docs there. Maybe even give the SDK its own docs or section.

A few other questions @manueliglesias

In the aws-amplify-graphql sample app the issues are disabled for some reason, so I'll ask here:

Why are those mutations created inside the app and not part of the auto-generated ones that Amplify CLI builds?
https://github.com/aws-samples/aws-amplify-graphql/blob/master/src/Components/AddPhoto.js#L86


When building the app with Amplify CLI, I get the config file generated, yet in the SDK docs it is shown how to build the config manually. Do I need a different config?


I get an error Type "S3ObjectInput" not found in document. and I have to add S3ObjectInput manually.

Is that a bug?

@ilyador

I see how this can be confusing, I'll be sharing this feedback with the team to improve the docs.

To try to clarify a little bit further:

  • AWS Amplify JS

    • This library

  • AWS AppSync

    • The AWS managed GraphQL service with real-time data

  • API category

    • One of the AWS Amplify JS categories. It provides support for REST and GraphQL apis.

  • AWS AppSync SDK

This means that you have two ways to interact with AWS AppSync GraphQL Apis:

  • Using Amplify JS API category
  • Using the aws-appsync sdk

On the TOC we have this structure that kind of mimics what I wrote, but yeah, it can of course be improved

image

Why are those mutations created inside the app and not part of the auto-generated ones that Amplify CLI builds?

I think that that sample app predates the AWS Amplify CLI and all its codegen features

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rygo6 picture rygo6  路  3Comments

shinnapatthesix picture shinnapatthesix  路  3Comments

callmekatootie picture callmekatootie  路  3Comments

romainquellec picture romainquellec  路  3Comments

lucasmike picture lucasmike  路  3Comments