I try to upload a file with GraphQL. While upload, I get following error message:
Variable "$file" got invalid value { resolve: [function], reject:
[function], promise: {}, file: { filename:
"insung-yoon-TPvE8qPfMr0-unsplash.jpg", mimetype: "image/jpeg",
encoding: "7bit", createReadStream: [function createReadStream] } };
Upload value invalid.
The only solution I found, was to disable the upload at apollo-graphql and add graphql-upload
new ApolloServer({ schema, context, uploads: false })
app.use(graphqlUploadExpress({ maxFileSize: 10000, maxFiles: 10 }))
I already had added this setting, but the issue is still there.
My mutation looks like:
#Mutation(() => ReturnType)
uploadFileAndData(
#Ctx() ctx: Context,
#Arg('data', { description: 'File Upload' }) data: MyInputType,
): Promise<ReturnType> {
return functionWithMagic({ ctx, data })
}
and my InputType like:
import { FileUpload, GraphQLUpload } from 'graphql-upload'
...
#InputType({ description: 'Upload data input' })
export class MyInputType {
#Field(() => GraphQLUpload)
#Joiful.any()
file: FileUpload
}
After a lot of searching, I finally found my issue. We are using a mono repo, and had installed two different version of file-upload in two packages. When I changed the version, at both packages on the same version and the error is gone.
To add, if you're using Altair graphql client, this error can originate from the client itself.
try:
1- close all tabs and start altair again
2- redo step one, then close the query tab and rewrite the query.
Related
Problem
I am trying to inject / replace environment variables with #rollup/plugin-replace. Unfortunately, I get this error:
TypeError: can't convert undefined to object
Code
// rollup.config.js
import replace from "#rollup/plugin-replace";
import { config } from "dotenv";
config();
export default {
// ...
plugins: [
replace({
values: { YOUTUBE_API: JSON.stringify(process.env.YOUTUBE_API) },
preventAssignment: true,
}),
// ...
}
And I call it like this:
onMount(() => {
(async function getPopular() {
videos = await axios.get("https://www.googleapis.com/youtube/v3/videos", {
part: "id, snippet, suggestions",
chart: "mostPopular",
key: YOUTUBE_API,
});
})();
});
What I tried
I logged out the variable and so can confirm that it exists. Also, if I remove the stringify function, I get another error:
ReferenceError: blablabblub is not defined
I have done this successfully in other projects. What the heck is wrong here?
So after some digging around with the same issue, I found it was related to object assignment. For example:
export default {
// ...
plugins: [
replace({
values: {
env: {
API_URL: process.env.API_URL,
API_VERSION: process.env.API_VERSION,
}
},
preventAssignment: true,
}),
// ...
}
// in some JS or Svelte file
const config = {
host: env.API_URL,
version: env.API_VERSION
}
// The above will result in a reference error of 'env' not being defined.
// in the same JS or Svelte file..
const envVars = env;
const config = {
host: envVars.API_URL,
version: envVars.API_VERSION
}
// this works just fine!
I haven't had anymore time to investigate, but my gut feeling is that rollup won't replace variable names when they are nested inside an object assignment. It might be nice for an optional flag to allow this, but it might also get very messy hence why they didn't do it.
I hope this helps if it's still an issue for you.
In NextJS,
how can I use redirect to turn URL like /page?foo=bar into /page/bar ?
I read https://nextjs.org/docs/api-reference/next.config.js/redirects but couldn't find a solution.
What I have today is:
{
source: '/page',
has: [
{
type: 'query',
key: 'foo'
}
],
destination: '/page/:foo',
permanent: true
}
but that make /page?foo=bar into /page/bar?foo=bar.
How can I drop the query ?
Edit:
So I realized that this doesn't event work at all with Netlify.
I tried to follow https://docs.netlify.com/routing/redirects/ but I have the same problem with the query parameters staying.
You can use middleware.
Just parse the query parameter yourself and add redirection.
Store a file _middleware.ts below the pages directory:
export async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
if (// Your-thing )
return NextResponse.redirect(//Your-url);
return NextResponse.next();
}
Maybe there is a different way, I don't know, but it doesn't matter.
At least when using Vercel, you can archive this by repeating the parameters from the has in the destination but leaving the value empty.
E.g.:
{
source: '/page',
has: [
{
type: 'query',
key: 'foo'
}
],
destination: '/page/:foo?foo=',
permanent: true
}
Parameters that already exit in the destination won't be copied over and parameters with an empty value in the destination will be removed completely.
I can't seem to figure out what the problem is. I'm trying to use EvaporateJS to upload files to S3, I'm also using React. Here is what my code looks like:
Blockquote
useEffect(() => {
Evaporate.create({
aws_key: AWS_ACCESS_KEY,
bucket: S3_BUCKET,
awsRegion: 'us-west-1', // s3 region
signerUrl: '/api/videos/signv4_auth',
awsSignatureVersion: '4',
computeContentMd5: true,
cloudfront: true,
cryptoMd5Method: (data) => {
return AWS.util.crypto.md5(data, 'base64');
},
cryptoHexEncodedHash256: (data) => {
return AWS.util.crypto.sha256(data, 'hex');
}
}).then(evaporate => {
console.log(evaporate);
// evaporate.add(); // showing as not a function
});
}, []);
But I get an error message: evaporate.add is not a function. When I inspect the evaporate variable that's being passed with then, it doesn't contain the add function, nor some of the other functions mentioned in documentation. Not sure why it's not working, any help would be highly appreciated.
Console output of evaporate
Error Message
Consider the following code within gatsby-config.js:
module.exports = {
plugins: [
{
resolve: `gatsby-source-fetch`,
options: {
name: `brands`,
type: `brands`,
url: `${dynamicURL}`, // This is the part I need to be dynamic at run/build time.
method: `get`,
axiosConfig: {
headers: { Accept: "text/csv" },
},
saveTo: `${__dirname}/src/data/brands-summary.csv`,
createNodes: false,
},
},
],
}
As you can see above, the URL for the source plugin is something that I need to be dynamic. The reason for this is that the file URL will change every time it's updated in the CMS. I need to query the CMS for that field and get its CDN URL before passing to the plugin.
I tried adding the following to the top of gatsby-config.js but I'm getting errors.
const axios = require("axios")
let dynamicURL = ""
const getBrands = async () => {
return await axios({
method: "get",
url: "https://some-proxy-url-that-returns-json-with-the-csv-file-url",
})
}
;(async () => {
const brands = await getBrands()
dynamicURL = brands.data.summary.url
})()
I'm assuming this doesn't work because the config is not waiting for the request above to resolve and therefore, all we get is a blank URL.
Is there any better way to do this? I can't simply supply the source plugin with a fixed/known URL ahead of time.
Any help greatly appreciated. I'm normally a Vue.js guy but having to work with React/Gatsby and so I'm not entirely familiar with it.
I had similar requirement where I need to set siteId of gatsby-plugin-matomo dynamically by fetching data from async api. After searching a lot of documentation of gatsby build lifecycle, I found a solution.
Here is my approach -
gatsby-config.js
module.exports = {
siteMetadata: {
...
},
plugins: {
{
resolve: 'gatsby-plugin-matomo',
options: {
siteId: '',
matomoUrl: 'MATOMO_URL',
siteUrl: 'GATSBY_SITE_URL',
dev: true
}
}
}
};
Here siteId is blank because I need to put it dynamically.
gatsby-node.js
exports.onPreInit = async ({ actions, store }) => {
const { setPluginStatus } = actions;
const state = store.getState();
const plugin = state.flattenedPlugins.find(plugin => plugin.name === "gatsby-plugin-matomo");
if (plugin) {
const matomo_site_id = await fetchMatomoSiteId('API_ENDPOINT_URL');
plugin.pluginOptions = {...plugin.pluginOptions, ...{ siteId: matomo_site_id }};
setPluginStatus({ pluginOptions: plugin.pluginOptions }, plugin);
}
};
exports.createPages = async function createPages({ actions, graphql }) {
/* Create page code */
};
onPreInit is a gatsby lifecycle method which is executing just after plugin loaded from config. onPreInit lifecycle hook has some built in methods.
store is the redux store where gatsby is storing all required information for build process.
setPluginStatus is a redux action by which plugin data can be modified in redux store of gatsby.
Here the important thing is onPreInit lifecycle hook has to be called in async way.
Hope this helps someone in future.
Another approach that may work for you is using environment variables as you said, the URL is known so, you can add them in a .env file rather than a CSV.
By default, Gatsby uses .env.development for gatsby develop and a .env.production for gatsby build command. So you will need to create two files in the root of your project.
In your .env (both and .env.development and .env.production) just add:
DYNAMIC_URL: https://yourUrl.com
Since your gatsby-config.js is rendered in your Node server, you don't need to prefix them by GATSBY_ as the ones rendered in the client-side needs. So, in your gatsby-config.js:
module.exports = {
plugins: [
{
resolve: `gatsby-source-fetch`,
options: {
name: `brands`,
type: `brands`,
url: process.env.DYNAMIC_URL, // This is the part I need to be dynamic at run/build time.
method: `get`,
axiosConfig: {
headers: { Accept: "text/csv" },
},
saveTo: `${__dirname}/src/data/brands-summary.csv`,
createNodes: false,
},
},
],
It's important to avoid tracking those files in your Git repository since you don't want to expose this type of data.
I am building an electron app which handles file uploads, I am using dialog to get the files from user, I need to send the files to server but I am getting the files path but I get errors when sending them . I am using Vue resource for requests. Below is my code:
<template>
<div>
<button #click="uploadAct()" class="primary">New Upload </button>
</div>
</template>
<script>
const {dialog} = require('electron').remote
const fs = require('fs')
import reqApi from '../../api/something'
export default {
methods: {
uploadAct () {
dialog.showOpenDialog({
title: 'Upload Attachments',
buttonLabel: 'Upload',
filters: [
{name: 'Images', extensions: ['jpg', 'png', 'gif']},
{name: 'All Files', extensions: ['*']}
],
properties: ['openFile', 'multiSelections']
}, function (filenames) {
if (filenames) {
let d = ''
filenames.forEach(function (element) {
d = element
})
// here i get a path of file correctly something like /path/to/file.jpg
reqApi.uploadattachmnets({photo: fs.createReadStream(d)}).then(
(response) => {
console.log(response)
},
(error) => {
console.log(error)
})
// })
}
})
}
}
}
</script>
I however end up with error on the request , any help will be appreciated .
Probably a typo but you have a call to an API:
carApi.uploadattachmnets({photo: fs.createReadStream(d)})
which is different to the one you are importing:
import reqApi from '../../api/something'
If not the above I'd assume this is going to be a CORS issue if Postman is already able to send files and receive the correct response from the endpoint. Without more info I'd recommend looking at: https://www.html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request
For a more specific response you'd need to post the API code so we can review how you are sending the file.