Use fileSystem on an API Route with NextJS deployed on Vercel - node.js

I need to use fileStystem methods as readdirSync on an API Route in NextJS. It works locally but when deployed on Vercel, the request responds with a 500 status code.
This is Vercel's Funcion Logs:
catch Error: ENOENT: no such file or directory, scandir '/var/task/_posts'
at Object.readdirSync (fs.js:1043:3)
at getAllArticles (/var/task/.next/server/chunks/230.js:135:67)
at __WEBPACK_DEFAULT_EXPORT__ (/var/task/.next/server/pages/api/search.js:37:93)
at Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils.js:101:15)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Server.handleApiRequest (/var/task/node_modules/next/dist/server/next-server.js:770:9)
at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:661:37)
at async Router.execute (/var/task/node_modules/next/dist/server/router.js:205:32)
at async Server.run (/var/task/node_modules/next/dist/server/next-server.js:841:29)
at async Server.handleRequest (/var/task/node_modules/next/dist/server/next-server.js:292:20) {
errno: -2,
syscall: 'scandir',
code: 'ENOENT',
path: '/var/task/_posts'
}
This is the API Route itself (/pages/api/search.ts):
export default (req: NextApiRequest, res: NextApiResponse) => {
const { query } = req;
try {
if (query.q) {
const posts = getAllArticles(ArticleTypes.POSTS, ['slug', 'href', 'title', 'category'], {
hints: {
hint: query.q as string,
fields: ['title', 'category'],
},
}).map((post) => post.data);
const projects = getAllArticles(ArticleTypes.PROJECTS, ['slug', 'href', 'title', 'category', 'technologies'], {
hints: {
hint: query.q as string,
fields: ['title', 'category', 'technologies'],
},
}).map((project) => project.data);
res.status(200).json({
...(posts.length > 0) && { posts },
...(projects.length > 0) && { projects },
});
} else {
res.status(422).send('El parámetro "q" es necesario.');
}
} catch (e) {
res.status(500).send('Ha ocurrido un error mientras se intentaba hacer la búsqueda.');
}
};
And this is the getAllArticles() function used in it:
export const getAllArticles = (
type: ArticleTypes,
items?: string[],
filters?: IFilters,
): IArticle[] => {
const articlesPath = join(process.cwd(), `_${type}`);
const articlesNames = fs
.readdirSync(articlesPath)
.filter((path) => /\.mdx$/.test(path));
const mergedItems: string[] = Array.from(new Set([
...(items && items.length > 0) ? items : [],
...(filters?.hints && filters.hints.fields.length > 0) ? filters.hints.fields : [],
]));
const allArticles = articlesNames
.map((article) => getArticle(type,
path.parse(article).name,
mergedItems.length > 0
? mergedItems
: undefined));
const filteredArticles = filterArticles(type, allArticles, filters);
return filteredArticles;
};
So I am currently using fs.readdirSync for reading .mdx files. As I noted before, this runs perfectly well locally but not when deployed on Vercel. Do I nees to configure any kind of config files or anything?
Thank you so much!

Related

Discord.js | interaction.reply is not a function [SlashCommand][Typescript]

I am making a /ping command in my bot, following the discordjs guide, but when I use the command, I get an error:
TypeError: interaction.reply is not a function
at Object.<anonymous> (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:8:15)
at Generator.next (<anonymous>)
at C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:8:71
at new Promise (<anonymous>)
at __awaiter (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:4:12)
at Object.execute (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:18:16)
at C:\Users\timda\code\discord bot\main.ts:43:18
at Generator.next (<anonymous>)
at C:\Users\timda\code\discord bot\main.ts:8:71
at new Promise (<anonymous>)
This is the /ping commands code:
import { SlashCommandBuilder } from 'discord.js';
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction: { reply: (arg0: string) => any }) {
console.log(interaction);
interaction.reply('Pong!');
},
};
And here is the code in my main file that loads in the SlashCommand files:
import fs from 'node:fs';
import path from 'node:path';
import { Client, Collection, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
import { clientId, token } from './config.json';
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent],
partials: [Partials.Message, Partials.Channel],
presence: { status: 'online', activities: [{ name: 'you 👀', type: 3 }] }
});
(client as any).commands = new Collection();
const commands: any[] = [];
for (const file of (fs.readdirSync(path.join('./bot-data/commands')).filter(file => file.endsWith('.ts')))) {
const command = require(`./bot-data/commands/${file}`);
(client as any).commands.set(command.data.name, command);
commands.push(command.data.toJSON());
};
client.on('ready', async (client: { user: { tag: any; }; }) => {
const rest = new REST({ version: '10' }).setToken(token);
try {
await rest.put(Routes.applicationCommands(clientId), { body: commands});
} catch (error) {
console.error(error);
}
console.log(`Succesfully logged in as ${client.user.tag}!`);
});
client.on('interactionCreate', async interaction => {
if(interaction.isCommand()) {
const command = (client as any).commands.get(interaction.commandName);
if (!command) {
await interaction.reply({content: `Something went wrong while executing ${interaction.commandName}.`, ephemeral: true});
return;
}
try {
await command.execute(client, interaction);
} catch (error) {
await interaction.reply({content: `Something went wrong while executing ${interaction.commandName}., ephemeral: true});
console.error(error);
}
}
});
The command does get loaded when /ping is used, because it does log the interaction in the console.
I am writing the bot in Typescript, that's why I did interaction: { reply: (arg0: string) => any }
and I'm using node to compile and run it.
NVM: version 1.1.10
NPM: version 8.19.2
I've tried writing interaction: { reply: (arg0: string) => any } in different ways and I've also already done a lot of googling, but I can't find the problem I'm facing here.
If someone could help me I would appreciate it.
Inside of main.ts, you're calling the execute function with these arguments:
await command.execute(client, interaction);
But inside of ping.ts, you're only accepting these arguments:
async execute(interaction: { reply: (arg0: string) => any }) { // etc
You need to include both client and interaction as arguments in ping.ts to prevent the values being passed into the wrong variables.
// I'm fairly certain that these are the right imports
import { BaseInteraction, SlashCommandBuilder, Client} from 'discord.js';
...
async execute(client: Client, interaction: BaseInteraction) { // etc

next-translate Cannot find module './locales/en/common.json'

I am trying to integrate the next-translate library into my Next app, however I am getting an error when configuring the loadLocaleFrom function
This is what my i18n.js file looks like:
module.exports = {
locales: ["en", "fr", "es", "ru", "ar", "zh", "hi", "sw"],
defaultLocale: "en",
pages: {
"*": ["common"],
"/": ["home"],
},
loadLocaleFrom: async (lang, ns) => {
try {
const m = await import(`./locales/${lang}/${ns}.json`);
return {
...m.default,
};
} catch (error) {
console.log(error);
}
},
};
And my next.config file:
const withPlugins = require("next-compose-plugins");
const withImages = require("next-images");
const nextTranslate = require("next-translate");
module.exports = withPlugins([withImages, nextTranslate], {
reactStrictMode: true,
images: {
disableStaticImages: true,
},
});
package.json version:
next-translate: ^1.5.0
next: 12.1.6
react: 17.0.2
Even though my directory has both, the common.json and the home.json files in the correct folder structure, the loadLocaleFrom function still throws an error that looks like this:
Error: Cannot find module './locales/en/common.json'
at webpackEmptyContext (D:\projects\mk\mk-academy\.next\server\pages\index.js:529:10)
at eval (webpack-internal:///./i18n.js:64:89)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Object.loadLocaleFrom (webpack-internal:///./i18n.js:63:23)
at async Promise.all (index 0) {
code: 'MODULE_NOT_FOUND'
}
I did try using the appWithI18n hoc in _app.js but that doesn't solve it too. I also did try moving the locales to a different directory under src but that shouldn't make a difference.
The image shows my directory structure
require worked for me instead of import
await require(`./locales/${lang}/${ns}.json`)

Jest Test suite failed to run - AssertionError [ERR_ASSERTION]: opts.entryPoint required

We had a test running successfully for our code that uses the notifications-node-client to send emails, eg
import { NotifyClient } from 'notifications-node-client';
import notify from './notify';
import config from '../../../config';
jest.mock('fs');
jest.mock('notifications-node-client');
describe('notify', () => {
const EMAIL = 'user#test.com';
const REGION_GB = 'gb';
const REGION_NI = 'ni';
const feedbackOptions = {
personalisation: {
satisfactionLevel: 'satisfied',
improvements: 'improvements',
},
};
test('for original GB submission sendEmail should be called and true returned', async () => {
NotifyClient.prototype.sendEmail.mockReturnValue({
body: {
id: 1,
},
});
expect(await notify(EMAIL, 'submissionSuccess', REGION_GB)).toBeTruthy();
expect(NotifyClient.prototype.sendEmail).toHaveBeenCalledWith(
config.notify.templates.submissionSuccess.gb,
EMAIL,
undefined,
);
});
...
Having swapped out our Winston logging solution to use #dwp/node-logger, the 'notify' tests now do not run, failing with
● Test suite failed to run
AssertionError [ERR_ASSERTION]: opts.entryPoint required
at new Thrift (node_modules/thriftrw/thrift.js:64:5)
at Object.<anonymous> (node_modules/jaeger-client/src/thrift.js:21:20)
at Object.<anonymous> (node_modules/jaeger-client/src/reporters/remote_reporter.js:15:1)
All of the other test suites in the project still run successfully.
Could anyone point me in the right direction about what change to make?
The code that we're testing is
import { NotifyClient } from 'notifications-node-client';
import config from '../../../config/index';
import { RegionKeys } from '../../../config/types';
import logger from '../../../xxxx/lib/logger';
type TemplateId = 'submissionSuccess' | 'supportingEvidence' | 'feedbackTemplateId';
type FeedbackOptions = {
personalisation: {
satisfactionLevel: string,
improvements: string,
},
reference?: string,
}
const { apiKey, templates, proxy } = config.notify;
export default async function notify(
email: string,
templateId: TemplateId,
region: RegionKeys,
options?: FeedbackOptions,
): Promise<boolean> {
let notifyTemplate;
let notifyApiKey;
let notifyOptions;
try {
if (templateId === 'feedbackTemplateId' && !options) {
throw new Error(`Unable to send email - mismatch between template ID (${templateId}) and options supplied`);
} else if (options && templateId !== 'feedbackTemplateId') {
notifyOptions = {};
} else {
notifyOptions = options;
}
const notifyClient = new NotifyClient(apiKey[region]);
notifyClient.setProxy(proxy);
notifyTemplate = templateId === 'feedbackTemplateId' ? templates.feedbackTemplateId : templates[templateId][region];
logger.debug(`apiKey: ${notifyApiKey}`);
logger.debug(`notify template Id: ${notifyTemplate}`);
logger.debug(`proxy: ${JSON.stringify(proxy)}`);
const response = await notifyClient.sendEmail(notifyTemplate, email, notifyOptions);
if (response.body) {
logger.info(`confirmation email sent to ${email} and relates to message id ${response.body.id}`);
}
return true;
} catch (err: any) {
logger.error(`there was an error sending the message: ${err.message}`);
return false;
}
}

NestJS Error: Error:Cannot find module './'

I encountered a error when using NestJS. The console shows 0 error first and then crahsed with the Error: Cannot find module './'. This is similar to the
Error:Cannot find module '../module_name'.
However, for this one, it shows './'. And I tried to delete the node_module and the dist folder and rerun the npm install that was usually used to solve the similar people.
The full error message is:
[6:32:11 PM] Starting compilation in watch mode...
[6:32:14 PM] Found 0 errors. Watching for file changes.
Error: Cannot find module './'
Require stack:
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.service.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.controller.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.module.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\app.module.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\main.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (C:\Users\Vibrant\Desktop\inventory_demo\src\client\client.service.ts:3:1)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
The client.service.js:
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientService = void 0;
const common_1 = require("#nestjs/common");
const client_1 = require("./");
let ClientService = class ClientService {
async client(clientWhereUniqueInput) {
return client_1.default.client.findUnique({
where: clientWhereUniqueInput,
});
}
async clients(params) {
const { skip, take, cursor, where } = params;
return client_1.default.client.findMany({
skip,
take,
cursor,
where,
});
}
async createClient(data) {
return client_1.default.client.create({ data });
}
async deleteClient(where) {
return client_1.default.client.delete({ where });
}
async getClientByID(id) {
return await client_1.default.client.findUnique({
where: { client_id: Number(id) },
});
}
async updateClient(params) {
const { where, data } = params;
return client_1.default.client.update({ where, data });
}
};
ClientService = __decorate([
(0, common_1.Injectable)()
], ClientService);
exports.ClientService = ClientService;
//# sourceMappingURL=client.service.js.map
I have two ts files has client in the name:
First one is called client.ts which is a prism database client:
import { PrismaClient } from '#prisma/client';
interface CustomNodeJsGlobal extends NodeJS.Global {
prisma: PrismaClient;
}
// Prevent multiple instances of Prisma Client in development
declare const global: CustomNodeJsGlobal;
const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV === 'development') global.prisma = prisma;
export default prisma;
The second one is a model called client, it only imports the basic nestjs modules I think.
The client module.ts
import { Module } from '#nestjs/common';
import { ClientController } from './client.controller';
import { ClientService } from './client.service';
#Module({
controllers: [ClientController],
providers: [ClientService]
})
export class ClientModule {}
The client controller(haven't start it yet):
import { Controller } from '#nestjs/common';
#Controller('client')
export class ClientController {}
and the client service:
import { Injectable } from '#nestjs/common';
import { Client, Prisma } from '#prisma/client';
import prisma from 'src/client';
#Injectable()
export class ClientService {
// The service to return a single client
async client(
clientWhereUniqueInput: Prisma.ClientWhereUniqueInput,
): Promise<Client> {
return prisma.client.findUnique({
where: clientWhereUniqueInput,
});
}
// The service to return a list of clients
async clients(params: {
skip?: number;
take?: number;
cursor?: Prisma.ClientWhereUniqueInput;
where?: Prisma.ClientWhereInput;
}): Promise<Client[]> {
const { skip, take, cursor, where } = params;
return prisma.client.findMany({
skip,
take,
cursor,
where,
});
}
// The service to create a client
async createClient(data: Prisma.ClientCreateInput): Promise<Client> {
return prisma.client.create({ data });
}
// The service to delete a client
async deleteClient(where: Prisma.ClientWhereUniqueInput): Promise<Client> {
return prisma.client.delete({ where });
}
// The service to find an client by id
async getClientByID(id: string) {
return await prisma.client.findUnique({
where: { client_id: Number(id) },
});
}
// The service to update a client
async updateClient(params: {
where: Prisma.ClientWhereUniqueInput;
data: Prisma.ClientUpdateInput;
}): Promise<Client> {
const { where, data } = params;
return prisma.client.update({ where, data });
}
// End of class
}
The problem is I used a model called client.
The solution is to rename it to clients since NestJS itself uses the name client. After the name modification, I will need to delete the whole dist folder and run npm rebuild.

Why Hook is called in all update services methods

I'm create a hook file with the following information, which is Hooks.js
Hooks.js is working to authenticate an actions with JWT when need it, I dont need it in all servies calls.
As my understanding the syntax to call a hook was app/use route/hooks and those hooks were only applied to and specific route and not globally.
module.exports = {
errorHandler: (context) => {
if (context.error) {
context.error.stack = null;
return context;
}
},
isValidToken: (context) => {
const token = context.params.headers.authorization;
const payload = Auth.validateToken(token);
console.log(payload);
if(payload !== "Invalid" && payload !== "No Token Provided"){
context.data = payload._id;
}
else {
throw new errors.NotAuthenticated('Authentication Error Token');
}
},
isValidDomain: (context) => {
if (
config.DOMAINS_WHITE_LIST.includes(
context.params.headers.origin || context.params.headers.host
)
) {
return context;
}
throw new errors.NotAuthenticated("Not Authenticated Domain");
},
normalizedId: (context) => {
context.id = context.id || context.params.route.id;
},
normalizedCode: (context) => {
context.id = context.params.route.code;
},
};
Then I create a file for services and routes, like the following:
const Hooks = require("../../Hooks/Hooks");
const userServices = require("./user.services");
module.exports = (app) => {
app
.use("/users", {
find: userServices.find,
create: userServices.createUser,
})
.hooks({
before: {
find: [Hooks.isValidDomain],
create: [Hooks.isValidDomain],
},
});
app
.use("/users/:code/validate", {
update: userServices.validateCode,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.normalizedCode],
},
});
app
.use("/users/personal", {
update: userServices.personalInfo,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.isValidToken],
},
});
};
Why Hooks.isValidToken applies to all my update methods? Even if I'm not calling it?
Please help.
app.hooks registers an application level hook which runs for all services. If you only want it for a specific service and method it needs to be app.service('users').hooks().

Resources