How can I disable the bodyparser for one API route method, but enable it for another? - next.js

As the question states, how can I disable the bodyparser for one API route method, but enable it for another?
Here's part of my API route:
const handler = async (req, res) => {
if (req.method === "POST") {
...
} else if (req.method === "DELETE") {
...
} else {
res.status(405).json({
data: null,
error: "Method not allowed."
});
return;
};
};
export const config = {
api: {
bodyParser: false,
},
};
export default handler;
You'll see at the bottom of the code block that the body parser applies to both methods at the moment. I need to use the body parser for the DELETE method so that I can access a variable set within the body, but it needs to be disabled for the POST method (handling of multipart forms for image uploads).
I've thought about splitting these into two different files as follows:
Current setup:
/api/advert/[advertId]/image/index.jsx
Potential setup:
/api/advert/[advertId]/image/post.jsx
/api/advert/[advertId]/image/delete.jsx
While this would work, it would deviate the structure from the rest of my code and so I was wondering if there is a cleaner way to achieve this? (Preferably without the use of additional middleware)
Thanks

Related

Client components, localstorage in nextjs13

I am moving the project from react to nextjs and have a problem. I have a global wrapper that handles the login context and is using local storage.
I set "use client" directive at the top, but the component is trying to be rendered on the server too, and this code gives a hydration error:
export function AuthProvider(props) {
const initialParsedToken = typeof localStorage !== 'undefined' ? localStorage.getItem("jwt") : null
if (initialParsedToken) {
const decodedToken = jwtDecode<JwtInterface>(initialParsedToken);
if (decodedToken.exp * 1000 < Date.now()) {
localStorage.removeItem("token");
} else {
initialState.user = decodedToken;
}
}
const [state, dispatch] = useReducer(authReducer, initialState);
if (typeof localStorage === 'undefined'){
return null
}
const login = (userData: any) => {
localStorage.setItem("jwt", userData.token);
dispatch({ type: "LOGIN", payload: userData });
};
function logout() {
localStorage.removeItem("jwt");
dispatch({ type: "LOGOUT" });
}
return (
<AuthContext.Provider
value={{ user: state.user, login, logout }}
{...props}
/>
);
}
The error:
Hydration failed because the initial UI does not match what was rendered on the server.
How can i use the useReducer hook in next, or make auth logic work with this framework?
I was planning to make render static content as server components, having client parts, but can I avoid using ugly useffect hack in each client component and just add a use client in nested dynamic content?
I tried using useffect hack in parent component, but nested components still don't work.
You should check on the type of window instead to make sure you are on the client side
if (typeof window !== undefined){
....your local storage logic
}
Also, you should consider moving your token to cookies instead of local storage as it doesn't cause conflicts and you can check your cookies on the server side using the context parameter, see this

How to use runtime config in composable?

I want to do this
composables/apiFetch.ts
import { $fetch } from 'ohmyfetch'
export const useApiFetch = $fetch.create({ baseURL: useRuntimeConfig().apiUrl })
And use it within Pinia so I don't repeat myself writing $fetch.create over and over again for every single API call.
somewhere_in_pinia.ts
...TRIM...
actions: {
async doSomething(payload: SomeNicePayload): Promise<void> {
const response = await useApiFetch('/something', { method: 'POST', body: payload })
}
}
...TRIM...
But Nuxt won't allow me
[nuxt] [request error] nuxt instance unavailable
at useNuxtApp (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:472:13)
at Module.useRuntimeConfig (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:480:10)
at $id_Yl353ZXbaH (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:38358:90)
at async __instantiateModule__ (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:40864:3)
I have been looking for solution online, followed instruction from the official discussion to no avail.
EDIT
I don't want to use Nitro, since my backend is already written on Laravel. I need to access the host without re-typing it all over the place so I thought I could use .env and runtimeConfig.
you are trying to access Nuxt instance while it's not ready yet. To make it work, write your composable as a function :
import { $fetch } from 'ohmyfetch'
export const useApiFetch = (url, params) => {
const instance = $fetch.create({ baseURL: useRuntimeConfig().apiUrl })
return instance(url, params)
}

Trying to implement shopify webhooks but getting 'InternalServerError: stream is not readable'

I'm building an app for shopify and need to add the GDPR webhooks. My back end is handled using next.js and I'm writing a webhook handler to verify them. The docs havent been very helpful because they dont show how to do it with node. This is my verification function.
export function verifiedShopifyWebhookHandler(
next: (req, res, body) => Promise
): NextApiHandler {
return async (req, res) => {
const hmacHeader = req.headers['x-shopify-hmac-sha256'];
const rawBody = await getRawBody(req);
const digest = crypto.createHmac('sha256', process.env.SHOPIFY_API_SECRET).update(rawBody).digest('base64');
if (digest === hmacHeader) {
return next(req, res, rawBody);
}
const webhookId = req.headers['x-shopify-webhook-id'];
return res.status(401).end();
};
}
But I get this Error: error - InternalServerError: stream is not readable
I think it has to do with now Next.js parses the incoming requests before they are sent to my api. Any ideas?
I discovered the answer. Next.js was pre parsing the body in the context which made it so that I couldn't use the raw body parser to parse it. By setting this:
export const config = {
api: {
bodyParser: false
}
};
above the api function in the api file it prevented next from parsing it and causing the issue. I found the answer because people had the same issue integrating swipe and using the bodyParser.

NextJS case insensitive route for SSG pages

I am using NextJS to translate a CSV of data into static pages. Each page is pages/[slug].jsx. I'm calling toLowerCase() on the slug value inside [slug].jsx getStaticPaths() and getStaticProps() functions. The generated pages are lowercase. e.g. /ab101, /ab102, /cc500 all resolve to the pages/[slug].jsx page.
Unfortunately, people might hand type the url and may use caps or mixed case for the slug value, currently resulting in a 404.
QUESTION: How can I make routing case insensitive with respect to the slug value?
UPDATE
When I return fallback: true from getStaticPaths(), my [slug].jsx file is hit even when there is not an exact path match. I can then check isFallback as illustrated by Anish Antony below.
Additionally, the items param that is passed to my page will be undefined when the page wasn't found. The router's pathname value is "/[slug]" and not the value of "slug". However, there is an asPath value which contains useful data, e.g. /mixedCaseSlug?param=value&foo=bar.
When the page renders, I check if it's a fallback. If it is, show a LOADING... message. Next will display that and call getStaticProps() to generate the "missing" page. You'll then re-render with the page data. In the event that getStaticProps couldn't get page data, I push a path that will lead to the built-in 404 page.
export default function Page({ item }) {
const { isFallback, push } = useRouter()
const hasPageData = item && Object.keys(item).length > 0
useEffect(() => {
if (!isFallback && !hasPageData) {
push('/page-not-found/error')
}
}, [isFallback, hasPageData])
const loadingMsg = <div>Loading...</div>
const notFoundMsg = <div>Page not found</div>
return isFallback ? loadingMsg : hasPageData ? <Item item={item} /> : notFoundMsg
}
I needed to update getStaticProps() to lowercase the slug param, as it may now be mixed case, but we want to find our page data. And I needed to allow for the case when there really is no data for the slug.
export async function getStaticProps({ params }) {
const { slug } = params
const item = data.find(o => o.Practice_Code.trim().toLowerCase() === slug.toLowerCase())
return {
props: {
item: item ? item : {}
}
}
}
This all seems very kludgy, so I'm still wondering if there is a better way.
NextJS routes are case sensitive.You can use fallback property in getStaticPaths to catch the routes which aren't in the same case as the one provided by default in getStaticPaths.
Edit: I have updated the answer based on the discussion with Dave.
We can give fallback:true or fallback:"blocking" , if we give fallback:true we we can show a custom component which will be displayed till the time page is loaded.For fallback:"blocking" new paths not returned by getStaticPaths will wait for the HTML to be generated,
When we give fallback:true or "blocking" static page will be generated when the user first access the site and the generated page will be served for further visits.
Sample code
export async function getStaticPaths() {
const idList = await fetchAllIds();
const paths = [
];
idList.forEach((id) => {paths.push(`/posts/${id}`)})
return { paths, fallback: true };
}
What we have note is our code in getStaticProps should be case insensitive to get the data irrespective of the one provided in url.
export async function getStaticProps({ params }) {
const { slug } = params;
try {
/// This fetch api should be able to fetch the data irrespective of the case of the slug
or we should convert it to the required case before passing it as a parameter to API
const data= await fetchSlugDetails(slug);
//Same logic should be performed if you are getting data filtered based on slug from an existing array.
return data? { props: { data} } : { notFound: true };
} catch (error) {
console.error(error);
return { notFound: true };
}
}
Note: You have to handle the notfound case and fallback case if you are using fallback:true. For fallback you can get the value isFallback from next/router while the page is being static generated

Get values from SvelteKit's $app/stores outside of the lifecycle of a component

My Svelte components import readable stores like this:
import { classes, locations, schedule } from 'stores.ts'
In stores.ts, I want to build the URL for fetch dynamically using page.host from $app/stores.
// Note: this is not a Svelte component; it's stores.ts
import { readable } from 'svelte/store'
import { getStores } from '$app/stores'
const { page } = getStores()
let FQDN
page.subscribe(({ host }) => {
FQDN = host
})
const getArray = async (url) => {
const response: Response = await fetch(url)
if (!response.ok) throw new Error(`Bad response trying to retrieve from ${url}.`)
return await response.json()
}
const getReadableStore = (url: string) => readable([], set => {
getArray(`http://${FQDN}${url}`)
.then(set)
.catch(err => console.error('Failed API call:', err))
return () => {}
})
export const classes = getReadableStore('/api/class/public.json')
export const locations = getReadableStore('/api/location/public.json')
export const schedule = getReadableStore('/api/schedule/public.json')
The sixth line throws this error...
Error: Function called outside component initialization
at get_current_component (/Users/nates/dev/shy-svelte/node_modules/svelte/internal/index.js:652:15)
at Proxy.getContext (/Users/nates/dev/shy-svelte/node_modules/svelte/internal/index.js:685:12)
at Module.getStores (/.svelte-kit/dev/runtime/app/stores.js:17:26)
at eval (/src/stores.ts:6:38)
at instantiateModule (/Users/nates/dev/shy-svelte/node_modules/#sveltejs/kit/node_modules/vite/dist/node/chunks/dep-e9a16784.js:68197:166)
Two questions...
What is the correct way to get page values from $app/stores outside of the context of a component? Is this possible? Answer from below: No, this is not possible outside the context of a component.
If I'm accessing a SvelteKit site, let's say http://localhost:3000/something or https://example.com and a Svelte component loads a readable store from stores.ts, is there a way in stores.ts to determine whether the original page request that loaded the component (which loaded from stores.ts) was http or https? Answer from below: No, this is not possible in stores.ts - only from a component.
UPDATE: Based on the feedback, I'm going to set a value in my .env called VITE_WEB_URL=http://localhost:3000 and change it for the production system. This cuts down on the number of lines of code and may be a better practice (comments welcome)...
// revised stores.ts
import { readable } from 'svelte/store'
const { VITE_WEB_URL } = import.meta.env
const getArray = async (url) => {
const response: Response = await fetch(url)
if (!response.ok) throw new Error(`Bad response trying to retrieve from ${url}.`)
return await response.json()
}
const getReadableStore = (url: string) => readable([], set => {
getArray(`${VITE_WEB_URL}${url}`)
.then(set)
.catch(err => console.error('Failed API call:', err))
return () => {}
})
export const classes = getReadableStore('/api/class/public.json')
export const locations = getReadableStore('/api/location/public.json')
export const schedule = getReadableStore('/api/schedule/public.json')
Extract from https://kit.svelte.dev/docs#modules-$app-stores
Because of that, the stores are not free-floating objects: they must be accessed during component initialisation, like anything else that would be accessed with getContext.
Therefore, since the readable store is bound to the context of a svelte component, I suggest you subscribe either way ($ or .subscribe) inside the component of the SvelteKit website and then send the protocol value (http or https) as parameter when it updates so that stores.ts stores it in a variable.
However, it looks like SvelteKit does not provide the protocol value, so parse the client side window.location.href in the page subscription and then send it.
Referencing a svelte store can be done everywhere.
Using the $: shorthand syntax, however, only works within a component.
$: BASE = `http://${$page.host}`
SvelteKit appears to delegate this to fetch indeed

Resources