Cannot read property 'resolve' of undefined when using import path from 'path' - node.js

When using :
import path from 'path';
path.resolve('/')
I get the title error, but when I use
require('path').resolve('messages.json'))
import { readFile, writeFile } from 'fs/promises';
import { v4 as uuidv4 } from 'uuid';
import path from 'path';
interface MessagesJson {
messages: Array<{ content: string; id: string }>;
}
export class MessagesRepository {
async findOne(id: string): Promise<string> {
return id;
}
async findAll(): Promise<any> {
return null;
}
async create(message: any): Promise<any> {
console.log(' dirname', require('path').resolve('messages.json'));
console.log(' path.resolve', path.resolve('/'));
// console.log(' path.resolve', path.resolve(__dirname, '/src'));
const messages: any = await readFile('src/messages.json', 'utf-8');
const parsedMessages: MessagesJson = JSON.parse(messages);
const newMessage = {
content: message,
id: uuidv4(),
};
await parsedMessages.messages.push(newMessage);
await writeFile('src/messages.json', JSON.stringify(parsedMessages));
return parsedMessages;
}
}
For the problem context, I'm working on a small project with nestjs, any option for path.foo() gets the same error as listed above, is it something related for after the compiling of the code?
Im very lost to where/what doc and/or information should i be reading to be able to understand what is happening.

require on it's own is, by technicality, a named import. This means that in Typescript it needs to be like import * as path from 'path' so that you can make use of path.resolve. Another option would be to deconstruct the import by using import { resolve } from 'path'; and now you can just call resolve() directly.

Related

how to prevent file upload when body validation fails in nestjs

I have the multipart form to be validated before file upload in nestjs application. the thing is that I don't want the file to be uploaded if validation of body fails.
here is how I wrote the code for.
// User controller method for create user with upload image
#Post()
#UseInterceptors(FileInterceptor('image'))
create(
#Body() userInput: CreateUserDto,
#UploadedFile(
new ParseFilePipe({
validators: [
// some validator here
]
})
) image: Express.Multer.File,
) {
return this.userService.create({ ...userInput, image: image.path });
}
Tried so many ways to turn around this issue, but didn't reach to any solution
Interceptors run before pipes do, so there's no way to make the saving of the file not happen unless you manage that yourself in your service. However, another option could be a custom exception filter that unlinks the file on error so that you don't have to worry about it post-upload
This is how I created the whole filter
import { isArray } from 'lodash';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
BadRequestException,
} from '#nestjs/common';
import { Request, Response } from 'express';
import * as fs from 'fs';
#Catch(BadRequestException)
export class DeleteFileOnErrorFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const getFiles = (files: Express.Multer.File[] | unknown | undefined) => {
if (!files) return [];
if (isArray(files)) return files;
return Object.values(files);
};
const filePaths = getFiles(request.files);
for (const file of filePaths) {
fs.unlink(file.path, (err) => {
if (err) {
console.error(err);
return err;
}
});
}
response.status(status).json(exception.getResponse());
}
}

How to import fs on the server side

I'm trying to send an ssr page containing the contents of a txt file, however I'm stuck on the part of being able to import the fs.
I understand that I need to be on the server side to be able to import the fs, I tried to use this code, without success
// pages/index.js
import fs from 'fs'
export async function getServerSideProps(ctx) {
return {
props: {}
}
}
export default function Home() {
return (
<h1>...</h1>
)
}
You can use import() or require() syntax inside getServerProps.
// pages/index.js
export async function getServerSideProps(ctx) {
const fs = await import('fs');
// or
const fs = require('fs');
return {
props: {},
};
}
export default function Home() {
return <h1>...</h1>;
}

NodeJs Script that compiles scss files fails because of postcss rule for undefined variables

I am using scss-bundle to import an scss file and resolve all his #import statements to later save it again as scss file.
This works fine and below is an example to see how it works:
scss-bundle.ts
import { Bundler } from 'scss-bundle';
import { relative } from 'path';
import { writeFile } from 'fs-extra';
/** Bundles all SCSS files into a single file */
async function bundleScss(input, output) {
const {found, bundledContent, imports} = await new Bundler()
.Bundle(input, ['./src/styles/**/*.scss']);
if (imports) {
const cwd = process.cwd();
const filesNotFound = imports
.filter((x) => !x.found)
.map((x) => relative(cwd, x.filePath));
if (filesNotFound.length) {
console.error(`SCSS imports failed \n\n${filesNotFound.join('\n - ')}\n`);
throw new Error('One or more SCSS imports failed');
}
}
if (found) {
await writeFile(output, bundledContent);
}
}
bundleScss('./src/styles/file-to-import.scss', './src/styles/imported-file.scss');
Where file-to-import.scss is the following file:
#import './file-to-import-1';
#import './file-to-import-2';
And file-to-import-1.scss and file-to-import-2.scss are the following files:
file-to-import-1.scss
.price-range {
background-color: $range-header-background-1;
}
file-to-import-2.scss
.qr-code {
background-color: $range-header-background-2;
}
The result of executing the script is:
imported-file.scss:
.price-range {
background-color: $range-header-background-1;
}
.qr-code {
background-color: $range-header-background-2;
}
Until this everything is working well.
Now ... I want to use postcss-css-modules in order to hash the names of the classes, the result should be something like this:
imported-file.scss after being hashed
._3BQkZ {
background-color: $range-header-background-1;
}
.Xb2EV {
background-color: $range-header-background-2;
}
I have already achieved that but only if I define the variables $range-header-background-1 and $range-header-background-2.
However, I can not define the variables yet because I need to defined them on run time as query params of an Http request.
If I run the script without defining the variables the following error is display:
(node:1972) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): CssSyntaxError: <css input>:372:14: Unknown word
(node:1972) [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.
Here is the scss-budle.ts with postcss-css-modules call:
import { Bundler } from 'scss-bundle';
import { relative } from 'path';
import * as path from 'path';
import { writeFile } from 'fs-extra';
import * as postcssModules from 'postcss-modules';
import * as postcss from 'postcss';
import * as fs from 'fs';
/** Bundles all SCSS files into a single file */
async function bundleScss(input, output) {
const {found, bundledContent, imports} = await new Bundler()
.Bundle(input, ['./src/styles/**/*.scss']);
if (imports) {
const cwd = process.cwd();
const filesNotFound = imports
.filter((x) => !x.found)
.map((x) => relative(cwd, x.filePath));
if (filesNotFound.length) {
console.error(`SCSS imports failed \n\n${filesNotFound.join('\n - ')}\n`);
throw new Error('One or more SCSS imports failed');
}
}
if (found) {
await writeFile(output, bundledContent);
const hashedResult = await postcss().use(postcssModules({
generateScopedName: '[hash:base64:5]',
getJSON(cssFileName: any, json: any, outputFileName: any) {
let jsonFileName = path.resolve('./src/styles/imported-file.json');
fs.writeFileSync(jsonFileName, JSON.stringify(json));
}
})).process(bundledContent);
await writeFile(output.replace('.scss', '-hashed.scss'), hashedResult.css, 'utf8');
return;
}
}
bundleScss('./src/styles/file-to-import.scss', './src/styles/imported-file.scss');
Does anybody know how to continue executing postcss-css-modules without stopping because the scss variables are not defined?
Thanks in advance.
I was able to run the script successfully using postcss-scss as parser of postcss:
import * as postcssScss from 'postcss-scss';
...
const hashedResult = await postcss([
postcssModules({
generateScopedName: '[hash:base64:8]',
getJSON(cssFileName: any, json: any, outputFileName: any) {
let jsonFileName = path.resolve('./src/styles/imported-file.json');
fs.writeFileSync(jsonFileName, JSON.stringify(json));
}
})
]).process(bundledContent, { parser: postcssScss});
Below, I leave the script complete:
scss-bundle.ts
import { Bundler } from 'scss-bundle';
import { relative } from 'path';
import * as path from 'path';
import { writeFile } from 'fs-extra';
import * as postcssModules from 'postcss-modules';
import * as postcss from 'postcss';
import * as fs from 'fs';
import * as postcssScss from 'postcss-scss';
/** Bundles all SCSS files into a single file */
async function bundleScss(input, output) {
const {found, bundledContent, imports} = await new Bundler()
.Bundle(input, ['./src/styles/**/*.scss']);
if (imports) {
const cwd = process.cwd();
const filesNotFound = imports
.filter((x) => !x.found)
.map((x) => relative(cwd, x.filePath));
if (filesNotFound.length) {
console.error(`SCSS imports failed \n\n${filesNotFound.join('\n - ')}\n`);
throw new Error('One or more SCSS imports failed');
}
}
if (found) {
await writeFile(output, bundledContent);
const hashedResult = await postcss([
postcssModules({
generateScopedName: '[hash:base64:8]',
getJSON(cssFileName: any, json: any, outputFileName: any) {
let jsonFileName = path.resolve('./src/styles/imported-file.json');
fs.writeFileSync(jsonFileName, JSON.stringify(json));
}
})
]).process(bundledContent, { parser: postcssScss});
await writeFile(output.replace('.scss', '-hashed.scss'), hashedResult.css, 'utf8');
return;
}
}
bundleScss('./src/styles/file-to-import.scss', './src/styles/imported-file.scss');

NestJS upload using GraphQL [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Is anyone has an example of how to upload a file in NestJs using GraphQl?
I can upload using given example via controller
https://github.com/nestjs/nest/issues/262#issuecomment-366098589,
but I couldn't find any comprehensive documentation how to upload using GrahpQL in NestJS
Apollo Server 2.0 should be able to do this now (packaged in nest), although I needed to install graphql-upload and import GraphQLUpload as I couldn't find the Upload type:
#Mutation(() => Image, { nullable: true })
async addImage(#Args({ name: 'image', type: () => GraphQLUpload }) image) {
// Do stuff with image...
}
At the time of this answer FileInterceptor is using multer and by converting ExecutionContext to http it uses getRequest and getResponse methods to provide req and res to multer.single which they are (req and res) undefined in GraphQL.
I have tried to get request from context using:
const ctx = GqlExecutionContext.create(context);
and there is req property in ctx but I can't find a way to use multer (yet).
Anyway, I made some changes to FileFieldsInterceptor to use it inside my project, but I may make pull request when I had time to clean it up:
import { Observable } from 'rxjs';
import {
NestInterceptor,
Optional,
ExecutionContext,
mixin,
} from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { storeFile } from './storeFile';
interface IField {
name: string;
options?: any;
}
export function GraphqlFileFieldsInterceptor(
uploadFields: IField[],
localOptions?: any,
) {
class MixinInterceptor implements NestInterceptor {
options: any = {};
constructor(#Optional() options: any = {}) {
this.options = { ...options, ...localOptions };
}
async intercept(
context: ExecutionContext,
call$: Observable<any>,
): Promise<Observable<any>> {
const ctx = GqlExecutionContext.create(context);
const args = ctx.getArgs();
let storeFilesResult = await Promise.all(
uploadFields.map(uploadField => {
const file = args[uploadField.name];
return storeFile(file, {
...uploadField.options,
...this.options,
}).then(address => {
args[uploadField.name] = address;
return address;
});
}),
);
return call$;
}
}
const Interceptor = mixin(MixinInterceptor);
return Interceptor;
}
and store file is something like this (may not be used like this):
import uuid from 'uuid/v4';
import fs from 'fs';
import path from 'path';
const dir = './files';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
export const storeFile = async (file, options): Promise<any> => {
// options is not doing anything right now
const { stream } = await file;
const filename = uuid();
const fileAddress = path.join(dir, filename + '.jpg');
return new Promise((resolve, reject) =>
stream
.on('error', error => {
if (stream.truncated)
// Delete the truncated file
fs.unlinkSync(fileAddress);
reject(error);
})
.pipe(fs.createWriteStream(fileAddress))
.on('error', error => reject(error))
.on('finish', () => resolve(fileAddress)),
);
};
In my Cats.resolvers.ts:
...
#Mutation()
#UseInterceptors(
GraphqlFileFieldsInterceptor([
{ name: 'catImage1' },
{ name: 'catImage2' },
{ name: 'catImage3' },
]),
)
async cats(
#Args('catImage1') catImage1: string,
#Args('catImage2') catImage2: string,
#Args('catImage3') catImage3: string,
){
console.log(catImage1) // will print catImage1 address
...
This implementation works perfectly with Node >= v14
package.json
Remove the fs-capacitor and graphql-upload entries from the resolutions section if you added them, and install the latest version of graphql-upload (v11.0.0 at this time) package as a dependency.
src/app.module.ts
Disable Apollo Server's built-in upload handling and add the graphqlUploadExpress middleware to your application.
import { graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "#nestjs/common"
#Module({
imports: [
GraphQLModule.forRoot({
uploads: false, // disable built-in upload handling
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
}
}
src/blog/post.resolver.ts (example resolver)
Remove the GraphQLUpload import from apollo-server-core and import from graphql-upload instead
import { FileUpload, GraphQLUpload } from "graphql-upload"
#Mutation(() => Post)
async postCreate(
#Args("title") title: string,
#Args("body") body: string,
#Args("attachment", { type: () => GraphQLUpload }) attachment: Promise<FileUpload>,
) {
const { filename, mimetype, encoding, createReadStream } = await attachment
console.log("attachment:", filename, mimetype, encoding)
const stream = createReadStream()
stream.on("data", (chunk: Buffer) => /* do stuff with data here */)
}
Source: https://github.com/nestjs/graphql/issues/901#issuecomment-780007582
Some other links that I found helpful:
https://stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload/
For uploading files using postman Link
EDIT: As per Developia comment below, apollo-server now implements file upload. Should be preferred way.
Below, original answer, for reference.
One normally does not use GraphQL for upload. GraphQL is fancy "specification of API", meaning that in the end of the day, low level HTTP request and responses are translated to/from JSON objects (if you don't have custom transport).
One solution could be to define special endpoint in GraphQL schema like:
mutation Mutation {
uploadFile(base64: String): Int
}
Then client would convert binary data to base64 string, which would be handled accordingly on resolver side. This way, file will become part of JSON object exchanged between GraphQL client and server.
While this is might be suitable for small files, small number of operations, it is definitely not a solution for upload service.
try this
import { Resolver, Mutation, Args } from '#nestjs/graphql';
import { createWriteStream } from 'fs';
import {GraphQLUpload} from "apollo-server-express"
#Resolver('Download')
export class DownloadResolver {
#Mutation(() => Boolean)
async uploadFile(#Args({name: 'file', type: () => GraphQLUpload})
{
createReadStream,
filename
}): Promise<boolean> {
return new Promise(async (resolve, reject) =>
createReadStream()
.pipe(createWriteStream(`./uploads/${filename}`))
.on('finish', () => resolve(true))
.on('error', () => reject(false))
);
}
}
You could use the apollo-upload-server lib. Seems like the easiest thing to do, in my opinion. Cheers
You need to define an upload controller and add it in your app.module, this is an example of what a controller should be (back-end):
#Controller()
export class Uploader {
#Post('sampleName')
#UseInterceptors(FileInterceptor('file'))
uploadFile(#UploadedFile() file) {
// file name selection
const path = `desired path`;
const writeStream = fs.createWriteStream(path);
writeStream.write(file.buffer);
writeStream.end();
return {
result: [res],
};
}
}
And call your controller by fetch in the front-end:
fetch('controller address', {
method: 'POST',
body: data,
})
.then((response) => response.json())
.then((success) => {
// What to do when succeed
});
})
.catch((error) => console.log('Error in uploading file: ', error));

How to use connection as standalone object with types?

Not working code just to illustrate what I'm trying to achieve
Some connection file
import { ConnectionManager } from 'typeorm';
const c = new ConnectionManager();
// user ormconfig.conf file
export const connection = c.createAndConnect();
using in some model
#Entity()
#Table("annual_incomes")
export class AnnualIncome
{
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 75 })
variant: string;
#Column("int")
sort: number;
#Column()
is_active: boolean;
}
Later somewhere in the code, I want to get connection with all methods, something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.getRepository(AnnualIncome).find();
});
Usually, I'm getting an error from tsc that .getRepository() method was not found in connection. However if I do something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.then(async connection => {
return await connection.getRepository(AnnualIncome).find();
}
});
the above code works with definitions and tsc does not complain about not-existing methods.
I'd like to avoid an extra definition connection.then() and get plain connection with all methods defined in <Connection> type.
just use createConnection method to create your connection when you bootstrap your application. Later you can access your connection from anywhere using getConnection() method:
import { AnnualIncome } from 'entities';
import { createConnection, getConnection } from 'typeorm';
// somewhere in your app, better where you bootstrap express and other things
createConnection(); // read config from ormconfig.json or pass them here
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await getConnection().getRepository(AnnualIncome).find();
});
Also you can simply use getRepository method also avalible from anywhere:
import { AnnualIncome } from 'entities';
import { getRepository } from 'typeorm';
// some code here
api.get('/incomes', async (ctx) => {
ctx.body = await getRepository(AnnualIncome).find();
});

Resources