I am trying to upload file with apollo-upload-client from Next.js app to Node.js back end using graphql.
My Apollo client config
const createClient = (ctx: NextPageContext) =>
new ApolloClient({
credentials: "include",
headers: {
cookie:
(typeof window === "undefined"
? ctx?.req?.headers.cookie
: undefined) || "",
},
cache: new InMemoryCache(),
link: createUploadLink({uri:'http://localhost:4000/graphql', credentials:"include"})
});
My express resolver with TypeORM
@Resolver()
export class ProfilePictureResolver {
@Mutation(() => Boolean)
async addProfilePicture(@Arg("picture", () => GraphQLUpload)
{
createReadStream,
filename
}: Upload): Promise<boolean> {
return new Promise(async (resolve, reject) =>
createReadStream()
.pipe(createWriteStream(__dirname + `/../../../images/${filename}`))
.on("finish", () => resolve(true))
.on("error", () => reject(false))
);
}
}
Page
const Profile:React.FC<IProps> = () => {
const user = useSelector(state => state.user);
const [file, setFileToUpload] = useState(null);
const [mutate, {loading}] = useMutation(UPLOAD_IMAGE_MUTATION);
function onChange({
target: {
validity,
files: [file],
},
}) {
if (validity.valid) mutate({ variables: { picture: file } });
}
const onSubmit = async (e) =>{
e.preventDefault();
console.log(file)
const response = await mutate({
variables: {picture: file}
});
}
return (
<Box mt={20} pl={30} pr={30}>
<Header>
Edit Profile
</Header>
<input onChange={onChange} type="file" placeholder="photo" />
<button onClick={(e)=>onSubmit(e)}>Submit</button>
</Box>
)
};
FormData
------WebKitFormBoundary3bcoEmOQWM0eUhCG Content-Disposition: form-data; name="operations"
{"operationName":"addProfilePicture","variables":{"picture":null},"query":"mutation addProfilePicture($picture: Upload!) {\n addProfilePicture(picture: $picture)\n}\n"} ------WebKitFormBoundary3bcoEmOQWM0eUhCG Content-Disposition: form-data; name="map"
{"1":["variables.picture"]} ------WebKitFormBoundary3bcoEmOQWM0eUhCG Content-Disposition: form-data; name="1"; filename="73387406_149357266327075_4919835380817576193_n.jpg" Content-Type: image/jpeg
------WebKitFormBoundary3bcoEmOQWM0eUhCG--
Before call mutation in console I see that file is present. What am I doing wrong?
Update: I fixed mutation on the client side, changed file on picture:file. Now I have another error:
Variable "$picture" got invalid value {}; Upload value invalid.
Update 2:
Here is what my query looks like, might be of help
js export const UPLOAD_IMAGE_MUTATION = gql` mutation addProfilePicture($picture: Upload!) { addProfilePicture(picture: $picture) } `;
SOLUTION
I figured that indeed there was something wrong with my apollo server resolver
I've change the old resolver (see above) on this one:
uploadFile: async (_, { file }) => {
const { createReadStream, filename } = await file;
await new Promise(res =>
createReadStream()
.pipe(createWriteStream(path.join(__dirname, "../images", filename)))
.on("close", res)
);
files.push(filename);
return true;
},
So as you see I dont use graphql upload anymore, and also I've changed my client mutation back to
mutate({ variables: { file } }
And new mutation query:
mutation UploadFile($file: Upload!) {
uploadFile(file: $file)
}
Now it works like a charm. Case closed.
mutate
called twice? should besetFileToUpload(file)
inonChange
? update FormData – xadmvariables.file
in FormData - show backend specs for this mutation, what is the required mutation arg name -file
orpicture
? – xadmpicture
– Good Guy