How to combine next-i18next into an existing getServerSideProps function in NextJS - next.js

I have a Next page that uses next-i18next in a getServerSideProps and I have another page that uses getServerSideProps to pull data from MongoDB. Both work correctly.
I would like to be able to add next-i18next to the function that connects to Mongo (basically combine the getServerSideProps functions), but I'm getting the error:
nexti18n-next Error: Initial locale argument was not passed into serverSideTranslations'
The first page's getServerSideProps function that connects to next-i18n
export const getServerSideProps = withAuthUserSSR({ whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,})(async ({ locale, }) => {
return {
props: {
...(await serverSideTranslations(locale,
[
'language-file',
...
]
)),
},
};
})
The getServerSideProps function in the second page that pulls data from Mongo:
export const getServerSideProps = withAuthUserSSR({ whenUnauthed: AuthAction.REDIRECT_TO_LOGIN })(async (context) => {
const username = context.params.var[0];
const userId = context.params.var[2];
const { db } = await connectToDatabase();
const pipeline = [
...
]
const postdata = await db.collection('posts').aggregate(pipeline).toArray();
return {
props: {
userId,
username,
postdata: JSON.parse(JSON.stringify(postdata)),
},
};
})
Is it possible to 'add' the next-i18next code to the second function? It seems to me to be an issue with the different way 'locale' and 'context' are defined in each function. I have tried lots of combinations of both but end up messing up either the mongo query or the translations.
This is how I thought it would be done:
export const getServerSideProps = withAuthUserSSR({ whenUnauthed: AuthAction.REDIRECT_TO_LOGIN })(async (context,{ locale, }) => {
const username = context.params.var[0];
const userId = context.params.var[2];
const { db } = await connectToDatabase();
const pipeline = [
...
]
const postdata = await db.collection('posts').aggregate(pipeline).toArray();
return {
props: {
...(await serverSideTranslations(locale,
[
'language-files',
...
]
)),
userId,
username,
postdata: JSON.parse(JSON.stringify(postdata)),
},
};
})
Many thanks for any possible help!

Related

Can i create a Nextjs dynamic route [id]-[first_name]-[last_name]?

I am using nextjs to build a directory. I effectively want to click on 'more info' and an info page to load under the URL of /info/[id]-[first_name]-[last_name].
I am pulling data from an api by id, which will then get the first_name and last_name data.
I have a file inside an info folder named [id]-[first_name]-[last_name] :
export default function Info({ info }) {
return (
<div>
<h1>First Name</h1>
<p> Last Name </p>
</div>
);
}
export const getStaticPaths = async () => {
const res = await fetch('http://xxx:1337/api/info');
const data = await res.json();
// map data to an array of path objects with params (id)
const paths = [data].map(info => {
return {
params: [{
id: `${info.id}-`,
first_name: `${info.first_name}-`,
last_name: `${info.last_name}`
}]
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch('http://xxxx:1337/api/info/' + id);
const data = await res.json();
return {
props: { info: data }
}
With this I just get the error:
Error: A required parameter (id]-[first_name]-[last_name) was not provided as a string in getStaticPaths for /info/[id]-[first_name]-[last_name]
I guess that error is pretty self-explanatory, but I am blocked at this point. I have seen that i may be able to use a slug, but that means re-working a lot of the api.
Any direction with this is apprecated. Thanks!
in this way you can catch all attributes /info/[id]/[first_name]/[last_name]
by making the file /info/[...slug]
export const getStaticProps = async ({ query }) => {
const [id ,firstname ,lastname] = query.slug }
or keep it /info/[slug] and get it as string after that you can split it

Pass params from getStaticPaths to getStaticProps, which is not binded to route parameter?

The SSG documentation recommends to fetch the data for the list of path params in getStaticPaths and fetch data for the individual pages in getStaticProps.
This is the route path:
[postId]/[tempUserShortId]/index.tsx
Except postId and tempUserShortId I tried to pass organizationShortId and imgId, but seems they are not available in getStaticProps.
export const getStaticPaths: GetStaticPaths = async () => {
axios.defaults.baseURL = baseURL(
process.env.NEXT_PUBLIC_ENVIRONMENT ?? 'dev',
false
)
const res = await axios({
method: 'get',
url: 'organizationShortIdPostIdTempUserShortIdAndImgIdList',
})
const paths = res.data.map((organizationAndPostId: string) => ({
params: {
organizationShortId: organizationAndPostId[0],
postId: organizationAndPostId[1],
tempUserShortId: organizationAndPostId[2],
imgId: organizationAndPostId[3],
},
}))
here, not available. Is it any trick to get them. They make easier backend query to execute.
export const getStaticProps: GetStaticProps = async ({ ...context }) => {
let organizationShortId: string = context.params
?.organizationShortId as string
let postId: string = context.params?.postId as string
let tempUserShortId: string = context.params?.tempUserShortId as string
axios.defaults.baseURL = baseURL(
process.env.NEXT_PUBLIC_ENVIRONMENT ?? 'dev',
false
)

RTK Query: Transform all query responses at once

With RTK Query, the response of an endpoint can be transformed with transformResponse like so:
import { apiSlice } from '../api/apiSlice'
const usersAdapter = createEntityAdapter()
const initialState = usersAdapter.getInitialState()
import { camelizeKeys } from 'humps'
export const extendedApiSlice = apiSlice.injectEndpoints({
endpoints: builder => ({
getUsers: builder.query({
query: () => '/users',
transformResponse: (response) => camelizeKeys(response.data),
}),
// … 25 other endpoints
})
})
If each endpoints response need to be transformed in a certain way, say for example by humps camelizeKeys function, this becomes very repetitive rather quickly.
What is the recommended way/best practice, to (globally) define a transformResponse for all queryies (and ideally another one for all mutations)?
I believe that the best place to define such a global transformation is in custom baseQuery:
export const baseQueryWithCamelize: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions = {}) => {
const result = await baseQuery(args, api, extraOptions);
if (result.data) {
result.data = camelizeKeys(result.data as any);
}
return result;
};
then you should use it in root api def:
export default createApi({
baseQuery: baseQueryWithCamelize,
tagTypes: TAGS,
endpoints: (builder) => ({
healthcheck: builder.query<void, void>({
query: () => URLS.HEALTHCHECK,
}),
}),
});
This is for anyone who is not using typescript.
changeResponse is the function you use to change the response for all the endpoints, it should return whatever you want i.e an Array or an Object.
import {changeResponse} from "../functions/changeResponse"
const baseQueryWithChange = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result.data) {
result.data = changeResponse(result.data.data) // function used to transform global response
}
return result
}
wrap baseQuery with the baseQueryWithChange function.
export const apiSlice = createApi({
baseQuery: baseQueryWithChange,
endpoints: builder => ({})
})

How to initialize router before rendering anything in Nextjs?

I'm using react-query, when I get the id from the url and try to call it inside getSubject, it passes an undefined value http://localhost:3000/api/subject/undefined
but when I click a link from another component to get in this subject component it works but if refresh the page it does not work.
const router = useRouter()
const { id } = router.query
const { data } = useQuery('subjects', async () => await getSubject(id))
return value...
}
You should use getServerSideProps in this case. It has access to query params. On top of that you can prefetch data on the server side too.
export interface PageProps {
id: string;
}
export function Page({ id }: PageProps ) {
const { data } = useQuery('subjects', async () => await getSubject(id))
}
export const getServerSideProps: GetServerSideProps<PageProps > = async ({
params,
}) => {
const { id } = params;
return {
props: {
id,
},
};
};
If you still want to use router, you can wait for router.isReady flag. When it is true, query params should be parsed.

App working locally but not on production: TypeError: Cannot read property 'titulo_categoria' of undefined

I'm trying to deploy an app using Prismic as CMS and everything works perfectly locally, but once I deploy to vercel I get the error:
19:09:51.850 | TypeError: Cannot read property 'titulo_categoria' of undefined
There seems to be something wrong when it tries to get the data from Prismic.
My code is the following:
import {getAllCategorias, getCategory2} from '../../lib/api';
export default function Index({cat}) {
return <>{cat.titulo_categoria[0].text}</>;
}
export async function getStaticProps({params}) {
const data = await getCategory2(params.slug);
return {
props: {
cat: data?.categorias ?? null,
},
};
}
export async function getStaticPaths() {
const allPosts = await getAllCategorias();
return {
paths: allPosts?.map(({node}) => `/test/${node._meta.uid}`) || [],
fallback: true,
};
}
And the API code that gets data from Prismic is:
import Prismic from 'prismic-javascript';
const REPOSITORY = process.env.PRISMIC_REPOSITORY_NAME;
const REF_API_URL = `https://${REPOSITORY}.prismic.io/api/v2`;
const GRAPHQL_API_URL = `https://${REPOSITORY}.prismic.io/graphql`;
// export const API_URL = 'https://your-repo-name.cdn.prismic.io/api/v2'
export const API_TOKEN = process.env.PRISMIC_API_TOKEN;
export const API_LOCALE = process.env.PRISMIC_REPOSITORY_LOCALE;
export const PrismicClient = Prismic.client(REF_API_URL, {
accessToken: API_TOKEN,
});
async function fetchAPI(query, {previewData, variables} = {}) {
const prismicAPI = await PrismicClient.getApi();
const res = await fetch(
`${GRAPHQL_API_URL}?query=${query}&variables=${JSON.stringify(variables)}`,
{
headers: {
'Prismic-Ref': previewData?.ref || prismicAPI.masterRef.ref,
'Content-Type': 'application/json',
'Accept-Language': API_LOCALE,
Authorization: `Token ${API_TOKEN}`,
},
}
);
if (res.status !== 200) {
console.log(await res.text());
throw new Error('Failed to fetch API');
}
const json = await res.json();
if (json.errors) {
console.error(json.errors);
throw new Error('Failed to fetch API');
}
return json.data;
}
export async function getCategory2(slug) {
const data = await fetchAPI(
`
query CategoryBySlug($slug: String!, $lang: String!) {
categorias(uid: $slug, lang: $lang) {
titulo_categoria
_meta {
uid
}
}
}
`,
{
variables: {
slug,
lang: API_LOCALE,
},
}
);
return data;
}
Any idea what's wrong with this? I been trying to figure it out for the whole day without any luck
Perhaps you already checked that, but since you mentioned everything works locally and not on Vercel are you sure your environment variables are set there? Especially PRISMIC_API_TOKEN since it appears you're relying on it to query the API?
Also I'm a bit worried about that part of the code:
props: {
cat: data?.categorias ?? null,
}
...where you might be sending a null value to your Index component resulting in your error, I'd try that instead:
props: {
cat: data?.categorias ?? {},
}
...plus using the safe navigation operator (?.) on the Index component?
Let me know how it goes!

Resources