Cannot catch BadRequestException thrown by FilesInterceptor - node.js

I am trying to set up a logic to save uploaded files using Multer.
To do this, I follow the Nestjs tutorial and use the "FilesInterceptor" interceptor.
controller file :
import {
Controller,
FileValidator,
ParseFilePipe,
Post,
UploadedFiles,
UseInterceptors
} from '#nestjs/common';
import { FilesInterceptor } from '#nestjs/platform-express';
import { Public } from 'src/auth/decorators/public.decorator';
import { MimeTypeValidationPipe } from './pipes/mimetype.validation.pipe';
const ACCEPTED_FILE_MIMETYPES = ["image/jpeg", "image/jpg", "image/png"]
const validators: FileValidator[] = [
new MimeTypeValidationPipe({ accepted: ACCEPTED_FILE_MIMETYPES })
];
#Controller('uploads')
export class UploadsController {
#Public()
#Post("/")
#UseInterceptors(FilesInterceptor("files"))
public async create(
#UploadedFiles(new ParseFilePipe({ validators }))
files: Express.Multer.File[]
){
files[0].originalname
const filenames = files.map(({ originalname }) => originalname)
return { filenames };
}
}
However, when I test the behavior of the server when the number of uploaded files exceeds the limit, the server returns me an error 500 (As if the error was not handled).
I then try to catch it by using an ExcepetionFilter like this one:
#Catch()
class TestFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
console.debug(exception)
if(exception instanceof HttpException) console.debug("This is an HTTP Exception !");
else console.debug("This is NOT an HTTP Exception");
const response = host.switchToHttp().getResponse<Response>();
return response.status(500).json({statusCode: 500 , message: "ERROR" });
}
}
And i get the following output :
BadRequestException: Too many files
at transformException (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/multer/multer/multer.utils.js:19:20)
at ~/development/Nest/nestapi/node_modules/#nestjs/platform-express/multer/interceptors/files.interceptor.js:18:73
at ~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/multer/lib/make-middleware.js:53:37
at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
at listener (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/on-finished/index.js:170:15)
at onFinish (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/on-finished/index.js:101:5)
at callback (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/ee-first/index.js:55:10)
at IncomingMessage.onevent (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/ee-first/index.js:93:5)
at IncomingMessage.emit (node:events:539:35)
at endReadableNT (node:internal/streams/readable:1345:12) {
response: { statusCode: 400, message: 'Too many files', error: 'Bad Request' },
status: 400
}
This is NOT an HTTP Exception
The filter indicates that it is NOT an HTTPException.
However, while digging in the FilesInterceptor.ts code I notice that the caught errors are handled by a small utility function "transformException" which is supposed to transform the Multer error into an HttpException (depending on the error code returned by Multer)
multer.utils.ts file (from nest repo)
import {
BadRequestException,
HttpException,
PayloadTooLargeException,
} from '#nestjs/common';
import { multerExceptions } from './multer.constants';
export function transformException(error: Error | undefined) {
if (!error || error instanceof HttpException) {
return error;
}
switch (error.message) {
case multerExceptions.LIMIT_FILE_SIZE:
return new PayloadTooLargeException(error.message);
case multerExceptions.LIMIT_FILE_COUNT:
case multerExceptions.LIMIT_FIELD_KEY:
case multerExceptions.LIMIT_FIELD_VALUE:
case multerExceptions.LIMIT_FIELD_COUNT:
case multerExceptions.LIMIT_UNEXPECTED_FILE:
case multerExceptions.LIMIT_PART_COUNT:
return new BadRequestException(error.message);
}
return error;
}
I don't understand why my filter (and NestJS' unhandledExceptionFilter) can't detect this exception, since for me it is supposed to be an instance of HttpException.
Can you help me?
Best regards

You probably have 2 copies of #nestjs/common being included in your project. The code that creates the error is using one copy, and your exception filter is using the other copy. When your code is checking instanceof, it's checking to see if the exception is an instance of HttpException from it's copy of #nestjs/common, but it's not, it's an instance of HttpException from the other copy of #nestjs/common. This is known as "multiple realms" (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms).
The way to fix this is to make sure you only have 1 copy of #nestjs/common in your project. Often, the reason you have 2 is because you have 2 package.json files with different version specs that they call for (e.g. "#nestjs/common": "^8.0.0" in one package.json, and "#nestjs/common": "^9.0.0" in another). You may need to use e.g. the overrides key to force a dependency to use the same version that you use elsewhere.
Hope that helps!

Sorry!
I think the problem is with me.
The LTS version (1.4.4-lts.1) of multer is buggy. So I decided to downgrade to 1.4.4 (version in which the bug in question does not occur). But to do so, I had to downgrade the nested dependency manually by doing npm install multer#1.4.4 in the node_modules/#nest/platform-express directory.
But that's when nestjs starts to format my errors badly.
The funny thing is that going back (npm install multer#1.4.4-lts.1 to the node_modules/#nest/platform-express directory), it doesn't solve the problem (Errors are still badly handled) and I have to delete the node_modules/#nest/platform-express folder and reinstall the package from the root of the project to get everything back in order (But with the LTS version bug, of course).
It's weird.

Related

Is there any way to use Pipes with Grpc in Nestjs?

So I'm building an httpgateway which sends messages to a microservice made with nestjs/grpc.
Problem is that, once I decorate my controller with #UsePipes(....) it throws an error for the gateway. I tried to log data entering into pipe and found out that grpc sends not only payload but also metadata and ServerDuplexStream prior to payload itself. So, my consumer throws an error because it faces with the ServerDuplexStream at first and cannot validate the args inside it.
I further tried to use my pipes in app.service but it doesnt make any sense since pipes receive data from the request. So it doesnt work as expected.
Is there a workaround like putting all three in a call in my gateway prior to sending request?
You can see an example of a pipe that im trying to implement:
#Injectable()
export class ValidateSingleBalanceByUser implements PipeTransform {
transform(value: SingleBalanceDto) {
if (!value.user) throw new RpcException('Provide user value to query!');
if (!value.asset) throw new RpcException('Provide asset value to query!');
return value;
}
}
and an example of a controller that im trying to implement to
#UsePipes(new ValidateSingleBalanceByUser())
#GrpcMethod('BridgeService', 'getSingleBalanceByUser')
singleBalanceByUser(data: SingleBalanceDto): Promise<Balance> {
return this.balancesService.handleSingleBalanceByUser(data);
}
I was getting the same problem, cause the Validation its a pipe and does not treat grpc exceptions. I fixed it using this solution:
controller.ts
#GrpcMethod('service','action')
#UsePipes(new ValidationPipe({ transform: true }))
#UseFilters(new TranslateHttpToGrpcExceptionFilter())
method(){}
translate.ts
#Catch(HttpException)
export class TranslateHttpToGrpcExceptionFilter implements ExceptionFilter {
static HttpStatusCode: Record<number, number> = {
[HttpStatus.BAD_REQUEST]: status.INVALID_ARGUMENT,
[HttpStatus.UNAUTHORIZED]: status.UNAUTHENTICATED,
[HttpStatus.FORBIDDEN]: status.PERMISSION_DENIED,
[HttpStatus.NOT_FOUND]: status.NOT_FOUND,
[HttpStatus.CONFLICT]: status.ALREADY_EXISTS,
[HttpStatus.GONE]: status.ABORTED,
[HttpStatus.TOO_MANY_REQUESTS]: status.RESOURCE_EXHAUSTED,
499: status.CANCELLED,
[HttpStatus.INTERNAL_SERVER_ERROR]: status.INTERNAL,
[HttpStatus.NOT_IMPLEMENTED]: status.UNIMPLEMENTED,
[HttpStatus.BAD_GATEWAY]: status.UNKNOWN,
[HttpStatus.SERVICE_UNAVAILABLE]: status.UNAVAILABLE,
[HttpStatus.GATEWAY_TIMEOUT]: status.DEADLINE_EXCEEDED,
[HttpStatus.HTTP_VERSION_NOT_SUPPORTED]: status.UNAVAILABLE,
[HttpStatus.PAYLOAD_TOO_LARGE]: status.OUT_OF_RANGE,
[HttpStatus.UNSUPPORTED_MEDIA_TYPE]: status.CANCELLED,
[HttpStatus.UNPROCESSABLE_ENTITY]: status.CANCELLED,
[HttpStatus.I_AM_A_TEAPOT]: status.UNKNOWN,
[HttpStatus.METHOD_NOT_ALLOWED]: status.CANCELLED,
[HttpStatus.PRECONDITION_FAILED]: status.FAILED_PRECONDITION
}
catch(exception: HttpException): Observable<never> | void {
const httpStatus = exception.getStatus()
const httpRes = exception.getResponse() as { details?: unknown, message: unknown }
return throwError(() => ({
code: TranslateHttpToGrpcExceptionFilter.HttpStatusCode[httpStatus] ?? status.UNKNOWN,
message: httpRes.message || exception.message,
details: Array.isArray(httpRes.details) ? httpRes.details : httpRes.message
}))
}
}
Hope it helps!

NodeJs import issue with Jest

Issue on facebook/jest
Currently working on a project where I need to dynamically load other components in the main application system, I'm facing an issue with some error that throws a Jest Invariant constraint violation when trying to import dynamically a module.
An example is accessible at https://gitlab.com/matthieu88160/stryker-issue
In this project, I have a class used to load a component and another that will use the ComponentLoader and add some check and post-processing (this part of the code is not present in the accessible project).
In the GitLab job, two test suites are executed and one fails. My problem is that the two test suites are exactly the same, obviously, I copy-pasted the first one to create. The second one and by the way demonstrate the issue.
I already tried some workaround found on the web without any success.
Here is the code of the component I try to test :
import {existsSync} from 'fs';
import {format} from 'util';
export default class ComponentLoader
{
static load(file) {
if(existsSync(file)) {
return import(file);
}
throw new Error(
format('Component file "%s" does not exist. Cannot load module', file)
);
}
}
And the test itself:
import {describe, expect, test} from '#jest/globals';
import {format} from 'util';
import {fileURLToPath} from 'url';
import {dirname} from 'path';
import mock from 'jest-mock';
describe(
'ComponentLoader',
() => {
describe('load', () => {
test('load method is able to load a component from a file', () => new Promise(
resolve => {
Promise.all(
[import('../src/ComponentLoader.mjs'), import('./fixture/component/A1/component.fixture.mjs')]
).then(modules => {
const file = format(
'%s/./fixture/component/A1/component.fixture.mjs',
dirname(fileURLToPath(import.meta.url))
);
modules[0].default.load(file).then(obj => {
expect(obj).toBe(modules[1]);
resolve();
});
});
})
);
});
}
);
And here the error report:
PASS test/ComponentLoaderA.test.mjs
FAIL test/ComponentLoaderB.test.mjs
● ComponentLoader › load › load method is able to load a component from a file
15 | static load(file) {
16 | if(existsSync(file)) {
> 17 | return import(file);
| ^
18 | }
19 |
20 | throw new Error(
at invariant (node_modules/jest-runtime/build/index.js:2004:11)
at Function.load (src/ComponentLoader.mjs:17:13)
at test/ComponentLoaderB.test.mjs:21:44
The interesting element from my point of view is the fact the ComponentLoaderA.test.mjs and the ComponentLoaderB.test.mjs are exactly the same.
The full error trace I found is:
CComponentLoader load load method is able to load a component from a file
Error:
at invariant (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:2004:11)
at Runtime.loadEsmModule (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:534:7)
at Runtime.linkModules (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:616:19)
at importModuleDynamically (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:555:16)
at importModuleDynamicallyWrapper (internal/vm/module.js:443:21)
at exports.importModuleDynamicallyCallback (internal/process/esm_loader.js:30:14)
at Function.load (importTest/.stryker-tmp/sandbox7042873/src/ComponentLoader.mjs:89:11)
at importTest/.stryker-tmp/sandbox7042873/test/ComponentLoaderB.test.mjs:22:37
at new Promise (<anonymous>)
at Object.<anonymous> (importTest/.stryker-tmp/sandbox7042873/test/ComponentLoaderB.test.mjs:14:79)
It seems the error does not have any message.
Further information from the jest-runtime investigation :
It seems that between the tests, the sandbox context is lost for a reason I cannot be able to manage to find at the moment.
In node_modules/jest-runtime/build/index.js:2004:11 :
The condition is NULL, the error is then thrown without a message.
function invariant(condition, message) {
if (!condition) {
throw new Error(message);
}
}
In node_modules/jest-runtime/build/index.js:534:7 :
The context is NULL, and no message given, creating my empty error message.
const context = this._environment.getVmContext();
invariant(context);
The toString() of this._environment.getVmContext method as follow:
getVmContext() {
return this.context;
}
The current _environment presents a null context :
NodeEnvironment {
context: null,
fakeTimers: null,
[...]
}
The deeper point I can reach is this code where the context appear to be null :
const module = new (_vm().SourceTextModule)(transformedCode, {
context,
identifier: modulePath,
importModuleDynamically: (specifier, referencingModule) => {
return this.linkModules(
specifier,
referencingModule.identifier,
referencingModule.context
)},
initializeImportMeta(meta) {
meta.url = (0, _url().pathToFileURL)(modulePath).href;
}
});
The context variable is not empty and _vm().SourceTextModule is a class extending Module.
I can notice in the importModuleDynamically execution using console.log(this._environment) that the context is currently null.

Module not found: Can't resolve 'fs' in Next.js application

Unable to identify what's happening in my next.js app. As fs is a default file system module of nodejs. It is giving the error of module not found.
If you use fs, be sure it's only within getInitialProps or getServerSideProps. (anything includes server-side rendering).
You may also need to create a next.config.js file with the following content to get the client bundle to build:
For webpack4
module.exports = {
webpack: (config, { isServer }) => {
// Fixes npm packages that depend on `fs` module
if (!isServer) {
config.node = {
fs: 'empty'
}
}
return config
}
}
For webpack5
module.exports = {
webpack5: true,
webpack: (config) => {
config.resolve.fallback = { fs: false };
return config;
},
};
Note: for other modules such as path, you can add multiple arguments such as
{
fs: false,
path: false
}
I spent hours on this and the solution is also here on Stackoverflow but on different issue -> https://stackoverflow.com/a/67478653/17562602
Hereby I asked for MOD permission to reshare this, since this issue is the first one to show up on Google and probably more and more people stumble would upon the same problem as I am, so I'll try to saved them some sweats
Soo, You need to add this in your next.config.js
module.exports = {
future: {
webpack5: true, // by default, if you customize webpack config, they switch back to version 4.
// Looks like backward compatibility approach.
},
webpack(config) {
config.resolve.fallback = {
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
// by next.js will be dropped. Doesn't make much sense, but how it is
fs: false, // the solution
};
return config;
},
};
It works for like a charm for me
Minimal reproducible example
A clean minimal example will be beneficial to Webpack beginners since auto splitting based on usage is so mind-blowingly magic.
Working hello world baseline:
pages/index.js
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
export function getStaticProps() {
return { props: { msg: 'hello world' } }
}
package.json
{
"name": "test",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "12.0.7",
"react": "17.0.2",
"react-dom": "17.0.2"
}
}
Run with:
npm install
npm run dev
Now let's add a dummy require('fs') to blow things up:
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
const fs = require('fs')
export function getStaticProps() {
return { props: { msg: 'hello world' } }
}
fails with:
Module not found: Can't resolve 'fs'
which is not too surprising, since there was no way for Next.js to know that that fs was server only, and we wouldn't want it to just ignore random require errors, right? Next.js only knows that for getStaticProps because that's a hardcoded Next.js function name.
OK, so let's inform Next.js by using fs inside getStaticProps, the following works again:
// Client + server code.
export default function IndexPage(props) {
return <div>{props.msg}</div>
}
// Server-only code.
const fs = require('fs')
export function getStaticProps() {
fs
return { props: { msg: 'hello world' } }
}
Mind equals blown. So we understand that any mention of fs inside of the body of getStaticProps, even an useless one like the above, makes Next.js/Webpack understand that it is going to be server-only.
Things would work the same for getServerSideProps and getStaticPaths.
Higher order components (HOCs) have to be in their own files
Now, the way that we factor out IndexPage and getStaticProps across different but similar pages is to use HOCs, which are just functions that return other functions.
HOCs will normally be put outside of pages/ and then required from multiple locations, but when you are about to factor things out to generalize, you might be tempted to put them directly in the pages/ file temporarily, something like:
// Client + server code.
import Link from 'next/link'
export function makeIndexPage(isIndex) {
return (props) => {
return <>
<Link href={isIndex ? '/index' : '/notindex'}>
<a>{isIndex ? 'index' : 'notindex'}</a>
</Link>
<div>{props.fs}</div>
<div>{props.isBlue}</div>
</>
}
}
export default makeIndexPage(true)
// Server-only code.
const fs = require('fs')
export function makeGetStaticProps(isBlue) {
return () => {
return { props: {
fs: Object.keys(fs).join(' '),
isBlue,
} }
}
}
export const getStaticProps = makeGetStaticProps(true)
but if you do this you will be saddened to see:
Module not found: Can't resolve 'fs'
So we understand another thing: the fs usage has to be directly inside the getStaticProps function body, Webpack can't catch it in subfunctions.
The only way to solve this is to have a separate file for the backend-only stuff as in:
pages/index.js
// Client + server code.
import { makeIndexPage } from "../front"
export default makeIndexPage(true)
// Server-only code.
import { makeGetStaticProps } from "../back"
export const getStaticProps = makeGetStaticProps(true)
pages/notindex.js
// Client + server code.
import { makeIndexPage } from "../front"
export default makeIndexPage(false)
// Server-only code.
import { makeGetStaticProps } from "../back"
export const getStaticProps = makeGetStaticProps(false)
front.js
// Client + server code.
import Link from 'next/link'
export function makeIndexPage(isIndex) {
return (props) => {
console.error('page');
return <>
<Link href={isIndex ? '/notindex' : '/'}>
<a>{isIndex ? 'notindex' : 'index'}</a>
</Link>
<div>{props.fs}</div>
<div>{props.isBlue}</div>
</>
}
}
back.js
// Server-only code.
const fs = require('fs')
export function makeGetStaticProps(isBlue) {
return () => {
return { props: {
fs: Object.keys(fs).join(' '),
isBlue,
} }
}
}
Webpack must see that name makeGetStaticProps getting assigned to getStaticProps, so it decides that the entire back file is server-only.
Note that it does not work if you try to merge back.js and front.js into a single file, probably because when you do export default makeIndexPage(true) webpack necessarily tries to pull the entire front.js file into the frontend, which includes the fs, so it fails.
This leads to a natural (and basically almost mandatory) split of library files between:
front.js and front/*: front-end + backend files. These are safe for the frontend. And the backend can do whatever the frontend can do (we are doing SSR right?) so those are also usable from the backend.
Perhaps this is the idea behind the conventional "components" folder in many official examples. But that is a bad name, because that folder should not only contain components, but also any library non-component helpers/constants that will be used from the frontend.
back.js and back/* (or alternatively anything outside of front/*): backend only files. These can only be used by the backend, importing them on frontend will lead to the error
fs,path or other node native modules can be used only inside server-side code, like "getServerSide" functions. If you try to use it in client you get error even you just console.log it.. That console.log should run inside server-side functions as well.
When you import "fs" and use it in server-side, next.js is clever enough to see that you use it in server-side so it wont add that import into the client bundle
One of the packages that I used was giving me this error, I fixed this with
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback.fs = false
}
return config
},
}
but this was throwing warning on terminal:
"Critical dependency: require function is used in a way in which
dependencies cannot be statically extracted"
Then I tried to load the node module on the browser. I copied the "min.js" of the node module from the node_modules and placed in "public/js/myPackage.js" and load it with Script
export default function BaseLayout({children}) {
return (
<>
<Script
// this in public folder
src="/js/myPackage.js"
// this means this script will be loaded first
strategy="beforeInteractive"
/>
</>
)
}
This package was attached to window object and in node_modules source code's index.js:
if (typeof window !== "undefined") {
window.TruffleContract = contract;
}
So I could access to this script as window.TruffleContract. BUt this was not an efficient way.
While this error requires a bit more reasoning than most errors you'll encounter, it happens for a straightforward reason.
Why this happens
Next.js, unlike many frameworks allows you to import server-only (Node.js APIs that don't work in a browser) code into your page files. When Next.js builds your project, it removes server only code from your client-side bundle by checking which code exists inside one any of the following built-in methods (code splitting):
getServerSideProps
getStaticProps
getStaticPaths
Side note: there is a demo app that visualizes how this works.
The Module not found: can't resolve 'xyz' error happens when you try to use server only code outside of these methods.
Error example 1 - basic
To reproduce this error, let's start with a working simple Next.js page file.
WORKING file
/** THIS FILE WORKS FINE! */
import type { GetServerSideProps } from "next";
import fs from "fs"; // our server-only import
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
const fileExists = fs.existsSync("/some-file");
return {
props: {
doesFileExist: fileExists,
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};
export default ExamplePage;
Now, let's reproduce the error by moving our fs.existsSync method outside of getServerSideProps. The difference is subtle, but the code below will throw our dreaded Module not found error.
ERROR file
import type { GetServerSideProps } from "next";
import fs from "fs";
type Props = {
doesFileExist: boolean;
};
/** ERROR!! - Module not found: can't resolve 'fs' */
const fileExists = fs.existsSync("/some-file");
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: fileExists,
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {doesFileExist ? "Yes" : "No"}</div>;
};
export default ExamplePage;
Error example 2 - realistic
The most common (and confusing) occurrence of this error happens when you are using modules that contain multiple types of code (client-side + server-side).
Let's say I have the following module called file-utils.ts:
import fs from 'fs'
// This code only works server-side
export function getFileExistence(filepath: string) {
return fs.existsSync(filepath)
}
// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
In this module, we have one server-only method and one "shared" method that in theory should work client-side (but as we'll see, theory isn't perfect).
Now, let's try incorporating this into our Next.js page file.
/** ERROR!! */
import type { GetServerSideProps } from "next";
import { getFileExistence, formatResult } from './file-utils.ts'
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: getFileExistence('/some-file')
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
// ERROR!!!
return <div>File exists?: {formatResult(doesFileExist)}</div>;
};
export default ExamplePage;
As you can see, we get an error here because when we attempt to use formatResult client-side, our module still has to import the server-side code.
To fix this, we need to split our modules up into two categories:
Server only
Shared code (client or server)
// file-utils.ts
import fs from 'fs'
// This code (and entire file) only works server-side
export function getFileExistence(filepath: string) {
return fs.existsSync(filepath)
}
// file-format-utils.ts
// This code works fine on both the server AND the client
export function formatResult(fileExistsResult: boolean) {
return fileExistsResult ? 'Yes, file exists' : 'No, file does not exist'
}
Now, we can create a WORKING page file:
/** WORKING! */
import type { GetServerSideProps } from "next";
import { getFileExistence } from './file-utils.ts' // server only
import { formatResult } from './file-format-utils.ts' // shared
type Props = {
doesFileExist: boolean;
};
export const getServerSideProps: GetServerSideProps = async () => {
return {
props: {
doesFileExist: getFileExistence('/some-file')
},
};
};
const ExamplePage = ({ doesFileExist }: Props) => {
return <div>File exists?: {formatResult(doesFileExist)}</div>;
};
export default ExamplePage;
Solutions
There are 2 ways to solve this:
The "correct" way
The "just get it working" way
The "Correct" way
The best way to solve this error is to make sure that you understand why it is happening (above) and make sure you are only using server-side code inside getStaticPaths, getStaticProps, or getServerSideProps and NOWHERE else.
And remember, if you import a module that contains both server-side and client-side code, you cannot use any of the imports from that module client-side (revisit example #2 above).
The "Just get it working" way
As others have suggested, you can alter your next.config.js to ignore certain modules at build-time. This means that when Next.js attempts to split your page file between server only and shared code, it will not try to polyfill Node.js APIs that fail to build client-side.
In this case, you just need:
/** next.config.js - with Webpack v5.x */
module.exports = {
... other settings ...
webpack: (config, { isServer }) => {
// If client-side, don't polyfill `fs`
if (!isServer) {
config.resolve.fallback = {
fs: false,
};
}
return config;
},
};
Drawbacks of this approach
As shown in the resolve.fallback section of the Webpack documentation, the primary reason for this config option is because as-of Webpack v5.x, core Node.js modules are no longer polyfilled by default. Therefore, the main purpose for this option is to provide a way for you to define which polyfill you want to use.
When you pass false as an option, this means, "do not include a polyfill".
While this works, it can be fragile and require ongoing maintenance to include any new modules that you introduce to your project. Unless you are converting an existing project / supporting legacy code, it is best to go for option #1 above as it promotes better module organization according to how Next.js actually splits the code under the hood.
If trying to use fs-extra in Next.js, this worked for me
module.exports = {
webpack: (config) => {
config.resolve.fallback = { fs: false, path: false, stream: false, constants: false };
return config;
}
}
I got this error in my NextJS app because I was missing export in
export function getStaticProps()
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: false,
webpack5: true,
webpack: (config) => {
config.resolve.fallback = {
fs: false,
net: false,
dns: false,
child_process: false,
tls: false,
};
return config;
},
};
This code fixed my problem and I want to share.Add this code to your next.config file.i'm using
webpack5
For me clearing the cache
npm cache clean -f
and then updating the node version to the latest stable release(14.17.0) worked
It might be that the module you are trying to implement is not supposed to run in a browser. I.e. it's server-side only.
For me, the problem was the old version of the node.js installed. It requires node.js version 14 and higher. The solution was to go to the node.js web page, download the latest version and just install it. And then re-run the project. All worked!
I had the same issue when I was trying to use babel.
For me this worked:
#add a .babelrc file to the root of the project and define presets and plugins
(in my case, I had some issues with the macros of babel, so I defined them)
{
"presets": ["next/babel"],
"plugins": ["macros"]
}
after that shut down your server and run it again
I had this exact issue. My problem was that I was importing types that I had declared in a types.d.ts file.
I was importing it like this, thanks to the autofill provided by VSCode.
import {CUSTOM_TYPE} from './types'
It should have been like this:
import {CUSTOM_TYPE} from './types.d'
In my case, I think the .d was unnecessary so I ended up removing it entirely and renamed my file to types.ts.
Weird enough, it was being imported directly into index.tsx without issues, but any helper files/functions inside the src directory would give me errors.
I ran into this in a NextJS application because I had defined a new helper function directly below getServerSideProps(), but had not yet called that function inside getServerSideProps().
I'm not sure why this created a problem, but it did. I could only get it to work by either calling that function, removing it, or commenting it out.
Don't use fs in the pages directory, since next.js suppose that files in pages directory are running in browser environment.
You could put the util file which uses fs to other directory such as /core
Then require the util in getStaticProps which runs in node.js environment.
// /pages/myPage/index.tsx
import View from './view';
export default View;
export async function getStaticProps() {
const util = require('core/some-util-uses-fs').default; // getStaticProps runs in nodes
const data = await util.getDataFromDisk();
return {
props: {
data,
},
};
}
In my case, this error appeared while refactoring the auth flow of a Next.js page. The cause was some an unused imports that I had not yet removed.
Previously I made the page a protected route like so:
export async function getServerSideProps ({ query, req, res }) {
const session = await unstable_getServerSession(req, res, authOptions)
if (!session) {
return {
redirect: {
destination: '/signin',
permanent: false,
},
}
}
//... rest of server-side logic
}
Whilst refactoring, I read up on NextAuth useSession. Based on what I read there, I was able to change the implementation such that I simply needed to add
MyComponent.auth = true to make a page protected. I then deleted the aforementioned code block inside of getServerSideProps. However, I had not yet deleted the two imports used by said code block:
import { unstable_getServerSession } from 'next-auth/next'
import { authOptions } from 'pages/api/auth/[...nextauth]'
I believe the second of those two imports was causing the problem. So the summary is that in addition to all of the great answers above, it could also be an unused import.
Sometimes this error can be because you have imported something but not mastered it anywhere. This worked for me. I reviewed my code and removed the unused dependencies.

Connection "default" was not found - TypeORM, NestJS and external NPM Package

I'm using NestJs to create a couple of applications and I want to move the code from a NestInterceptor for an external NPM Package so I can use the same interceptor in multiple applications.
The problem is that the same code that works when used "locally" just stop working when moved to the external package.
Here's the code for the interceptor:
import { Injectable, NestInterceptor, CallHandler, ExecutionContext } from '#nestjs/common'
import { map } from 'rxjs/operators'
import { getManager } from 'typeorm'
import jwt_decode from 'jwt-decode'
#Injectable()
export class MyInterceptor implements NestInterceptor {
entity: any
constructor(entity: any) {
this.entity = entity
}
async intercept(context: ExecutionContext, next: CallHandler): Promise<any> {
const request = context.switchToHttp().getRequest()
const repository = getManager().getRepository(this.entity)
return next.handle().pipe(map((data) => data))
}
}
Here's a given controller:
import { myInterceptor } from "../src/interceptors/interceptor.ts";
#UseInterceptors(new CompanyIdInterceptor(User))
export class UserController {
}
This works fine, but if a move the file to an external NPM package and import from it like this:
import { myInterceptor } from "mynpmpackage";
I get the following error:
[Nest] 24065 - 04/18/2019, 10:04 AM [ExceptionsHandler] Connection "default" was not found. +26114ms
ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (/home/andre/Services/npm-sdk/src/error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.get (/home/andre/Services/npm-sdk/src/connection/ConnectionManager.ts:40:19)
Any ideas, on what causes this and how to solve it?
This might not be your problem exactly, but I had a similar problem when moving things to external packages with TypeORM. Make sure all packages from parent project are using the same version of the TypeORM package.
In my case, using yarn why typeorm showed me two different versions were being installed. One of them was used to register the entities, while the framework connected to the SQL database using another version, generating this clash.
Check your versions using yarn why [pkg-name] or if you're using NPM, try npx npm-why [pkg-name] or install globally from https://www.npmjs.com/package/npm-why.
After verifying TypeOrm versions is same in both the packages i.e- external package and consumer repository as mentioned by #Luís Brito still issue persist then issue could be-
Basically when we create an external package - TypeORM tries to get the "default" connection option, but If not found then throws an error:
ConnectionNotFoundError: Connection "default" was not found.
We can solve this issue by doing some kind of sanity check before establishing a connection - luckily we have .has() method on getConnectionManager().
import { Connection, getConnectionManager, getConnectionOptions,
createConnection, getConnection, QueryRunner } from 'typeorm';
async init() {
let connection: Connection;
let queryRunner: QueryRunner;
if (!getConnectionManager().has('default')) {
const connectionOptions = await getConnectionOptions();
connection = await createConnection(connectionOptions);
} else {
connection = getConnection();
}
queryRunner = connection.createQueryRunner();
}
Above is a quick code-snippet which was the actual root cause for this issue but If you are interested to see complete working repositories (different example) -
External NPM Package :
Git Repo : git-unit-of-work (specific file- src/providers/typeorm/typeorm-uow.ts)
Published in NPM : npm-unit-of-work
Consumer of above package : nest-typeorm-postgre (specific files- package.json, src/countries/countries.service.ts & countries.module.ts)

ES6 import error handling

I am currently using Babel.
I did the following before with require:
try {
var myModule = require('my-module');
} catch (err) {
// send error to log file
}
However when trying to do this with import:
try {
import myModule from 'my-module';
} catch (err) {
// send error to log file
}
I get the error:
'import' and 'export' may only appear at the top level
Now I understand that import is different to require. From reading Are ES6 module imports hoisted? import hoists which means the imports are loaded before code execution.
What I did before was that if any requires failed a log was created which alerted me via email (sending logs to logstash etc.). So my question boils down to the following.
How does one handle import errors in a good practice fashion in nodejs? Does such a thing exist?
You can't catch static imports errors (cf. Boris' answer)
Yet, you could use a dynamic import() for that.
It's now supported by all evergreen browsers & Node, and is part of the standards since ES2020.
class ImportError extends Error {}
const loadModule = async (modulePath) => {
try {
return await import(modulePath)
} catch (e) {
throw new ImportError(`Unable to import module ${modulePath}`)
}
}
[2021 Edit] Look at Caveman answer for a more up to date answer allowing to make dynamic import
This talk give it away : https://github.com/ModuleLoader/es-module-loader/issues/280 and agree with what you said.
import only works at the base level. They are static and always load
before the module is run.
So you can't do a code check.
But, the good news is that as it's static, it can be analysed, tools like webpack throw errors at build time.
Supplementary dynamic import.
class ImportError extends Error {}
const loadModule = async (modulePath) => {
try {
return await import(modulePath)
} catch (e) {
throw new ImportError(`Unable to import module ${modulePath}`)
}
}
async function main() {
// import myDefault, {foo, bar} from '/modules/my-module.js'
const { default: myDefault, foo, bar } = await loadModule('/modules/my-module.js')
}
or chained_promises
import("/modules/my-module.js").then(module=>{
module.foo()
module.bar()
}).catch(err=>
console.log(err.message)
)
or Destructuring assignment
import("/modules/my-module.js").then(({foo, bar})=>{
foo()
bar()
}).catch(err=>
console.log(err.message)
)
A very modern answer to this now since cloud services is becoming the norm is to let the import fail and log to stderr as cloud services logs from stderr to their logging service. So basically you don't need to do anything.

Resources