Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL AppSync S3 Upload with Complex Objects #2706

Open
michaelcuneo opened this issue Feb 14, 2019 · 15 comments
Open

GraphQL AppSync S3 Upload with Complex Objects #2706

michaelcuneo opened this issue Feb 14, 2019 · 15 comments
Labels
AppSync Related to AppSync issues feature-request Request a new feature GraphQL Related to GraphQL API issues

Comments

@michaelcuneo
Copy link

** Which Category is your question related to? **

GraphQL AppSync, and S3 Uploads with Complex Objects.

** What AWS Services are you utilizing? **

GraphQL AppSync, and S3 Uploads with Complex Objects.

** Provide additional details e.g. code snippets **

I have set up a simple Blog, but each article will require a single Feature Image, so I set up my schema as follows...

enum Visibility {
  public
  private
}
type Post @model {
  id: ID!
  title: String!
  content: String!
  feature: S3Object
  gallery: S3Object
  comments: [Comment] @connection(name: "PostComments")
  author: [Author] @connection(name: "PostAuthor")
}
type S3Object {
  bucket: String!
  key: String!
  region: String!
  localUri: String
  mimeType: String
}
type Author @model {
  id: ID!
  username: String!
  email: String!
  post: Post @connection(name: "PostAuthor")
}
type Comment @model {
  id: ID!
  content: String
  post: Post @connection(name: "PostComments")
}
input S3ObjectInput {
  id: ID
  name: String!
  description: String
  file: S3ObjectInput
}

I have read the articles about complex objects, but the mutation looks like it needs some extra structure that looks to be missing in the documentation.

      if (this.props.currentMethod === 'add') {
        await API.graphql(graphqlOperation(createPost, { input: this.state }))
          .then(result => {
            console.log(result);
            this.props.onChangeMethod('listing');
          })
          .catch(err => console.log(err));
      }

Thats my existing createPost mutation, without the S3 Upload added... this.state doesn't have the feature object yet... I need to add 'feature' to my state.

If I add feature as

      feature = {
        bucket,
        key,
        region,
        localUri: selectedFile,
        mimeType,
      };

Where do I upload the file? Do I add feature to the mutation state, to send it up to AWS... Do I do the S3 Upload separately with something else, and just store the key and such locally?

@manueliglesias
Copy link
Contributor

Hi @michaelcuneo

I've noticed you are using Amplify's API category (await API.graphql(graphqlOperation(createPost, { input: this.state }))).

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.

If you don't want to use the apollo based client (aws-appsync), you need to upload the file yourself using the Storagecategory.

@michaelcuneo
Copy link
Author

So I either have to wrap my app in Apollo and use the old way of auth, api setup, etc... or use a really really old, way of doing the upload myself and ignoring the fact that amplify-is is now an Apollo wrapper when using API GraphQL? This means it’s impossible.

Why would amplify-js wrap Apollo automatically, but disable most of its useful function like, ComplexObjects?

@michaelcuneo
Copy link
Author

What I mean is... amplify-js does away with the complex writing your own queries and mutations and provides them in an easy automated setup, and wraps it all in Apollo, (If I remember correctly)... so to use Complex Objects I have to completely do away with amplify-js... how can I wrap apollo twice? How can I have a bunch of predesigned mutations and queries, and just start writing my own? It's as if this is half implemented? I'm entirely baffled...

@haverchuck haverchuck added AppSync Related to AppSync issues question General question labels Feb 18, 2019
@youneshenniwrites
Copy link

Facing a similar issue. Is there a detailed example on how to achieve this in React Native using AppSync (i.e. uploading files to S3 with Amplify GraphQL Client and storing pointers to those files in DynamoDB).

@michaelcuneo
Copy link
Author

I was made aware ages ago that amplify-cli was no longer being developed, and it was becoming amplify-js... but recently I've been looking at the git-hub for aws-amplify, and there's two now being developed side by side? What's the deal with that? Why would anyone still use amplify-cli if it's now called amplify-js, and what's the difference between the two. I remember Amplify-cli made a folder in my project that was very similar to amplify-js but called aws-amplify or something like that. For a while there though. there was a big warning on the old github that it wasn't being developed anymore and people should refer to amplify-js. I'm so damn confused.

@youneshenniwrites
Copy link

youneshenniwrites commented Mar 8, 2019

I struggled a lot in implementing file upload using Amplify, AppSync, and S3 for my React native application.

As far as I understand, Amplify AppSync GraphQL Client does not support complex objects. You must use the AppSync SDK (with the Apollo Client) to achieve this.

I wanted to share with you my methodology on how I made it work. Probably this will give you some insights.

I am using Expo to access the library of the device, RNS3 to put the image in S3, and the AppSync SDK to execute the mutation that store the reference in DynamoDB.

Full repo in my Github.

Here are screenshots of my entire code. Keys are from your IAM user.

schema.graphql
schema

MutationCreatePicture.js
Mutation

App.js
App

First half of the AddPhoto.js
AddPhoto-1

Second half of the AddPhoto.js
AddPhoto-2

@bishonbopanna
Copy link

@dabit3 can you please comment.

Starting from this tutorial - https://github.com/aws-samples/aws-amplify-graphql

I have run into a similar requirement where I have to upload an image and do it while creating/updating an object (appsync + dynamo) as an atomic operation but the below just puts an entry into dynamo and not upload to S3. Does amplify-js does not have this capability to deal with complex objects ?

await API.graphql(graphqlOperation(createPicture,
            {
                input: {
                            name,
                            owner,
                            visibility,
                            id: uuid(),
                            createdAt: new Date().toISOString(),
                            file: { bucket,
                                    region,
                                    key,
                                    localUri, 
                                    mimeType
                                }
                        }
            }))
            .then(res => {
                console.log("Done");
            });

Schema -

type Picture @model @auth(rules: [{allow: owner}]) {
  id: ID!
  name: String
  owner: String
  visibility: Visibility
  file: S3ObjectInput
  createdAt: String
}

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

enum Visibility {
  public
  private
}


@mfreeman451
Copy link

Am also having the same issue.. I should have probably choosen the AWS AppSync SDK to begin with, will have to go back and change things around...

@ayappaP
Copy link

ayappaP commented Jun 3, 2019

Same issue here as well. You cannot upload images to S3 using aws amplify graphql client.
Must use the Appsync SDK which has a huge size just like every other AWS package, bloating your app. The documentation should make it clear that you cannot use complexobjects at the beginning so that people don't have to rewrite a significant portion of the app just because they used the graphql client.
👎

@jordanranz
Copy link
Contributor

jordanranz commented Jul 2, 2019

As mentioned above, the AppSync SDK supports complex objects. Here is the documentation.

@michaelcuneo, amplify-js and amplify-cli are both actively developed projects that help achieve different goals. Not sure where you read that amplify-cli was not being actively developed.

I will mark this as a feature request as this thread seems to be a request to add complex object support for the Amplify GraphQL client.

@jordanranz jordanranz added the feature-request Request a new feature label Jul 2, 2019
@sammartinez sammartinez removed the question General question label Jan 3, 2020
@mtltechtemp
Copy link

@jordanranz I made it working by defining the following types:

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

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

And then:

type User
  @model
  @auth(
    rules: [
      {
        allow: owner
        ownerField: "id"
        queries: [get]
        mutations: [create, update]
      }
    ]
  ) {
  id: ID!
  alias: String!
  email: AWSEmail!
  fullName: String!
  phone: AWSPhone!
  society: String!
  avatar: S3Object
  logo: S3Object
  language: String
}

In front:

const { name, type: mimeType } = selectedFile;
const [, , , extension] = /([^.]+)(\.(\w+))?$/.exec(name);
const bucket = awsconfig.aws_user_files_s3_bucket;
const region = awsconfig.aws_user_files_s3_bucket_region;
const visibility = "public";
const { identityId } = await(this as any).$Amplify.Auth.currentCredentials();
const key = `${visibility}/${identityId}/${uuidv4()}${
  extension && "."
}${extension}`;
const file = {
  bucket,
  key,
  region,
  mimeType,
  localUri: selectedFile,
};

const userDoc = await(this as any).$Amplify.API.graphql(
  (this as any).$Amplify.graphqlOperation(updateUser, {
    input: {
      id: this.stateUser.id,
      logo: file,
    },
  })
) as GraphQLResult<UpdateUserMutation>;

console.log("userDoc", userDoc);

I just had to define S3ObjectInputand S3Object otherwise it doesn't work.

I think we can close this issue if the documentation is updated.

@seanim0920
Copy link

I tried doing the above but it didn't seem to work for me, no new images appear in my bucket. Did anyone else have luck with @mtltechtemp 's approach?

@flyandi
Copy link

flyandi commented Oct 13, 2020

I gave up and just upload straight using Storage.put and on top I get the progress update event:

export const upload = (file, onProgress) => {
    const { type: mimeType } = file;
    const target = uuid();
    const key = `images/${target}`

    return Storage.put(key, file, {
        progressCallback: onProgress,
        contentType: mimeType,
    }).then(() => Promise.resolve({key, target}))
}

after that I just store the actual reference in dynamo.

@mcanvar
Copy link

mcanvar commented Feb 26, 2021

I've tried hard to get this work then I gave up too.

@cfbo
Copy link

cfbo commented Sep 15, 2021

Does anyone know if there is a way to monitor progress (similar to what is shown in the screenshot below) when using the graphql S3Object / complex objects type ?
I have a react client and I am using datastore.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
AppSync Related to AppSync issues feature-request Request a new feature GraphQL Related to GraphQL API issues
Projects
None yet
Development

No branches or pull requests