How to clone Request in nestjs guard? - node.js

I need to get request params from form-data request in nest guard,so I use multer in my guard
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const postMulterRequest: any = await new Promise((resolve, reject) => {
multer().any()(request, {}, function(err) {
if (err) reject(err);
resolve(request);
});
});
}
and I also need to get files and request data in controller
#UseInterceptors(FileInterceptor('filedata'))
async UploadedFile(#UploadedFile() file, #Body() body, #Request() req): Promise<ImgInfo> {}
when I did not use multer, it works. But after using multer, it performs:
file: undefined, body:{}
I can get file from the req but can not get other params
I want to know how can I get the from form-data request in guard without modifying the original request?
reference:https://stackoverflow.com/questions/59294070/nestjs-receive-form-data-in-guards/61407264#61407264

Related

Converting recevied body of axios POST to Blob/ArrayBuffer

I have a React/Typescript frontend that is suppose to send a file to an Express backend via an Axios POST call. The Express backend is then suppose to forward the file to Azure storage. The Azure append method expects "a string, Blob, ArrayBuffer, ArrayBufferView, or a function returning NodeJS.ReadableStream".
However, what I receive on the backend (req.body) is not in any of those formats. I am not quite sure what is the format of the data received by the backend is in and/or how to convert it to one of the required formats. The console.log(req.body) for a very simple image gives the following
[Object: null prototype] { '�PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00#\x00\x00\x00"\b\x02\x00\x00\x00Z���\x00\x00\x00\x01sRGB\x00��\x1C�\x00\x00\x00\x04gAMA\x00\x00��\x0B�a\x05\x00\x00\x00\tpHYs\x00\x00\x0E�\x00\x00\x0E�\x01�o�d\x00\x00\x00\x1AIDATHK��\x01\x01\x00\x00\x00� ��nH#\x00\x00\x00��\x01\x0E\x14\x00\x01\x05?��\x00\x00\x00\x00IEND�B`�': ''}
I tried setting Content-Type to multipart/form-data (and using formData.append as is done in this post How to post a file from a form with Axios) or setting Content-Type to file.type; however, in those cases I just receive empty object ({}) on the backend for req.body.
Frontend
const onHandleChange = async (event: any) => {
const file: File = event.currentTarget.files[0];
axios
.post("/api/files", file, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
.then((uploadResponse: AxiosResponse | Promise<AxiosResponse>) =>
console.log(uploadResponse)
);
};
return (
<>
<p>Demo</p>
<form>
<input type="file" id="fileField" onChange={onHandleChange}></input>
</form>
</>
);
Backend
router.post('/', async (req, res, next) => {
try {
const datalakeServiceClient = new DataLakeServiceClient(
`https://${ACCOUNT}.dfs.core.windows.net`,
defaultCredential
);
const fileSystemClient = datalakeServiceClient.getFileSystemClient(FILESYSTEM);
const fileClient = fileSystemClient.getFileClient('simpleFile');
const content = req.body;
const length = parseInt(req.header('Content-Length'));
await fileClient.create();
await fileClient.append(content, 0, length); // This line expects Blob/ArrayBuffer, etc. and gives the error
await fileClient.flush(length);
console.log('Done');
} catch (error) {
throw new Error(error);
}
});

How to upload image on NestJS server from another service where image is in the form of URL?

I have a controller made in my NestJS service S1, which accepts file using FileInterceptor, the function signature looks like this:
#UseInterceptors(FileInterceptor('file'))
#Post('uploads')
async uploadAttachment(#UploadedFile() file, #Query() queryParams: {filename: string}, #Res() res: Response, #Headers() header, #Req() req)
{
//Some logic here
}
Now I want to use this API to upload an image from a different service S2, but I only have the image's publicly accessible URL
I am trying like this:
import * as formData from 'form-data';
import fetch from "node-fetch";
let serv = await fetch(url);
let img = await serv.buffer();
let url = "http://localhost:8000/api/v1/ticketing/uploads?filename=checking.jpg"
form.append("file", Readable.from(img));
return firstValueFrom(this.httpService.post(url, form, {headers: {...header, ...form.getHeaders()}}).pipe(
map((resp) => resp.data),
catchError((_err) => {
throw new HttpException(
{success: false, error: {message: "failed"}, payload: _err.response.data},
HttpStatus.BAD_REQUEST,
);
})
)
);
But this is not working, apparently the file is undefined on the controller of service S1. Could someone please help me out with this? I am new to NodeJS
Also apologies for the poor editing.

There is a way to make Axios return the data as default response?

When we use Axios we always have to get the data from response. Like this:
const response = await Axios.get('/url')
const data = response.data
There is a way to make Axios return the data already? Like this:
const data = await Axios.get('/url')
We never used anything besides the data from the response.
You can use ES6 Destructing like this:
const { data } = await Axios.get('/url');
So you won't have write another line of code.
add a response interceptors
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data; // do like this
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
what i normally do is create a js file called interceptors.js
import axios from 'axios';
export function registerInterceptors() {
axios.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
);
}
in ./src/index.js
import { registerInterceptors } from './path/to/interceptors';
registerInterceptors();//this will register the interceptors.
For a best practice don't use axios every where, just in case in the future if you want to migrate to a different http provider then you have to change everywhere it uses.
create a wrapper around axios and use that wrapper in your app
for ex:
create a js file called http.js
const execute = ({url, method, params, data}) => {
return axios({
url,
method,//GET or POST
data,
params,
});
}
const get = (url, params) => {
return execute({
url, method: 'GET', params
})
}
const post = (url, data) => {
return execute({
url, method: 'POST', data
})
}
export default {
get,
post,
};
and use it like
import http from './http';
....
http.get('url', {a:1, b:2})
so now you can customize all over the app, even changing the http provider is so simple.

NestJS - File upload to microservice

I need to upload a file to an API-Gateway. After adding some meta information, the file should be send to another (micro) service (as Content-Type: multipart/form-data). I am having some problems to build a FormData object within the API-Gateway. I do not want to persist the file on the gateway, so I am basically just trying to pass it through.
For creating the formData-object, I am using Form-Data
This is what a tried:
// Controller
#Post()
#UseInterceptors(FileInterceptor('file'))
async create(#Res() res, #UploadedFile('file') file, #Body() body: any) {
return await this.someService.create(file);
}
// Service
async create(file: any) {
const formData = new FormData();
formData.append('file', file);
formData.append('key', 'value');
const formHeaders = formData.getHeaders();
try {
const result = await this.httpService
.post('http://some-other-service/import', formData , {
headers: {
...formHeaders,
},
})
.toPromise();
return result.data;
} catch (e) {
throw new BadGatewayException();
}
}
This results in the following error:
TypeError: source.on is not a function
at Function.DelayedStream.create (/usr/app/node_modules/delayed-stream/lib/delayed_stream.js:33:10)
at FormData.CombinedStream.append (/usr/app/node_modules/combined-stream/lib/combined_stream.js:44:37)
at FormData.append (/usr/app/node_modules/form-data/lib/form_data.js:74:3)
at ImportService.<anonymous> (/usr/app/src/import/import.service.ts:47:18)
This question is a bit old, but someone might benefit from this solution.
The problem is that you are passing the whole #UploadedFile object to the formData.append method. The #UploadedFile object contains the data from from the file, but also mimetype, size, fieldname ('file' in this case), originalname (the original file name), etc.
You need to pass the actual contents of the file you are trying to upload to the formData.append method.
So to make it work, use
formData.append('file', file.buffer);
//OR
formData.append('file', file.buffer, file.originalname);

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

Resources