Asynchronous controller in Express for Form parsing to Mongoose - express

Currently, I'm developing a way to upload a message (file and fields) from Dropzone to Mongoose using Express Router. My back-end controller (which is called after authentication and data validation) goes as follows:
//Import Internal Dependencies
const Loader = require('../models/loader.js');
const Formidable = require('formidable');
const fs = require('fs');
module.exports = {
load: async (req, res, next) => {
var form = new Formidable.IncomingForm();
let path;
let contentType;
await form.parse(req, async function (err, fields, files) {
if (err) {
return res.status(404).json(err);
} else {
const {
user,
patient,
condition,
compound,
classi
} = fields;
path = files.image.path;
contentType = files.image.type;
fs.readFile(path, async function (err, data) {
if (err) {
return res.status(404).json(err);
} else {
//Save load
const newLoader = new Loader({
user,
patient,
condition,
compound,
classi,
image: {
data,
contentType
}
});
//Delete image in local storage
await fs.unlink(path, function (error) {
if(error){
return res.status(404).json(error)
}
});
await newLoader.save();
res.status(200).json("Load image sucessfully.");
next()
}
})
}
});
}
};
When I test it with Postman I got a status 202 and images are successfully upload to the database. However, when I try to upload with dropzone without the fields (which should cause some error and be displayed in dropzone) I got the following errors/warning in the back-end console (Dropzone stoped at upload and didn't show any error):
(node:20834) UnhandledPromiseRejectionWarning: ValidationError: load validation failed: user: Path `user` is required., classi: Path `classi` is required.
at new ValidationError (/root/aimuneBack/node_modules/mongoose/lib/error/validation.js:27:11)
at model.Document.invalidate (/root/aimuneBack/node_modules/mongoose/lib/document.js:1876:32)
at p.doValidate.skipSchemaValidators (/root/aimuneBack/node_modules/mongoose/lib/document.js:1744:17)
at /root/aimuneBack/node_modules/mongoose/lib/schematype.js:808:9
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
(node:20834) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:20834) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
at emitWarning (internal/process/promises.js:92:15)
at emitPendingUnhandledRejections (internal/process/promises.js:109:11)
at process._tickCallback (internal/process/next_tick.js:189:7)
POST /load - - ms - -
So I know I have done something wrong with my asynchronous code and unfortunately cannot figure it out. Hope you can help. Best regards, Andre

Related

Express Mongoose throw custom error on callbacks

I'm trying to throw some custom error classes from mongoose callbacks.
Here is a simple code
const Restaurant = require('../models/Restaurant')
const { InternalServerError, UnauthorizedError } = require('../errors/errors')
const checkRestaurantAuthorization = async (token) => {
const restaurant = Restaurant.findOne({ 'token': token }, function (error, result) {
if (error) throw new InternalServerError()
else if (!result) throw new UnauthorizedError()
else return token
})
}
In my code checkRestaurantAuthorization is called by a simple middleware like
const restaurantMidlleware = async (req, res, next) => {
console.log('Request Type:', req.method);
try {
token = await checkRestaurantAuthorization('invalid_token')
next()
} catch (error) {
next(error)
}
}
Now if a restaurant instance with the given token is not found, the app crashes with throw er; // Unhandled 'error' event. From my testing it seems that executions stops when throw new UnauthorizedError() is called and I'm unable to identify the issue.
Here is also an example of a custom defined error if it's useful
class UnauthorizedError extends Error {
constructor(message) {
super(message)
this.name = 'Unauthorized request'
this.code = 403
Error.captureStackTrace(this, UnauthorizedError)
}
}
What am I missing?
have you tried putting your first block in 'try-catch' block?
The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.
you can change code to promise or async-await
another source of the problem could be the fact that your are using async and callback in one function try to omit async then use it again
And there is no point in writing 'const restaurant =' in
const restaurant = Restaurant.findOne
since every found restaurant will be saved in callback's result variable
try this
function checkRestaurantAuthorization(token){
return new Promise(async(resolve, reject)=>{
try {
const restaurant = await Restaurant.findOne({ 'token': token });
if (!restaurant)
return reject(new UnauthorizedError())
else
return resolve(token)
}catch(error){
return reject(new InternalServerError())
}
})}
Even better approach would be using only async function with try-catch instead of returning a promise or any callback

how to execute UseGuard in nestjs microservie

I'm planning to use my authentication part as a microservice so I user nest js TCP transporter but I don't know how to execute passport local strategy in my microservice I used the below code
#MessagePattern('login')
#UseGuards(LocalAuthGuard)
localLogin( loginDto: LoginDto) {
console.log('awa')
return loginDto
// return this.authService.localLogin(req.user, loginDto.email);
}
but it doesn't work any idea how can I authorized local strategy in microservice my local strategy looks like below
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthService } from './auth.service';
import { Request } from 'express';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
});
}
async validate(
email: string,
password: string,
): Promise<{ id: string; isVerified: boolean }> {
try {
const user = await this.authService.validateLocalUser(email, password);
if (!user) {
//throw new UnauthorizedException();
}
return { id: user.id,isVerified: user.isVerified };
} catch (error) {
console.log('error')
}
}
}
This is the error I got
node:71975) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'reply' of null
at AppExceptionFilter.catch (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/core/exceptions/base-exception-filter.js:26:24)
at AppExceptionFilter.catch (/Users/imanthaatapattu/apps/beta-identity-service/dist/common/exception-filters/app-exception.filter.js:29:20)
at RpcExceptionsHandler.invokeCustomFilters (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/exceptions/rpc-exceptions-handler.js:34:32)
at RpcExceptionsHandler.handle (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/exceptions/rpc-exceptions-handler.js:13:36)
at RpcProxy.handleError (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/context/rpc-proxy.js:24:34)
at /Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/context/rpc-proxy.js:17:29
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:71975) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
(node:71975) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Passport is an Express middleware, meant for use over HTTP. You're using it with a middleware, which isn't quite expected. If you want to continue using passport with a microservice, you need to provide the same values it expects under the HTTP context. You can see Nest's implementation of the AuthGuard here. The most important part of this, is making sure that getRequest returns an object with the property body and with body having the properties email and password. It would most likely be easier to write your own microservice specific guard that can determine if the request is valid or not (via a JWT or similar)

How to prevent error propagation in Apollo Client useQuery in react?

I'd like to catch the error in component level and prevent propagation while using the useQuery in #apollo/react-hook.
Here is my example code
const invitationDocument = gql`
query DecodeInvitation($token: String!) {
DecodeInvitation(token: $token) {
name
email
}
}
`
const InvitationPage = (props) => {
const { data, error, loading } = useQuery(invitationDocument, {variables: { token: "XXXX" }});
if(error)
{
return <InvitationErrorPage error={error.message}/>
}
return loading? <LoadingPage> : <InvitationAcceptPage />
}
It works fine but at the same time, the error is being propagated to its parents level so I get another error notification message which comes from the error handler at the global level.
At the application level, I use the apollo-link-error to manage the Graphql errors.
import { onError } from 'apollo-link-error';
const errorLink = onError (({ graphqlErrors, networkError }) => {
if(graphqlErrors)
notification.error(graphqlErrors[0].message);
});
const client = ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([
errorLink,
new HttpLink({ uri: `http://localhost:8080/graphql`})
])
})
For now, I am finding a solution to stop propagation to top-level so that I can show only InvitationErrorPage and stop displaying error notification at the global level.
I was also trying to prevent errors from being logged in an Error Link by handling them on a useQuery hook and a further delve into the ApolloLink documentation helped clear up what is happening. The key misunderstanding is that the Error Link is not a parent- or application-level handler, it is request middleware. It's helpful to think about how the data is coming back from the server:
Thus, when you see an error notification from the Error Link it is not something that "propagated up" from the useQuery hook: it occurred in the request path before the useQuery result was available on the client.
Thus, the onError callback for the Error Link will always be called before any error handling code in the useQuery hook.
Probably your best bet is to use a combination of the operation and graphQLErrors[x].extensions to figure out what errors you should pass through the Error Link middleware like so:
const errorLink = onError(({operation, response, graphQLErrors}) => {
if (!graphQLErrors) {
return;
}
if (operation.operationName === "DecodeInvitation") {
for (const err of graphQLErrors) {
if (err.extensions?.code === 'UNAUTHENTICATED') {
// Return without "notifying"
return;
}
}
}
// Notify otherwise
notification.error(graphqlErrors[0].message);
})

Vuex, best practice with a global errors and notifications handling

here is what i do, and i'am not realy sure its correct :
//store
async addUser({commit}) {
try {
const {data} = await apiService.addUser()
commit('SET_USER', data)
commit('SET_NOTIFICATION', {type:'success', message: 'user successfuly created'})
} catch (error) {
commit('SET_NOTIFICATION', {type:'error', message:error})
}
}
SET_USER(state, user) {
state.users.push(user)
}
//my component:
async addUser() {
this.isLoading = true
await this.$store.dispatch('updatePatient', this.form)
this.isLoading = false
}
is it legit ?
sometimes i think i would need more logic inside my component depending on the succes or rejected api request. Should i put all the logic in my actions ? like i do at the moment ?
Maybe should I add a status state for each actions, for example :
state {
users: []
postUserSuccess: null
postUserError: false
updateUserSuccess: null
updateUserError: false
// ...
}
and do what i want in the component with a computed property mapped to the store ?
What do you think ?
I don't know if it's a best practice but I let the components the exception handling. That method has its pros (you don't have to pollute the state with error management) and cons (you have to repeat the error management code for every action call).
All service calls will be made in actions
The state will only be set in mutations.
All service calls will return a promise with a resolve(data to load in the state) and a reject(message errors to present).
There will be an interceptor to reject the response in case there's a custom error (here you can put if the response has an error prop reject the response and send as an error the error prop, now you don't have to deconstruct the response in the action).
I'm going to give you a simplified example (I use axios, you can learn how to do it with the library that you use).
Actions in Vuex are asynchronous. So you don't need to try/catch them.
ApiService - Add User
const addUser = () => {
return new Promise((resolve, reject) => {
axios
.post(url, user)
.then(response => resolve(response.data))
.catch(error => reject(error));
});
};
store
async addUser({commit}) {
const data = await apiService.addUser();
commit('SET_USER', data);
return data;
}
if the promise in apiService.addUser is resolved the commit is going to be made if is rejected axios will return the promise and you can catch the error in the component that calls the action.
Component
async addUser() {
this.isLoading = true;
try {
await this.$store.dispatch('updatePatient', this.form);
} catch (error) {
// here goes the code to display the error or do x if there is an error,
// sometimes I store an errors array in the data of the component other times I do x logic
}
this.isLoading = false;
}
State
Your state will be cleaner now that you don't need to store those errors there.
state {
users: []
}

How to download file from server using nodejs

I want to download file from other website to my pc using expressjs
I tried to use: res.download to download but it seems to be worked on my own server only
Here is my code:
res.download('http://apkleecher.com/download/dl.php?dl=com.instagram.android', 'folder', function(err){
if (err) {
console.log(err);
} else {
}
});
And it return the error:
{ Error: ENOENT: no such file or directory, stat '/home/keitaro/Desktop/google-play/http:/apkleecher.com/download/dl.php?dl=com.instagram.android'
errno: -2,
code: 'ENOENT',
syscall: 'stat',
path: '/home/keitaro/Desktop/google-play/http:/apkleecher.com/download/dl.php?dl=com.instagram.android',
expose: false,
statusCode: 404,
status: 404 }
In my guess, the problem is in the path of url.
Turning my comment into an answer since it worked for you...
You can fetch a resource from a remote web server using either http.get() or the request() module in node. If you like to use promises for your asynchronous operations, then the request-promise module is a promisified version of the request module and works great.
You can also use just plain http.get(), but it's a lot more work because you have to read the stream, rather the results yourself and install appropriate error handling, all of which the request() module does for you in one simple call.
Here's a simple example using the request-promise module:
const rp = require('request-promise');
rp('http://www.google.com').then(function (htmlString) {
// Process html...
}).catch(function (err) {
// process error here
});
res.download requires a path to your local filesystem.
try this:
res.redirect("http://apkleecher.com/download/dl.php?dl=com.instagram.android")
best way to download remote file is use stream .its use small mount of memory
**npm i got**
//========================
const got=require('got');
const fs=require('fs');
const path=require('path');
file_downloader(link,file_name){
var file_path = path.join(__dirname,file_name);
await got.stream(encodeURI(link))
.on('response', async (data) => {
//first response check headers like ['content-length']
})
.on('error', async (error) => {
console.log("===========Stream Error======= ");
console.log(error);
console.log("===========//End Stream Error======= ");
})
.on('downloadProgress', async (progress) => {
console.log(file.name, progress);
})
.pipe(fs.createWriteStream(file_path));
}