When trying to upload file, axios throw "Request failed with status code 404" - express

I'm using VueJs3, Multer middleware for file uploading, expressJs. Its pretty new for me, so probably it is just a small mistake and i couldn't find it... After submit in console axios throw this error : "Request failed with status code 404".
And console.log(formData.append("file", this.file)) gives 'undefined'.
Some code from component:
import axios from "axios"
export default {
name: "FileUpload",
data() {
return {
file: "",
message: "",
}
},
methods: {
onSelect() {
const file = this.$refs.file.files[0]
this.file = file
},
async onSubmit() {
const formData = new FormData()
formData.append("file", this.file)
try {
await axios.post("/upload", formData)
this.message = "Upload successfully"
} catch (err) {
console.log(err)
this.message = "Something went wrong :("
}
},
},
}
And back-end:
const express = require("express")
const dotenv = require("dotenv")
const cors = require("cors")
const bodyParser = require("body-parser")
// const db = require("./app/config/db.config")
const multer = require("multer")
dotenv.config()
const app = express()
const port = process.env.PORT || 8080
const upload = multer({
dest: "./uploads",
})
var corsOptions = {
origin: "http://localhost:8081",
}
app.use(cors())
app.use(express.json())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.post("/upload", upload.single("file"), (req, res) => {
res.json({ file: req.file })
})
app.listen(port, () => console.log(`Server started on port ${port}`))
I been trying to look for solutions in some tutorials, but probably mistake is to small for my rookie eyes at the moment...

Not sure which axios version you are using but you could try switching the versions because some versions handle multipart/form-data differently.
from docs
Starting from v0.27.0, Axios supports automatic object serialization to a FormData object if the request Content-Type header is set to multipart/form-data.
I take this to mean that the header needs to be defined explicitly
const formData = new FormData()
formData.append("file", this.file)
await axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
you can also try using the postForm method
docs
await axios.postForm("/upload", {file: this.file})
// or with multipart
await axios.postForm("/upload", {'files[]': [this.file]})
I would also recommend that you look at the network panel to see if that header is included, and try sending the request directly with something like postman or insomnia to determine definitively whether the issue is on the front-end or back-end.

Related

Next.js: React Apollo Client Not sending cookies?

I'm using Apollo Client as a graphql client on my next.js application, Here is the function that creates a client for me:
let client: ApolloClient<any>;
export const __ssrMode__: boolean = typeof window === "undefined";
export const uri: string = "http://localhost:3001/graphql";
const createApolloClient = (): ApolloClient<any> => {
return new ApolloClient({
credentials: "include",
ssrMode: __ssrMode__,
link: createHttpLink({
uri,
credentials: "include",
}),
cache: new InMemoryCache(),
});
};
Surprisingly, when I make a mutation to the graphql server I'm able to set the cookies but, I'm not able to get the cookies from the client. What may be possibily the problem?
I came to the same problem, my solution was to create a client every time a server-side rendering is made, maybe it's not ideal to have a client to execute GraphQL calls in the browser and others in the server but it's what worked best for me. This is the code:
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client';
import { NextPageContext } from 'next';
import { setContext } from '#apollo/client/link/context';
export const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'include',
});
const CreateClient = (ctx: NextPageContext | null) => {
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
cookie:
(typeof window === 'undefined'
? ctx?.req?.headers.cookie || undefined
: undefined) || '',
},
};
});
return new ApolloClient({
credentials: 'include',
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
ssrMode: true,
});
};
export default CreateClient;
So, what I do is pass the context from the getServerSideProps and see if I have some cookies there, if so I just set the cookies, you can also send the authorization token if it's in the cookie. To call it is very simple:
export async function getServerSideProps(context: NextPageContext) {
const client = CreateClient(context);
const { data } = await client.query({
query: SOME_QUERY,
});
return {
props: {
data,
},
};
}
You can also do a HOC as in the Ben Awad tutorial Apollo Client HOC but I think it was too much for what I was trying to do. Hope it helped you or helps someone there :)
Also, I'm using Next 12.1.5 and React 18

Axios + Multer Express req.file undefined

I'm trying to simply upload a single file from the client (react/axios) to the server (multer / express). I've read through every "req.file undefined" and can't seem to see the same issues with my own code.
The other issue is that actually my req on the server sees the file in the "files", but multer doesn't save it and req.file is undefined.
What could be happening here?
For client I've tried both methods of sending the form data, neither work.
const onAnalyze = async () => {
if (selectedFile !== null) {
//we have a file, so that's what we're sending
var formData = new FormData();
formData.append("analyze", selectedFile);
//let res = await api.post('/analyze/upload', formData)
try {
const response = await axios({
method: "post",
url: "http://localhost:5000/analyze/upload",
data: formData,
header: { "Content-Type": "multipart/form-data" }
});
console.log(response)
} catch (error) {
console.log(error)
}
// console.log(res)
// setAnalysis(res.data)
} else if (text.length <= maxLength) {
let res = await api.post('/analyze', { text: text })
setAnalysis(res.data)
}
}
For the server it seems simple.. I just don't know. This file destination exists. req.file is always undefined
import express from 'express';
import { getMedia, createMedia } from '../controllers/media.js';
import { AnalyzeText, AnalyzeFile } from '../controllers/analyze.js'
import multer from 'multer'
const fileStorageEngine = multer.diskStorage({
destination: "uploads",
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
var upload = multer({ storage: fileStorageEngine })
const router = express.Router();
//Get All Movies and TV shows.
router.get('/', getMedia);
//Request to create a new item based on a title
router.post('/', createMedia);
//Recuist to analyze information (not sure if this should be a post or not)
router.post('/analyze', AnalyzeText)
router.post('/analyze/upload', upload.single('analyze'), (req, res) => {
console.log(req.file)
res.status(200).json('well we found it again');
});
Turns out I had another middleware running that was wrapping my file upload. Removed that, everything works.
If you're using react you may face this problem sending your request with axios. But I solved it by adding a name attribute to my input element. And removing the new formData method totally and passing the input.file[0] into axios, content-type multipart-formdata, and you must use the multer.diskStorage method. If not your image would be saved as text file

Calling an api and return these values in Nodejs

I am trying to build a small website. In that i using React for frontend, Nodejs for backend, and some third party api. Here my idea is, first to post the form data to nodejs. And from then i accepting that data in node and need to call an external api. For this purpose i am using axios. After receiving values from my api i have to send that value back to react application. And when i run my code in postman, the output is {}. I think that i am not getting values from my api but dont know how to resolve this. And i am new to these technologies. Someone pls help me to sort out this problem. Thanking you in advance. Here is my what i have tried so far.
const express = require('express');
const axios = require('axios');
const router = express.Router();
const request = require('request');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended : false}));
router.get('/', (req, res) => {
res.send(" Express Homepage is running...");
});
async function callApi(emailid, pswd) {
return axios({
method:'post',
url:'http://51.X.X/api/login',
data: {
"email": `${emailid}`,
"password": `${pswd}`
},
headers: {'Content-Type': 'application/json' }
})};
callApi().then(function(response){
return response.data;
})
.catch(function(error){
console.log(error);
})
app.post('/api/login', (req, res) => {
let emailid = String(req.body.email);
let pswd = String(req.body.password);
const data = callApi(emailid, pswd);
if(data) {
res.send(data);
}else {
res.json({msg : " Response data not recieved.."})
}
});
use async/await syntax to handle asynchronous calls
app.post('/api/login', async (req, res) => {
let emailid = String(req.body.email);
let pswd = String(req.body.password);
const data = await callApi(emailid, pswd);
if(data) {
res.send(data);
}else {
res.json({msg : " Response data not recieved.."})
}
});
The problem is you are not waiting for async call to finish.
use async-await as mentioned in official doc https://www.npmjs.com/package/axios
function callAPI(){
const response = await axios({
method:'post',
url:'http://51.X.X/api/login',
data: {
"email": `${emailid}`,
"password": `${pswd}`
},
headers: {'Content-Type': 'application/json' }
})};
return response
}
app.post('/api/login', async (req, res) => {
let emailid = String(req.body.email);
let pswd = String(req.body.password);
//add try catch to catch exception
const data = await callApi(emailid, pswd);
if(data) {
//check for response from axios in official doc and send what data you
want to send
res.send(data);
}else {
res.json({msg : " Response data not recieved.."})
}
});

How to send a request from Nuxt.js client over Nuxt.js server and receive the response back to the client

I'm developing a Vue.js application which has only frontend (no server) and send a lot of requests to different APIs. The originally quite simple app became more complex. And there are problems with some APIs, because browsers do not accept the responses due to CORS. That is why I'm trying to test, if I can migrate the app to Nuxt.js.
My approach is as follows (inspired by this comment), but I expect, that there is probably a better way to send the requests from the client over the server.
pages/test-page.vue
methods: {
async sendRequest(testData) {
const response = await axios.post('api', testData)
// Here can I use the response on the page.
}
}
nuxt.config.js
serverMiddleware: [
{ path: '/api', handler: '~/server-middleware/postRequestHandler.js' }
],
server-middleware/postRequestHandler.js
import axios from 'axios'
const configs = require('../store/config.js')
module.exports = function(req, res, next) {
let body = ''
req.on('data', (data) => {
body += data
})
req.on('end', async () => {
if (req.hasOwnProperty('originalUrl') && req.originalUrl === '/api') {
const parsedBody = JSON.parse(body)
// Send the request from the server.
const response = await axios.post(
configs.state().testUrl,
body
)
req.body = response
}
next()
})
}
middleware/test.js (see: API: The Context)
export default function(context) {
// Universal keys
const { store } = context
// Server-side
if (process.server) {
const { req } = context
store.body = req.body
}
}
pages/api.vue
<template>
{{ body }}
</template>
<script>
export default {
middleware: 'test',
computed: {
body() {
return this.$store.body
}
}
}
</script>
When the user makes an action on the page "test", which will initiate the method "sendRequest()", then the request "axios.post('api', testData)" will result in a response, which contains the HTML code of the page "api". I can then extract the JSON "body" from the HTML.
I find the final step as suboptimal, but I have no idea, how can I send just the JSON and not the whole page. But I suppose, that there must be a much better way to get the data to the client.
There are two possible solutions:
Proxy (see: https://nuxtjs.org/faq/http-proxy)
API (see: https://medium.com/#johnryancottam/running-nuxt-in-parallel-with-express-ffbd1feef83c)
Ad 1. Proxy
The configuration of the proxy can look like this:
nuxt.config.js
module.exports = {
...
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
proxy: {
'/proxy/packagist-search/': {
target: 'https://packagist.org',
pathRewrite: {
'^/proxy/packagist-search/': '/search.json?q='
},
changeOrigin: true
}
},
...
}
The request over proxy can look like this:
axios
.get('/proxy/packagist-search/' + this.search.phpLibrary.searchPhrase)
.then((response) => {
console.log(
'Could get the values packagist.org',
response.data
)
}
})
.catch((e) => {
console.log(
'Could not get the values from packagist.org',
e
)
})
Ad 2. API
Select Express as the project’s server-side framework, when creating the new Nuxt.js app.
server/index.js
...
app.post('/api/confluence', confluence.send)
app.use(nuxt.render)
...
server/confluence.js (simplified)
const axios = require('axios')
const config = require('../nuxt.config.js')
exports.send = function(req, res) {
let body = ''
let page = {}
req.on('data', (data) => {
body += data
})
req.on('end', async () => {
const parsedBody = JSON.parse(body)
try {
page = await axios.get(
config.api.confluence.url.api + ...,
config.api.confluence.auth
)
} catch (e) {
console.log('ERROR: ', e)
}
}
res.json({
page
})
}
The request over API can look like this:
this.$axios
.post('api/confluence', postData)
.then((response) => {
console.log('Wiki response: ', response.data)
})
.catch((e) => {
console.log('Could not update the wiki page. ', e)
})
Now with nuxtjs3 :
nuxtjs3 rc release
you have fetch or useFetch no need to import axios or other libs, what is great, automatic parsing of body, automatic detection of head
fetching data
you have middleware and server api on same application, you can add headers on queries, hide for example token etc
server layer
a quick example here in vue file i call server api :
const { status } = await $fetch.raw( '/api/newsletter', { method: "POST", body: this.form.email } )
.then( (response) => ({
status: response.status,
}) )
.catch( (error) => ({
status: error?.response?.status || 500,
}) );
it will call a method on my server, to init the server on root directory i created a folder name server then api, and a file name newsletter.ts (i use typescript)
then in this file :
export default defineEventHandler(async (event) => {
const {REST_API, MAILINGLIST_UNID, MAILINGLIST_TOKEN} = useRuntimeConfig();
const subscriber = await readBody(event);
console.log("url used for rest call" + REST_API);
console.log("token" + MAILINGLIST_TOKEN);
console.log("mailing list unid" + MAILINGLIST_UNID);
let recipientWebDTO = {
email: subscriber,
subscriptions: [{
"mailingListUnid": MAILINGLIST_UNID
}]
};
const {status} = await $fetch.raw(REST_API, {
method: "POST",
body: recipientWebDTO,
headers: {
Authorization: MAILINGLIST_TOKEN,
},
}).then((response) => ({
status: response.status,
}))
.catch((error) => ({
status: error?.response?.status || 500,
}));
event.res.statusCode = status;
return "";
})
What are the benefits ?
REST_API,MAILING_LIST_UNID, MAILING_LIST_TOKEN are not exposed on
client and even file newsletter.ts is not available on debug browser.
You can add log only on server side You event not expose api url to avoid some attacks
You don't have to create a new backend just to hide some criticals token or datas
then it is up to you to choose middleware route or server api. You don't have to import new libs, h3 is embedded via nitro with nuxtjs3 and fetch with vuejs3
for proxy you have also sendProxy offered by h3 : sendProxy H3
When you build in dev server and client build in same time(and nothing to implement or configure in config file), and with build to o, just don deploy your project in static way (but i think you can deploy front in static and server in node i don't know)

React Native IOS application can not upload image through Express+ multer server

React Native IOS application, want to upload image; from device.
RN 0.39.2
Client:
const formData = new FormData()
formData.append('files', file)
formData.append('type', 'image')
fetch(API_HOST+UPLOAD_AVATAR,{
method:'post',
headers: {'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d','Authorization': 'Bearer'+' '+token},
body: formData
})
.then(response=>response.json())
.then(data=>{
//console.log(data)
//Alert.alert(data)
})
.catch(error=>{
console.log(error)
})
Server :
var multer = require('multer');
var upload = multer();
router.post('/user', ensureAuthenticated, upload.any(), function (req, res) {
console.log(req.body);
console.log(req.files);
})
Error:
server req.body and req.files are empty.
Then I try to use RNFetchBlob.
RNFetchBlob.fetch('POST', API_HOST+UPLOAD_AVATAR, {
'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d'
'Authorization' : 'Bearer'+' '+token
}, formData)
.then((resp) => {
}).catch((err) => {
// ...
})
then error change to
NSMutableDictionary cannot be converted to NSString.
And req.body is {}, req.files is undefined
I assume you found a solution to this, if yes, could you share it?.
In any case, for the RNFetchBlob issue, I used to get the same error and I solved by changing FormData to an array. Like this:
const body = [{
name: 'data',
data: JSON.stringify(whateverData)
}, {
name: 'file',
data: filePath,
}];
…
RNFetchBlob.fetch('POST', apiEndpoint, headers, body);
Hope that helps.