How to add AWS S3 to Nuxt.js - amazon-web-services

So i'm trying to use S3 to upload some profiles images and then be able to call them by a URL, and for this i wanted to create a Nuxt plugin to add all the related functions to it
but for now i'm trying to keep it simple
plugins/s3.js
import S3 from 'aws-sdk/clients/s3'
export default defineNuxtPlugin(nuxtApp => {
return {
provide: { S3 },
};
});
by doing this i'm getting issues with vite that are saying that global is not define, so i found that i could add some vite config inside my nuxt.config.ts and i did it
nuxt.config.ts
vite: {
define: {
'global': {}
}
},
but by doing that i'm having some conflict at /node_modules/nuxt/dist/pages/runtime/router.mjs:65:4
so there is a solution by adding "window" but if i added i'm getting another error on production saying " TypeError: t is not a constructor" and that's all :)
what would be the best approach to fix this issue? i just want to be able to upload files and call them

Related

TypeError: undefined is not an object (evaluating '_storage.default.put') while using AWS Amplify Storage in Expo react native

I'm looking to add the ability to upload images and videos to S3 in my Expo react native project. I'm using ImagePicker to allow the user to pick images and videos...that works fine. I was having issues with video uploaded using signed URLs - the type was left empty so playback wasn't possible - for some reason, it didn't seem to matter for images. So I followed this tutorial for AWS Amplify since it seemed to provide a more robust way of pushing content to S3 buckets.
I configured Amplify according to the documentation - it resulted in an S3 bucket. I then setup auth as described in the documentation and ran
amplify add storage
To add storage to my project - I included the bucket that amplify setup for me. I then added the following code to my ImagePicker function:
const imageName = result.assets[0].uri.replace(/^.*[\\\/]/, '');
const fileType = mime.lookup(result.assets[0].uri);
const access = { level: "public", contentType: {fileType} };
const imageData = await fetch(result.assets[0].uri)
const blobData = await imageData.blob()
console.log("Image name and filetype " + imageName + " and " + fileType);
try {
await Storage.put(imageName, blobData, access)
} catch (err) {
console.log('error: ', err)
}
Which results in:
Image name and filetype E5E29BF9-6CD2-4D87-8B63-7FA9B0BE4A80.mov and video/quicktime
error: [TypeError: undefined is not an object (evaluating '_storage.default.put')]
I'm not sure how to proceed in troubleshooting this and why I'm getting that error. Any help would be appreciated
The problem ended up being that I was importing Storage from the wrong library:
import Storage from '#aws-amplify/storage'
import Amplify from "#aws-amplify/core";
Needed to be:
import { Amplify, Storage } from 'aws-amplify';
I found the former in a tutorial so it led me down the wrong path.

Sharing API gateway endpoint URL across different stacks in CDK

I have following AWS CDK backed solution:
Static S3 based webpage which communicates with
API Gateway which then sends data to
AWS lambda.
The problem is that S3 page needs to be aware of API gateway endpoint URL.
Obviously this is not achievable within the same CDK stack. So I have defined two stacks:
Backend (API gateway + lambda)
Frontend (S3 based static webpage)
They are linked as dependant in CDK code:
const app = new cdk.App();
const backStack = new BackendStack(app, 'Stack-back', {...});
new FrontendStack(app, 'Stack-front', {...}).addDependency(backStack, "API URL from backend is needed");
I try to share URL as follows.
Code from backend stack definition:
const api = new apiGW.RestApi(this, 'MyAPI', {
restApiName: 'My API',
description: 'This service provides interface towards web app',
defaultCorsPreflightOptions: {
allowOrigins: apiGW.Cors.ALL_ORIGINS,
}
});
api.root.addMethod("POST", lambdaIntegration);
new CfnOutput(this, 'ApiUrlRef', {
value: api.url,
description: 'API Gateway URL',
exportName: 'ApiUrl',
});
Code from frontend stack definition:
const apiUrl = Fn.importValue('ApiUrl');
Unfortunately, instead of URL I get token (${Token[TOKEN.256]}). At the same time, I see URL is resolved in CDK generated files:
./cdk.out/Stack-back.template.json:
"ApiUrlRef": {
"Description": "API Gateway URL",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "MyAPI7DAA778AA"
},
".execute-api.us-west-1.",
{
"Ref": "AWS::URLSuffix"
},
"/",
{
"Ref": "MyAPIDeploymentStageprodA7777A7A"
},
"/"
]
]
},
"Export": {
"Name": "ApiUrl"
}
}
},
What I'm doing wrong?
UPD:
After advice of fedonev to pass data as props, situation did not changed much. Now url looks like that:
"https://${Token[TOKEN.225]}.execute-api.us-west-1.${Token[AWS.URLSuffix.3]}/${Token[TOKEN.244]}/"
I think important part I missed (which was also pointed by
Milan Gatyas) is how I create HTML with URL of gateway.
In my frontend-stack.ts, I use template file. After template is filled, I store it in S3:
const filledTemplatePath: string = path.join(processedWebFileDir,'index.html');
const webTemplate: string = fs.readFileSync(filledTemplatePath, 'utf8')
const Handlebars = require("handlebars")
let template = Handlebars.compile(webTemplate)
const adjustedHtml: string = template({ apiGwEndpoint: apiUrl.toString() })
fs.writeFileSync(filledTemplatePath, adjustedHtml)
// bucket
const bucket: S3.Bucket = new S3.Bucket(this, "WebsiteBucket",
{
bucketName: 'frontend',
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
publicReadAccess: true,
})
new S3Deploy.BucketDeployment(this, 'DeployWebsite', {
sources: [S3Deploy.Source.asset(processedWebFileDir)],
destinationBucket: bucket,
});
(I'm new to TS and web, please don't judge much :) )
Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?
Will be grateful for a link or explanation so that I could understand the process better, there are so much new information to me that some parts are still quite foggy.
As #fedonev mentioned, the tokens are just placeholder values in the TypeScript application. CDK app replaces tokens with intrinsic functions when the CloudFormation template is produced.
However, your use case is different. You try to know the information inside the CDK app which is available only at synthesis time, and you can't use the intrinsic function to resolve the URL while being in CDK app to write to file.
If possible you can utilize the custom domain for the API Gateway. Then you can work with beforehand known custom domain in your static file and assign the custom domain to the API Gateway in your CDK App.
[Edit: rewrote the answer to reflect updates to the OP]
Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?
Yes. The API URL will resolve only at deploy-time. You are trying to consume it at synth-time when you write to the template file. At synth-time, CDK represents not-yet-available values as Tokens like ${Token[TOKEN.256]}, the CDK's clever way of handling such deferred values.
What I'm doing wrong?
You need to defer the consumption of API URL until its value is resolved (= until the API is deployed). In most cases, passing constructs as props between stacks is the right approach. But not in your case: you want to inject the URL into the template file. As usual with AWS, you have many options:
Split the stacks into separate apps, deployed separately. Deploy BackendStack. Hardcode the url into FrontendStack. Quick and dirty.
Instead of S3, use Amplify front-end hosting, which can expose the URL to your template as an environment variable. Beginner friendly, has CDK support.
Add a CustomResource construct, which would be backed by a Lambda that writes the URL to the template file as part of the deploy lifecycle. This solution is elegant but not newbie-friendly.
Use a Pipeline to inject the URL variable as a build step during deploy. Another advanced approach.

AWS Amplify Duplicate Error: Duplicated files or mocks

I set up a new amplify, added auth, and a post confirmation lambda function to move user data into DynamoDB. When I run NPM start, I get this error:
Failed to construct transformer: DuplicateError: Duplicated files or mocks. Please check the console for more info
at setModule (C:\Users\cjfew\Desktop\Fresh\MyDemo\node_modules\jest-haste-map\build\index.js:543:17)
.js:426:22 {
mockPath1: 'amplify#current-cloud-backend\function\FreshAuthPostConfirmation\src\package.json',
mockPath2: 'amplify\backend\function\FreshAuthPostConfirmation\src\package.json'
}
'''
Based on what I have read, #current-cloud-backend gets created by amplify, based on the files in the backend folder. It seems like that package.json is supposed to be there, but I am not sure why it is an error. I saw somewhere that I should just delete the subclass duplicate file, which I assumed to be the one in #current-cloud-backend, but amplify is going to keep producing this error every time I push to it, how do I avoid this from happening at all?
There is a discussion about this error in this Amplify GitHub Issue. The file package.json appears twice to jest-haste-map, and the solution is to explicitly ignore the #current-cloud-backend folder when building and starting your app.
The solution to the problem depends on your version of React Native: here you find an overview of how exlusion of files work for different versions. For example, you can create a metro.config.js file with the following contents to exclude the #current-cloud-backend:
const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = {
resolver: {
blacklistRE: exclusionList([/#current-cloud-backend\/.*/])
}
};
And install metro-config as a dev dependency. If that doesn't work there are some other solutions in the links that you can try out.

How to retrieve the schema.graphql file from #aws-cdk/aws-appsync before deployment

I am using the code-first approach in #aws-cdk/aws-appsync for generating my graphql schema. For Typescript code generation purposes I need a way to retrieve the schema.graphql before deployment (maybe somehow extracting it from the cdk synth command?).
I am not sure if we are facing the same issue. Basically I wanted to access the GQL schema in my client react app which was in a different repository than the cdk app in which the infrastructure is defined.
I ended up using the aws-cli to extract the appsync schema using the following command:
aws appsync get-introspection-schema --api-id [API-ID] --format SDL --no-include-directives outfile=[OUTFILE]
For getting the automatically created schema before deployment, use this script:
readFile('cdk.out/<YOUR-APP-NAME>.template.json', 'utf8', (err, data) => {
if (err) {
throw err;
}
const definition = JSON.parse(data)
.Resources
.<YOUR-SCHEMA-ID> // replace with your schema id
.Properties
.Definition;
writeFile('lib/domain/generated.graphql', definition, (error) => {
if (error) throw error;
});
});

How to get SAM template of lambda function deployed via UI?

I wrote a bash script similar to this one: Download an already uploaded Lambda function
Everything is fine with all lambda functions that have been deployed via SAM template files. However, when I retrieve the deployment package of a lambda function (application) that has been deployed via the web UI of AWS, all I get is the index.js file in the deployment package of that function.
Anyway, it is possible to generate a SAM yaml file that describes the architecture of the given lambda application by selecting it over the Lambda Management Console via Actions > Export Function > Download AWS SAM file. Consequently, there should be a possibility to do this via aws-cli or is that not possible at all?
You can get function configuration with awscli https://docs.aws.amazon.com/cli/latest/reference/lambda/get-function-configuration.html and the response will contain a Code section with a link to the function package https://docs.aws.amazon.com/lambda/latest/dg/API_FunctionCodeLocation.html
Also, you can create a CloudFormation stack from the existing infrastructure with CloudFormer https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-cloudformer.html
Having a CloudFormation template you can get the resource description. https://docs.aws.amazon.com/cli/latest/reference/cloudformation/describe-stack-resources.html with the link to the function source code on S3.
See more on this in https://stackoverflow.com/a/55764927/6628583
This is a clue, not a complete answer, sorry. There should be, but there is not yet, an aws-cli to get this SAM content. The URI in the browser is obtuse with some obscure network calls, e.g.
https://us-west-2.console.aws.amazon.com/p/log/1/lambda/1/OP/&k0=feevc&m0=1&d0=%7B%22s_fid%22:%2233SNIPPEDC66-34SNIPPED7FE5B%22%7D&p0=exportSAM&cb=1595621538378&proxy-rid=7c6a6a31SNIPPEDf1a400b457cc
There seems to be no easy way to construct that URI to GET the exportSAM download.
The browser lambda.js is not much help, e.g.
var A = Object(p.connect)((function(e) {
return {
blueprint: Object(v.b)(e).query.exportBp,
integrationConfigs: h.c.getNodes(e),
downloadMessages: h.c.getDownloadMessages(e),
isOpen: !!Object(v.b)(e).query.exportModal,
exporting: h.c.getExporting(e)
}
}
), (function(e) {
return {
downloadSam: function(t) {
return e((n = t,
{
type: b.f.EXPORT_BLUEPRINT_TO_FLOURISH,
blueprintName: n
}));
var n
},
close: function() {
return e(Object(m.c)({
query: {
exportModal: void 0,
exportBp: void 0
}
}, {
persistQuery: !0
}))
}
}
}
), (function(e, t, n) {
return S(S(S(S({}, n), e), t), {}, {
downloadSam: function() {
return t.downloadSam(e.blueprint)
}
})
}