Why am I getting {statusCode: 400, error: 'Bad Request', message: 'Malicious Path'} when fetching strapi API from my Next.js app - request

When I try to fetch data from strapi API, from my Next.js app I get {statusCode: 400, error: 'Bad Request', message: 'Malicious Path'}.
My code looks like this:
import '../styles/globals.css'
import App from "next/app"
import Head from "next/head"
import Link from 'next/link'
import { createContext } from "react";
import { fetchAPI } from "lib/api";
import { getStrapiMedia } from "lib/media"
export const GlobalContext = createContext({})
export default function MyApp({ Component, pageProps }) {
const { global } = pageProps;
console.log(global)
return (
<>
<Head>
<title>{getStrapiMedia(global.siteName)}</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta name="description" content={getStrapiMedia(global.metaDescription)} />
<Link rel="shortcut icon" href={getStrapiMedia(global.favicon)} />
</Head>
<GlobalContext.Provider value={ global }>
<Component { ...pageProps } />
</GlobalContext.Provider>
</>
)
}
// get app's properties, ctx = context
MyApp.getInitialProps = async(ctx) => {
const appProps = await App.getInitialProps(ctx)
const global = await fetchAPI("/global");
return {...appProps, pageProps: { global }}
}
Here are functions from api.js and media.js
const API_URL = process.env.API_URL
export function getStrapiURL(path = "") {
return `${
API_URL || "http://localhost:1337"
}${path}`;
}
// Helper to make GET requests to Strapi
export async function fetchAPI(path) {
const requestUrl = getStrapiURL(path);
const res = await fetch(requestUrl);
const data = await res.json();
return data;
}
import { getStrapiURL } from "./api";
export function getStrapiMedia(media) {
// if media = null return nothing, else return img
if (typeof media !== "undefined") {
if (media == null) {
return "";
}
// if media starts with "/" return API_URL + img URL else return img URL
const imageUrl = media.url.startsWith("/")
? getStrapiURL(media.url)
: media.url;
return imageUrl;
}
}
It doesn't look like a problem with API as I can fetch the data with postman but not in my App. Fetch would look like this API_URL/global.
Any help would be appreciated.
You can find whole code here

So it turned out I was fetching http://API_URL//global instead of http://API_URL/global. Changing
MyApp.getInitialProps = async(ctx) => {
const appProps = await App.getInitialProps(ctx)
const global = await fetchAPI("/global");
return {...appProps, pageProps: { global }}
}
into
MyApp.getInitialProps = async(ctx) => {
const appProps = await App.getInitialProps(ctx)
const global = await fetchAPI("global");
return {...appProps, pageProps: { global }}
}
solved this issue.

Related

After building nextjs app, chunk/pages/_app-.....js file size is too large

I have a next js project that is created by create next app and modified _app.ts to this
import "../../public/assets/css/bootstrap.css";
import "antd/dist/antd.css";
import "../../public/assets/css/common.css";
import "../styles/globals.css";
import "../styles/builderGlobals.css";
import "../../public/assets/css/quiz.css";
import "../../public/assets/css/main.css";
import "../../public/assets/css/responsive.css";
import Head from "next/head";
import { wrapper } from "../app/store";
import { setUser } from "../Modules/Auth/authSlice";
import Layout from "../components/Layouts/layout";
import type { AppPropsWithLayout } from "../utils/types";
import { setNotifSettingsData } from "../Modules/Notifications/notificationsSlice";
import serverApi from "../utils/axios/serverApi";
import NextNProgress from "nextjs-progressbar";
import { useAppSelector } from "../app/hooks";
const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
const favicon = useAppSelector(state => state.settingsData.favico_icon);
const getLayout =
Component.getLayout ??
((page) => (
<Layout>
<Head>
<link rel="shortcut icon" href={favicon || "/img/favicon.png"} />
</Head>
<NextNProgress /> {page}
</Layout>
));
return getLayout(<Component {...pageProps} />);
};
MyApp.getInitialProps = wrapper.getInitialAppProps(
(store) =>
async ({ Component, ctx }) => {
if (!ctx.req) {
return {
pageProps: {
...(Component.getInitialProps
? await Component.getInitialProps({ ...ctx, store })
: {}),
pathname: ctx.pathname,
},
};
}
try {
const { data: initialData } = await serverApi(
ctx,
`/settings/get-initial-site-data`
);
store.dispatch(setUser(initialData?.authUser));
store.dispatch(setNotifSettingsData(initialData?.siteSettings));
return {
pageProps: {
...(Component.getInitialProps
? await Component.getInitialProps({ ...ctx, store })
: {}),
pathname: ctx.pathname,
},
};
} catch (error) {
ctx.res.statusCode = 404;
ctx.res.end("Not found");
return;
}
}
);
export default wrapper.withRedux(MyApp);
But after running yarn build it created too large _app chunk. about 431kb
That is huge. How can I reduce this chunk? or am I doing anything wrong?
https://github.com/sakib412/sakib412/raw/main/WhatsApp%20Image%202022-10-13%20at%206.48.08%20PM.jpeg

Handling updates in ReactJS

I'm making an application in which the user has the ability to decide if his creations are active or inactive, and the API route responsible for that is
(I'm using NextJs API routes)
import { NextApiRequest, NextApiResponse } from "next";
import { decryptCookie } from "../../../lib/cookie";
import { prisma } from "../../../lib/prisma";
interface User {
email: string;
issuer: string;
}
export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "PUT") return res.status(405).end;
let userFromCookie: User;
try {
userFromCookie = await decryptCookie(req.cookies.auth);
if (!userFromCookie.email) {
throw new Error("Cannot find user. Unable to proceed with creation.");
}
const userEmail = userFromCookie.email;
const active = JSON.parse(req.body);
const userInDb = await prisma.user.findOne({
where: {
email: userEmail,
},
});
const response = await prisma.brainstorm.update({
data: {
active,
},
where: {
id: userInDb.id,
},
});
res.status(201).json({ response });
} catch (error) {
return res.status(500).end(error.message);
}
};
the components that contain this action receives it's data as props from a map method in a parent component
I'll put in here the whole component, but you guys can worry about the Switch that indicates the activeness and the function responsible for the change.
import React, { useState, useEffect } from "react";
import Switch from "react-switch";
import {
Container,
BrainstormInfo,
BrainstormTitle,
Active,
Group,
StormPieces,
} from "./styles";
import { Brainstorm } from "../../pages/user-dashboard";
import useFormatDate from "../../hooks/useFormatDate";
import produce from "immer";
interface Props {
brainstormData: Brainstorm;
}
const UserBrainstormCard: React.FC<Props> = ({ brainstormData }) => {
if (!brainstormData) return <h1>Loading...</h1>;
const [active, setActive] = useState(brainstormData.active);
const formatedDate = useFormatDate(
(brainstormData.createdAt as unknown) as string
);
async function handleActiveness() {
setActive(!active);
const response = await fetch("/api/brainstorm/update", {
method: "PUT",
body: JSON.stringify(active),
});
const data = await response.json();
setActive(data.response.active);
}
return (
<Container>
<BrainstormInfo>
<p>Brainstorm</p>
<p>{formatedDate}</p>
</BrainstormInfo>
<BrainstormTitle>
<h3>{brainstormData.title}</h3>
</BrainstormTitle>
<Active>
<Group>
<p>Active:</p>
<Switch
offHandleColor="#eee"
onHandleColor="#eee"
draggable={false}
onChange={handleActiveness}
checked={active}
checkedIcon={false}
uncheckedIcon={false}
height={15}
width={30}
handleDiameter={20}
offColor="#f13030"
onColor="#2dea8f"
/>
</Group>
<StormPieces>
<p>
{brainstormData.stormPieces.length}
{` `}Stormpieces
</p>
</StormPieces>
</Active>
</Container>
);
};
export default UserBrainstormCard;
The call to the API happens, but when I update the page it all goes back to what the value it was initially.
I'm pretty sure that the problem has to do with state, and that I should find a way to insert this values in the state. But I don't know a clear path on how to do it

Send param to fetch in getInitialProps react and nextjs

I 'm traying to send a param to getInitialProp function to made the fecth to the correct json.
here is my code:
hepler.js --> here I made the fetch per se.
export async function getEvents() {
const res = await fetch("https://url/eventos.json");
let new_data = await res.json();
return { events: new_data.data };
}
export async function getDetails(slug) {
const res = await fetch(`https://myurl/${slug}.json`);
let data_detail_event = await res.json();
return { data_detail_event };
}
_app.js // here I have the getInitialProps and works great
import App from "next/app";
import ContextProvider from "../provider/ContextProvider";
import fetch from "isomorphic-unfetch";
import {getEvents, getDetails} from '../helper/index'
export default class MyApp extends App {
static async getInitialProps() {
const events = await getEvents();
return {
events : events.events
};
}
render() {
const { Component, pageProps } = this.props;
return (
<div>
<ContextProvider events={this.props.events} >
<Component {...pageProps} />
</ContextProvider>
</div>
);
}
}
pages/[id].js
import { useRouter } from "next/router";
import Context from "../../config/Context";
/* Components */
import WordCounter from "../../components/word-counter/WordCounter";
function Post(props) {
const router = useRouter();
const context = React.useContext(Context);
return (
<React.Fragment>
<WordCounter />
</React.Fragment>
);
}
Post.getInitialProps = async ({ query}) => {
const detail = await getDetail(query.id) --> here I send the param and it seems never arrive to helper.js, why?
return {detail}
}
export default Post
Where is the problem? HELP!
THAANKS!
i think getInitialProps run in server and your helper function doesn't load there.
use fetch inside getInitialProps .

Next.js - Browser back gives--- TypeError: Cannot read property 'split' of undefined

My issue is similar to this..
https://github.com/zeit/next.js/issues/5604
I am using a custom server but I am not using any custom route handling. Even if remove the custom server and only run next i get this error while navigating back using browser back button.
As mentioned in https://github.com/zeit/next.js/blob/canary/errors/popstate-state-empty.md
I am not manipulating window.history in any place. Still I am getting this error.
I am using next/router for routing.
This is the _app.js code.
import React from 'react';
import App from 'next/app';
import Router from 'next/router';
import Head from 'next/head';
import withRedux from 'withRedux';
import { Provider } from 'redux-bundler-react';
import { ThemeProvider } from 'emotion-theming';
import { Global } from '#emotion/core';
import themeOne from 'ui-web/theme';
import { getCookie } from 'modules/authentication';
import configureStore from '../../src/store';
import { persist, cacheVersions } from '../../src/common';
import { appWithTranslation } from '../../i18n';
const makeStore = initialState => configureStore(initialState);
class MyApp extends App {
static async getInitialProps(props) {
const { Component, ctx, router } = props;
if (ctx.isServer && ctx.req.headers.cookie) {
const token = getCookie('authToken', ctx.req);
ctx.store.doSetAuthToken(token);
}
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx, router.pathname)
: {};
return { pageProps };
}
render() {
const { Component, store, pageProps } = this.props;
return (
<Provider store={store}>
<ThemeProvider theme={themeOne}>
<Head>
<title>Learny</title>
<link
href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600&display=swap'
rel='stylesheet'
/>
</Head>
<Global
styles={theme => ({
body: {
margin: 0,
overflowX: 'hidden',
backgroundColor: theme.colors.background,
a: {
textDecoration: 'none',
},
},
})}
/>
<Component {...pageProps} />
</ThemeProvider>
</Provider>
);
}
}
export default withRedux(makeStore, { debug: false, persist, cacheVersions })(
appWithTranslation(MyApp)
);
server.js code sample is
/* eslint-disable #typescript-eslint/no-var-requires */
const express = require('express');
const next = require('next');
const nextI18NextMiddleware = require('next-i18next/middleware').default;
const nextI18next = require('./i18n');
const port = process.env.PORT || 3000;
const app = next({ dev: process.env.NODE_ENV !== 'production' });
const handle = app.getRequestHandler();
(async () => {
await app.prepare();
const server = express();
server.use(nextI18NextMiddleware(nextI18next));
server.all('*', (req, res) => handle(req, res));
await server.listen(port);
console.log(`> Ready on http://localhost:${port}`);
})();

Cookie token access in _app.js in next.js

Here is my _app.js page:
import { Provider } from 'react-redux';
import App from 'next/app';
import withRedux from 'next-redux-wrapper';
import { Layout } from '../components';
import makeStore from '../store';
import { api } from '../utils/api';
class MyApp extends App {
render() {
const { Component, pageProps, store } = this.props;
console.log(this.props); // Here I cannot see cookie user data, it is empty object even though the cookie is set in `_document.js`
return (
<Provider store={store}>
<Layout { ...this.props }>
<Component { ...pageProps } />
</Layout>
</Provider>
);
}
}
MyApp.getInitialProps = api.authInititalProps();
export default withRedux(makeStore)(MyApp);
As you can see I'm trying to get cookie user data in getInitialProps lifycycle. Here my api file which gathers the logic of getting the cookie data:
const WINDOW_USER_SCRIPT_KEY = '__USER__';
class API {
getServerSideToken = req => {
const { signedCookies = {} } = req;
if(!signedCookies || !signedCookies.token) {
return {}
}
return { user: signedCookies.token };
}
getClientSideToken = () => {
if(typeof window !== 'undefined') {
const user = window[WINDOW_USER_SCRIPT_KEY] || {};
return { user };
}
return { user: {} }
};
authInititalProps = () => ({ req, res }) => {
return req ? this.getServerSideToken(req) : this.getClientSideToken();
}
}
The token itself is set in `_document.js:
import Document, { Head, Main, NextScript } from 'next/document';
import { api } from '../utils/api';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const props = await Document.getInitialProps(ctx);
const userData = await api.getServerSideToken(ctx.req);
return { ...props, ...userData }
}
render() {
const { user = {} } = this.props;
return(
<html>
<Head />
<body>
<Main />
<script dangerouslySetInnerHTML={{ __html: api.getUserScript(user) }} />
<NextScript />
</body>
</html>
);
}
};
export default MyDocument;
What is happening is that I'm not getting the cookie token inside my _app.js, instead I'm getting empty object, even though the cookie is set and in the console after typing window.__USER__ I can see it. The aim of all that is to pass user data down to Layout component which is invoked inside the _app.js. Why cannot I see it inside _app.js?

Resources