NextJS: TypeError: Cannot read property 'headers' of undefined - node.js

I am receiving this error in getInitialProps function attached to my page:
const getInitialProps = async ({ req, query }: NextPageContext) => {
let isValidReferenceQuery = true;
const { path } = absoluteUrl(req as IncomingMessage);
try {
await policyService.getPolicyByReference(
path,
query.referenceQuery as string
);
} catch (error) {
isValidReferenceQuery = false;
}
return {
referenceQuery: (query.referenceQuery as string) ?? null,
isValidReferenceQuery,
};
};
Page.getInitialProps = getInitialProps;
The function absoluteUrl determines if the API call was made server-side or client-side and adjusts the path accordingly:
export default function absoluteUrl(
req: IncomingMessage
): Record<string, string> {
const protocol = req.headers.referer?.split('//')[0] ?? 'http:';
const host = req
? req.headers['x-forwarded-host'] || req.headers.host
: window.location.host;
const path =
host === 'localhost:3000'
? `${protocol}//localhost:8080`
: `${protocol}//${host}${axios.defaults.baseURL}`;
return {
path,
};
}
I have updated my _document.tsx & _app.tsx to include getInitialProps as described to opt-out of automatic static generation in development mode so req should be defined. We also need all pages to be SSR for localisation:
import React from 'react';
import App, { AppContext } from 'next/app';
import { ReactQueryCacheProvider, QueryCache } from 'react-query';
import { Hydrate } from 'react-query/hydration';
import { appWithTranslation } from '#/localisation/i18n';
import { AppPageProps } from '#/models/page';
const TextMiningApp = ({ Component, pageProps }: AppPageProps) => {
const queryCache = new QueryCache();
const Layout = Component.Layout ? Component.Layout : React.Fragment;
return (
<ReactQueryCacheProvider queryCache={queryCache}>
<Hydrate state={pageProps.dehydratedState}>
<Layout>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<Component {...pageProps} />
</Layout>
</Hydrate>
</ReactQueryCacheProvider>
);
};
TextMiningApp.getInitialProps = async (appContext: AppContext) => ({
...(await App.getInitialProps(appContext)),
});
export default appWithTranslation(TextMiningApp);
I cannot see why if i have opted out of automatic static generation that req is undefined?

Not sure if this is still needed, but currently getInitialProps in _app.js actually works a little differently than pages. Instead of directly passing context as an argument, it passes an object with ctx and Component properties. So you would need to do something like this:
TextMiningApp.getInitialProps = async( { ctx, Component } ) => {
// This should be working now.
console.log( ctx.req );
}
One catch is that App.getInitialProps it needs to be passed all properties from the original argument:
App.getInitialProps( ctx, Component );
See https://github.com/vercel/next.js/discussions/14913

Related

Intercepting in Multer Mutates Request? (NestJS)

Does multer mutates any request that has given to it? I'm currently trying to intercept the request to add this in logs.
But whenever I try to execute this code first:
const newReq = cloneDeep(request); // lodash cloneDeep
const newRes = cloneDeep(response);
const postMulterRequest: any = await new Promise((resolve, reject) => {
const multerReponse = multer().any()
multerReponse(request, newRes, err => {
if (err) reject(err)
resolve(request)
})
})
files = postMulterRequest?.files;
The #UseInterceptors(FileInterceptor('file')) becomes undefined.
I have already seen the problem, it seems like the multerReponse(request, newRes, err => { mutates the request. But I don't know what the other approach I can do to fix this. (I tried JSON Serialization, Object.assign, cloneDeep, but none of those worked)
I have tried adding newReq and newRes (cloned object) to multerResponse at first it worked. But at the second time, the thread only hangs up, and doesn't proceed to next steps. Or the multerReponse(request, newRes, err => { doesn't return anything.
The whole code looks like this and used globally (some parts of here were redacted/removed; but the main logic is still the same) :
#Injectable()
export class AuditingInterceptor implements NestInterceptor {
constructor(
#InjectModel(Auditing.name)
private readonly AuditingModel: Model<Auditing>,
) {}
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
const { headers, method, ip, route, query, body } = request;
let bodyParam = Object.assign({}, body),
files: any;
const newReq = cloneDeep(request); // lodash cloneDeep
const newRes = cloneDeep(response);
const postMulterRequest: any = await new Promise((resolve, reject) => {
const multerReponse = multer().any();
multerReponse(newReq, newRes, (err) => {
if (err) reject(err);
resolve(newReq);
});
});
files = postMulterRequest?.files;
return next.handle().pipe(
tap(() =>
this.AuditingModel.create({
request: {
query,
bodyParam,
files,
},
timeAccessed: new Date().toISOString(),
}),
),
);
}
}
Summary of what I need to do here is I need to intercept and log the file in our DB before it gets processed in the method/endpoint that uses #UseInterceptors(FileInterceptor('file')).
I have solve this by intercepting the request using the
#Req() req
and creating a method to handle the files that was intercepted inside the FileInterceptor decorator.
Code Example:
// create logs service first to handle your queries
createLogs(file, req){
// do what you need to do with the file, and req here
const { filename } = file;
const { ip } = req
....
}
// main service
// inject the service first
constructor(#Inject(LogsService) private logsService: LogsService)
uploadHandler(file, req){
this.logsService.createLogs(file, req)
// proceed with the next steps
....
}
// controller
#Post('upload')
#UseInterceptors(FileInterceptor('file'))
testFunction(#UploadedFile() file: Express.Multer.File,, #Req req){
return this.serviceNameHere.uploadHandler(file, req);
}

RTK Query in Redux-Toolkit is returning data of undefined, when I console.log the data it appears in console

I was trying to display an array of data fetched from my custom server with RTK Query using Next.js (React framework). And this is my first time using RTK Query. Whenever I console.log the data, it appears in the browser console. But whenever I try to map the data to render it in the browser, it keeps throwing an error saying Cannot read properties of undefined (reading 'map').
I figured Next.js always throws an error if an initial state is undefined or null even if the state change. This link talked about solving the problem using useMemo hook https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics
But I didn't understand it well. Please kindly help me out with displaying the data.
Here is the BaseQuery function example I followed, it was derived from redux toolkit docmentation https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#axios-basequery
import axios from "axios";
const axiosBaseQuery =
({ baseUrl } = { baseUrl: "" }) =>
async ({ url, method, data }) => {
try {
const result = await axios({ url: baseUrl + url, method, data });
return { data: result.data };
} catch (axiosError) {
let err = axiosError;
return {
error: { status: err.response?.status, data: err.response?.data },
};
}
};
export default axiosBaseQuery;
I make the GET request here
import { createApi } from "#reduxjs/toolkit/query/react";
import axiosBaseQuery from "./axiosBaseQuery";
export const getAllCarsApi = createApi({
reducerPath: "getAllCarsApi",
baseQuery: axiosBaseQuery({
baseUrl: "http://localhost:5000/",
}),
endpoints(build) {
return {
getAllCars: build.query({
query: () => ({ url: "all-cars", method: "get" }),
}),
};
},
});
export const { useGetAllCarsQuery } = getAllCarsApi;
This is my redux store
import { configureStore } from "#reduxjs/toolkit";
import { getAllCarsApi } from "./getAllCarsApi";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
const store = configureStore({
reducer: { [getAllCarsApi.reducerPath]: getAllCarsApi.reducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(getAllCarsApi.middleware),
});
setupListeners(store.dispatch);
export default store;
I provide the store to the _app.js file.
import "../styles/globals.css";
import axios from "axios";
import { MyContextProvider } from "#/store/MyContext";
import { Provider } from "react-redux";
import store from "#/store/ReduxStore/index";
axios.defaults.withCredentials = true;
function MyApp({ Component, pageProps }) {
return (
<MyContextProvider>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</MyContextProvider>
);
}
export default MyApp;
I get the data here in my frontend.
import { useGetAllCarsQuery } from "#/store/ReduxStore/getAllCarsApi";
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
export default theTest;
This is a timing thing.
Your component will always render immediately and it will not defer rendering until data is there. That means it will also render before your data has been fetched. So while the data is still loading, data is undefined - and you try to map over that.
You could do things like just checking if data is there to deal with that:
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data && data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};

React and Easybase - Invalid hook call. Hooks can only be called inside of the body of a function component

I am trying to use React and Easybase (database). I'm having some issues however.
This is in the SolanaSignature.tsx file.
import { useWallet } from '#solana/wallet-adapter-react';
import bs58 from 'bs58';
import React, { FC, useCallback } from 'react';
import ReactDOM from 'react-dom';
import { sign } from 'tweetnacl';
import AddUser from './mainstorage';
export const SignMessageButton : FC = () => {
const { publicKey, signMessage } = useWallet();
const onClick = useCallback(async () => {
try {
if (!publicKey) throw new Error('Wallet not connected!');
if (!signMessage) throw new Error('Wallet does not support message signing! Please use a wallet such as Phantom or Solflare! NOTE: Some Ledgers wallets are not supported!');
const message = new TextEncoder().encode('Omega Protocol - Signature verification for Bold Badgers.');
const signature = await signMessage(message);
if (!sign.detached.verify(message, signature, publicKey.toBytes())) throw new Error('Invalid signature!');
//alert(`Message signature: ${bs58.encode(signature)}`);
AddUser();
} catch (error: any) {
alert(`Signing failed: ${error?.message}`);
}
}, [publicKey, signMessage]);
return signMessage ? (<button className="wallet-adapter-button wallet-adapter-button-trigger shine" onClick={onClick} disabled={!publicKey}>Verify</button>) : null;
};
and then the mainstorage file:
import { useEffect } from 'react';
import { useEasybase } from 'easybase-react';
const AddUser = () => {
const { db } = useEasybase();
useEffect(() => {
db('OMEGABB').insert({ walletid: "test", discordid: "test", signature: "test", valid: false, lastvalid: new Date() }).one()
.then(() => console.log("Success!"));
}, [])
return (
{/* ... */}
);
}
export default AddUser;
What is happening however when I click the button is that it comes up with a warning: Hooks can only be called inside the body of a function component.
This does work in the initial index file (aka the parent file) but does not work here. Right now this is only a dummy/test but trying to get it writing to the database.
Thanks!
As per React's documentation:
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns.
Currently, you're attempting to call a hook inside the onClick handler - AddUser is a custom hook since it also uses hooks and the better name for it should be useAddUser.
I suggest to make some improvements by returning a function from your custom hook that you can call to add a new user, e.g.:
export const useAddUser = () => {
const {db} = useEasybase()
const addUser = React.useCallback(() => {
db('OMEGABB')
.insert(/*...*/)
.then(/*...*/)
.catch(/*...*/)
}, [db])
return {
addUser,
/*...*/
}
}
Then, you can use useAddUser in the following way:
const {useAddUser} from './mainstorage'
const SignMessageButton: FC = () => {
const {publicKey, signMessage} = useWallet()
const {addUser} = useAddUser();
const onClick = React.useCallback(
async () => {
try {
// ...
addUser()
} catch (error) {/*...*/}
},
[publicKey, signMessage, addUser]
)
/*...*/
}

Next.js not build when using getStaticPaths and props

I'm trying to run next build when using getStaticProps and getStaticPaths method in one of my routes, but it fails every time. Firstly, it just couldn't connect to my API (which is obvious, they're created using Next.js' API routes which are not available when not running a Next.js app). I thought that maybe running a development server in the background would help. It did, but generated another problems, like these:
Error: Cannot find module for page: /reader/[id]
Error: Cannot find module for page: /
> Build error occurred
Error: Export encountered errors on following paths:
/
/reader/1
Dunno why. Here's the code of /reader/[id]:
const Reader = ({ reader }) => {
const router = useRouter();
return (
<Layout>
<pre>{JSON.stringify(reader, null, 2)}</pre>
</Layout>
);
};
export async function getStaticPaths() {
const response = await fetch("http://localhost:3000/api/readers");
const result: IReader[] = await response.json();
const paths = result.map((result) => ({
params: { id: result.id.toString() },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const res = await fetch("http://localhost:3000/api/readers/" + params.id);
const result = await res.json();
return { props: { reader: result } };
}
export default Reader;
Nothing special. Code I literally rewritten from the docs and adapted for my site.
And here's the /api/readers/[id] handler.
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const knex = getKnex();
const { id } = req.query;
switch (req.method) {
case "GET":
try {
const reader = await knex
.select("*")
.from("readers")
.where("id", id)
.first();
res.status(200).json(reader);
} catch {
res.status(500).end();
}
break;
}
}
Nothing special either. So why is it crashing every time I try to build my app? Thanks for any help in advance.
You should not fetch an internal API route from getStaticProps — instead, you can write the fetch code present in API route directly in getStaticProps.
https://nextjs.org/docs/basic-features/data-fetching#write-server-side-code-directly

How to fix:Response is undefined in react-admin

I'm trying to use react-admin to provide a user CRUD from my API (express)
I followed the steps from react-admin documentation.
Creating my own DataProvider.
Inserting Admin component tells me it is properly setup.
Adding a child Ressource component with users as the ressource name and ListGuesser as the list.
At this point I get a toast saying response in undefined and a console error saying Warning: Missing translation for key: "response is undefined"
I can see in the network tabs that the request is properly sent and receives a 200 response with the data I expected
I cannot understand it and where it comes from
Here is my adminComponent
import React from 'react';
import { Admin, Resource, ListGuesser } from 'react-admin'
import myDataProvider from './myDataProvider'
import './adminHomepage.css'
let myProvider = myDataProvider('http://localhost:8666')
function AdminHomepage(props) {
return (
<Admin dataProvider={myProvider}>
<Resource name="users" list={ListGuesser} />
</Admin>
);
}
export default AdminHomepage;
Here is my dataProvider
import useAuth from "../../hooks/useAuth";
import { stringify } from 'query-string';
import {
fetchUtils,
GET_LIST,
GET_ONE,
CREATE,
UPDATE,
DELETE,
GET_MANY_REFERENCE
} from 'ra-core';
const { getToken } = useAuth();
export default (apiUrl, httpClient = fetchUtils.fetchJson) => {
const convertDataRequestToHttp = (type, resource, params) => {
let url = "";
const options = {};
options.headers = new Headers({ Authorization : getToken(), Accept: "application/json" })
switch (type) {
case GET_LIST: {
url = `${apiUrl}/${resource}/`;
break;
}
case GET_ONE: {
url = `${apiUrl}/${resource}/${params.id}`;
break;
}
case CREATE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "POST";
options.body = JSON.stringify(params.data);
break;
}
case UPDATE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "PUT";
options.body = JSON.stringify(params.data);
break;
}
case DELETE: {
url = `${apiUrl}/${resource}/${params.id}`;
options.method = "DEL";
break;
}
default: {
throw new Error(`Unsupported request type ${type}`);
}
}
return { url, options };
};
const convertHttpResponse = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE: {
if (!headers.has("content-range")) {
throw new Error(
"Content-Range is missing from header, see react-admin data provider documentation"
);
}
let ret = {
data: json.users,
total: parseInt(
headers
.get("Content-Range")
.split(" ")
.pop()
)
};
console.log("RETURN", ret)
return ret
}
case CREATE: {
return { data: { ...params.data, id: json.id } };
}
default: {
return { data: json };
}
}
};
return (type, resource, params) => {
const { url, options } = convertDataRequestToHttp(type, resource, params);
return httpClient(url, options).then(response => {
console.log(response)
convertHttpResponse(response, type, resource, params);
});
};
};
Screenshot of my error
Warning: Missing translation for key: "response is undefined"
in Notification (created by Connect(Notification))
in Connect(Notification) (created by WithStyles(Connect(Notification)))
in WithStyles(Connect(Notification)) (created by Context.Consumer)
in Context.Consumer (created by translate(WithStyles(Connect(Notification))))
in translate(WithStyles(Connect(Notification))) (created by Layout)
in Layout (created by WithStyles(Layout))
in WithStyles(Layout) (created by Route)
in Route (created by withRouter(WithStyles(Layout)))
in withRouter(WithStyles(Layout)) (created by Connect(withRouter(WithStyles(Layout))))
in Connect(withRouter(WithStyles(Layout))) (created by LayoutWithTheme)
in LayoutWithTheme (created by Route)
in Route (created by CoreAdminRouter)
in CoreAdminRouter (created by Connect(CoreAdminRouter))
in Connect(CoreAdminRouter) (created by getContext(Connect(CoreAdminRouter)))
in getContext(Connect(CoreAdminRouter)) (created by Route)
in Route (created by CoreAdminBase)
in CoreAdminBase (created by withContext(CoreAdminBase))
in withContext(CoreAdminBase) (at adminHomepage.js:11)
in AdminHomepage (created by Router.Consumer)
in Router.Consumer (created by Route)
in Route (at App.js:17)
in App (at src/​index.js:7)
I think you're forgetting to return the actual result back:
Change your code to:
return (type, resource, params) => {
const { url, options } = convertDataRequestToHttp(type, resource, params);
return httpClient(url, options).then(response => {
console.log(response)
return convertHttpResponse(response, type, resource, params);
});
}
This bit is probably just to pad the answer.
A Promise in JavaScript is just an object which will at some point get resolved to a value. When you do Promise.then you're basically returning a new promise which will have a callback triggered when resolved and that callback will receive the resolved property. The new promise your make will have the return value based on the result of the callback given. In your case the final resolved promise would have been undefined because nothing was returned in the callback

Resources