NextJs 13(app dir) fetch data from build in api - reactjs

I am using Nextjs 13 with /src and /app directory. Below I am trying to fetch data from nextjs api:
//src/app/page.tsx
const getProducts = async () => {
try {
const res = await fetch('/api/products');
const data = await res.json();
return data;
} catch (err) {
console.log(err);
}
}
export default async function Home() {
....
}
//src/pages/api/products
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Product[]>
) {
res.status(200).json(products)
}
this doesn't work and instead show Failed to parse URL from /api/products and TypeError [ERR_INVALID_URL]: Invalid URL.
Note: When I fetch the same data using localhost:3000 with url that does work perfectly fine.
I even tried using `/pages/api/products' that doesn't work either.

please create index.js inside /api/products/index.js and then build your endpoint and call that from Component as you did above
e.g
export default function handler(req, res) {
res.status(200).json([{id:1, title:'T-Shirt'},{id:2,title:'Shoes'}]);
}

Related

Intercepting Auth0 getSession with MSW.js and Cypress

I'm building NextJS app with SSR. I've written the getServerSideProps function that makes a call to supabase. Before making the call I'm trying to get user session by calling getSession function from #auth0/nextjs-auth0 package.
I'm trying to mock it in the handlers.ts file:
import { rest } from 'msw';
export const handlers = [
// this is the endpoint called by getSession
rest.get('/api/auth/session', (_req, res, ctx) => {
return res(ctx.json(USER_DATA));
}),
rest.get('https://<supabase-id>.supabase.co/rest/v1/something', (_req, res, ctx) => {
return res(ctx.json(SOMETHING));
}),
];
My mocks file: requestMocks/index.ts:
export const initMockServer = async () => {
const { server } = await import('./server');
server.listen();
return server;
};
export const initMockBrowser = async () => {
const { worker } = await import('./browser');
worker.start();
return worker;
};
export const initMocks = async () => {
if (typeof window === 'undefined') {
console.log('<<<< setup server');
return initMockServer();
}
console.log('<<<< setup browser');
return initMockBrowser();
};
initMocks();
Finally, I'm calling it in the _app.tsx file:
if (process.env.NEXT_PUBLIC_API_MOCKING === 'true') {
require('../requestMocks');
}
Unfortunately, it does work for me. I'm getting no user session data in the getServerSideProps function in my page component:
import { getSession } from '#auth0/nextjs-auth0';
export const getServerSideProps = async ({ req, res }: { req: NextApiRequest; res: NextApiResponse }) => {
const session = getSession(req, res);
if (!session?.user.accessToken) {
// I'm constantly falling here
console.log('no.session');
return { props: { something: [] } };
}
// DO something else
};
Any suggestions on how to make it working in Cypress tests would be great.
I'm expecting that I will be able to mock requests made in getServerSideProps function with MSW.js library.
I made it finally. Looks like I don't have to mock any calls. I need to copy my user appSession cookie and save it in cypress/fixtures/appSessionCookie.json file:
{
"appSession": "<cookie-value>"
}
Then use it in tests as follows:
before(() => {
cy.fixture('appSessionCookie').then((cookie) => {
cy.setCookie('appSession', cookie.appSession);
});
});
This makes a user automatically logged in with Auth0.

How to get post data in NextJS with Typescript?

I wrote a function to get posted form data in a NextJs page. While this works, the req and res parameters aren't typed:
const getBody = promisify(bodyParser.urlencoded());
export async function getServerSideProps({ req, res }) {
if (req.method === "POST") {
await getBody(req, res);
}
return {
props: {
input: req.body?.input
}
}
}
I've tried using req: NextApiRequest, res: NextApiResponse with an interface but the function body won't work:
interface ExtendedNextApiRequest extends NextApiRequest {
body: {
input: string;
};
}
export async function getServerSideProps(req: ExtendedNextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
await getBody(req, res);
}
return {
props: {
input: req.body?.input
}
}
}
However I get a serialization error:
Error: Error serializing `.input` returned from `getServerSideProps` in "/my-page".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
Your problem is that you are using getServerSideProps as an API endpoint. getServerSideProps is meant to be be used to fetch data from an API endpoint or backend logic and pass it to the function component, so it can not handle post methods. If you want to make an API endpoint, you can make a function in the /pages/api directory. You can strong type that like this:
import type { NextApiHandler } from "next";
let handler: NextApiHandler<any> = async (req, res) => {
// your code goes here
};
export default handler;
where the any can be replaced by the type of the api response.

NextJS: error in getServerSideProps function with axios

On the main page (index.js file) I use the getServerSideProps function
export async function getServerSideProps(context) {
axios.defaults.headers.common['Lang'] = context.locale
try {
const response = await axios.get('/index?limit=8')
return {
props: {
data: response.data
},
};
} catch (error) {
return {
props: {
error: error
},
};
}
}
Everything used to work, but now it's starting to make a mistake
connect EADDRNOTAVAIL ip:443 - Local (ip:0)
Although if you make a request to the same address in useEffect () - everything works
Tried to upgrade next to version 12 - the error remained
Screenshot
try
const response = await axios.get(`https://yourserver.com/index?limit=8`)
and if works replace https://yourserver.com by your .env variable
Also, try to console.log your variable:
const response = await axios.get('/index?limit=8')
console.log(response)
And check if your API route has .get method
In getServerSideProps you have to type the whole url http://localhost:3000/api/my-end-point
So I have two instances of axios in nextjs.
import Axios from 'axios'
// Use in react component
const ClientAxios = Axios.create({
baseURL: '/api'
})
// Use in getServerSideProps
const SystemAxios = Axios.create({
baseURL: 'http://localhost:3000/api'
})

Directly import next.js API endpoint in getServerSideProps()

When fetching data using getServerSideProps() in Next.js, they recommend directly importing the API endpoint instead of using fetch() and running another HTTP request. This makes sense, and I was able to get it working until implemented middleware for my API (note, I'm using the API feature built into Next.js). Now with middleware implemented, I can't export functions that use the middleware, I have to export the handler. See below:
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => {
const post = await req.db.collection("posts").findOne();
res.send({
post: post,
});
});
export default handler;
What would be the recommend way to import my API endpoint into getServerSideProps? I would like to do something as follows, but the getPost() function no longer has access to the database middleware:
export const getPost = async () => {
const post = await req.db.collection("posts").findOne();
return post;
}
handler.get(async (req, res) => {
res.send({
post: getPost(),
});
});
and then in my next.js page:
import { getPost } from './api/post';
...
export async function getServerSideProps(context) {
return {
props: {
post: getPost(),
}
}
}
In any case, you'll have to pass the req and res objects to the function. But if you do the following, the post prop should be populated with a NextApiResponse instance, which at it's base is a stream.Writable object, which is probably not what you want...
import { getPost } from './api/post';
...
export async function getServerSideProps({req, res}) {
return {
props: {
post: await getPost(req, res),
}
}
}
You could try to read the stream, but that seems like more trouble than refactoring your code, but if you call getPost(req, res).end(), I think you should get the streamed data, but I'm not sure how it will be formatted. You'd have to check.
You could split your functions up a little more..
// In your api endpoint:
const handler = nextConnect();
handler.use(middleware);
export async function getPostMethod(db) {
return await db.collection("posts").findOne();
}
handler.get(async (req, res) => {
res.send({
post: await getPostMethod(req, res, req.db)
})
});
export default handler;
// In your page component:
export async function getServerSideProps({req, res}) {
// Do what ever you have to do here to get your database connection
const db = await whereIsMyDb()
return {
props: {
post: await getPostMethod(db),
}
}
}

React PWA - How do you detect if the requested data to be served from cache or from server

I am building a PWA app, where I download data by a user action call, "Go to offline". So once the button is clicked the data is fetched and saved to the browser indexdb storage. I am using workbox and CRA2. I folled this blog post to customize workbox config with cra app.
Now say there is no PWA, and when page requests data, react call actions for example:
export async function fetchProjectStatuses(
dispatch,
params = {},
raiseError = false
) {
try {
dispatch(requestProjectStatuses());
const data = await fetchProjectStatusesApi();
dispatch(receiveProjectStatuses(data));
return data;
} catch (err) {
if (raiseError) {
throw err;
} else {
dispatch(requestProjectStatusesFailed(err));
}
}
}
and fetchProjectStatusesApi is defined as:
import axios from "axios";
const fetchAllUrl = () => `/project_statuses.json`;
export async function fetchProjectStatusesApi(config, params = {}) {
const url = fetchAllUrl();
const { data } = await axios.get(url, {
headers: { Accept: "application/json" }
});
return data;
}
This works. Now when offline, I am trying to write something like:
import { registerRoute } from "workbox-routing";
registerRoute(
new RegExp("/project_statuses\\.json"),
async ({ url, event, params }) => {
try {
const response = await fetch(event.request);
const responseBody = await response.text();
return new Response(responseBody);
} catch (err) {
// get from DB
}
}
);
So how do I write handler so that it forwards the data to the fetchProjectStatusesApi if network is present , else get data from DB. I know how to pull the date from local IDB. Only thing I am not able to figure out:
How do I detect the app is offline so the data has to come from local db
If app is online how do I forward the response from fetch to axios which is called from the api function.
I am writing it first time so I have no idea yet. Any help will be appreciated.
Thank you.

Resources