Below my pages directory i have a few routes (for example "/product/details").
I'm using getServerSideProps() to have the page render server side.
How can i send a POST request containing data in the body to this page directly?
The idea would be that i can do something like this:
export async function getServerSideProps(postData) {
return {
props: {postData.body},
}
}
I've tried console logging "postData". I can see that the post request headers are being sent, but the request body is missing.
Thanks
Edit:
I'm doing the posting using Postman, and i'm sending a raw body of type JSON containing a single key:value. But as i said, the page doesn't seem to receive the posted data.
Here is a code snippet for how i'm sending a post request to a route using puppeteer:
const page = await puppeteerConnection.newPage();
await page.setRequestInterception(true);
await page.once('request', (request) => {
let data = {
'method': 'POST',
'postData': JSON.stringify(jsonData),
'headers': {
...request.headers(),
'Content-Type': 'application/json'
},
};
request.continue(data);
page.setRequestInterception(false);
});
await page.goto('pathToNextJSRoute');
getServerSideProps() accepts a context parameter (which you've named postData in your example) and one of the keys in that object (req) contains the request body you're looking for. It arrives as a readable stream of byte data, though, so you'll need to convert it first:
const streamToString = async (stream) => {
if (stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString("utf-8");
}
return null;
};
export async function getServerSideProps(context) {
let data = null;
if (context.req.method === "POST") {
const body = await streamToString(context.req);
data = JSON.parse(body);
}
console.log(data);
return {
props: { data },
};
}
Related
I'm new in JS world and callbacks.
Why I can't return response after then function for the Firebase callable functions?
It returns empty if I return like shown below. I guess it doesn't wait for the response, response has data.output variable actually.
exports.testApi = functions.https.onCall(async(data, context) => {
const formData = new FormData();
formData.append("height", "512");
const response = await axios.post('https://....', formData, {
headers: formData.getHeaders()
})
.then((response) => {
console.log(response.data);
return {'imageURL':response.data.output};
})
.catch((error) => {
console.log(error)
});
}
);
It works with this format
const response = await axios.post('https://..', formData, {
headers: formData.getHeaders()
})
return {'imageURL':response.data.output};
The main attraction of async and await is cleaner syntax, in particular syntax that doesn't use then (opinion). You can mix them but I would always try to avoid it, especially here where the task is so simple.
exports.testApi = functions.https.onCall(async(data, context) => {
try {
const formData = new FormData();
formData.append("height", "512");
// Wait for post to give us a response.
const response = await axios.post('https://....', formData, {
headers: formData.getHeaders()
});
// If we get here, we waited and got a response. Proceed.
console.log(response.data);
return {'imageURL': response.data.output};
} catch (error) {
// If we get here, post threw an error (assuming it throws).
// And we never executed any lines after const response = await...
console.log(error);
}
});
Side note: Firebase Cloud Functions have to be properly terminated and this function does not. For example, if post throws an error and control flows to the catch block then the function will simply timeout because we haven't returned a Promise or thrown a compliant error.
Cookies are not sent to the server via getServerSideProps, here is the code in the front-end:
export async function getServerSideProps() {
const res = await axios.get("http://localhost:5000/api/auth", {withCredentials: true});
const data = await res.data;
return { props: { data } }
}
On the server I have a strategy that checks the access JWT token.
export class JwtStrategy extends PassportStrategy(Strategy, "jwt") {
constructor() {
super({
ignoreExpiration: false,
secretOrKey: "secret",
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
console.log(request.cookies) // [Object: null prototype] {}
let data = request.cookies['access'];
return data;
}
]),
});
}
async validate(payload: any){
return payload;
}
}
That is, when I send a request via getServerSideProps cookies do not come to the server, although if I send, for example via useEffect, then cookies come normally.
That's because the request inside getServerSideProps doesn't run in the browser - where cookies are automatically sent on every request - but actually gets executed on the server, in a Node.js environment.
This means you need to explicitly pass the cookies to the axios request to send them through.
export async function getServerSideProps({ req }) {
const res = await axios.get("http://localhost:5000/api/auth", {
withCredentials: true,
headers: {
Cookie: req.headers.cookie
}
});
const data = await res.data;
return { props: { data } }
}
The same principle applies to requests made from API routes to external APIs, cookies need to be explicitly passed as well.
export default function handler(req, res) {
const res = await axios.get("http://localhost:5000/api/auth", {
withCredentials: true,
headers: {
Cookie: req.headers.cookie
}
});
const data = await res.data;
res.status(200).json(data)
}
I am trying to fetch products from shopify API but don't understand why it's throwing an an error set in my catch statement. The error says
error - utils/fetchShop.js (28:10) # productData
Error: Products not fetched
26 | return data;
27 | } catch (error) {
> 28 | throw new Error('Products not fetched');
| ^
29 | }
30 | }
Can you help me figure out what I am doing wrong here. So basically I am
Creating a function called productData that will accept a query.
productData it will make a POST request to the Shopify Storefront GraphQL API using the set headers and return the json response.
productData function will return the data to the getAllProducts function which will set it equal to the allProducts variable.
Here is my code:
const domain = process.env.SHOPIFY_STOREFRONT_DOMAIN;
const storefrontAccessToken = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN;
async function productData(query) {
const URL = `https://${domain}/api/2022-04/graphql.json`;
const options = {
endpoint: URL,
method: 'POST',
headers: {
'X-Shopify-Storefront-Access-Token': storefrontAccessToken,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
query
}),
};
try {
const data = await fetch(URL, options).then((response) => {
return response.json();
});
return data;
} catch (error) {
throw new Error('Products not fetched');
}
}
export async function getAllProducts() {
const query = `{
products(first: 250) {
edges {
node {
handle
id
}
}
}
}`;
const response = await productData(query);
const slugs = response.data.products.edges ?
response.data.products.edges :
[];
return slugs;
}
So I had this exact problem. It seems to be something to do with the URL variable being all caps.
If you change:
const URL = `https://${domain}/api/2022-10/graphql.json`;
endpoint: URL
To:
const _url = `https://${domain}/api/2022-10/graphql.json`;
endpoint: _url
That should fix the problem. It worked for me.
Be sure to also change the variable in your fetch too to reflect the changes.
EDIT: As this variable is immutable I have added the underscore to note this.
I have a question about auth0 and next js.
For example, I have the next code (this code works)
//initialprops enables server-side rendering in a page and allows you to do initial data population
ModelsList.getInitialProps = async (ctx) => {
//this is static token to test from auth0.com
const accessToken = 'eyJhbG.....'
//fetching data
const res = await fetch('http://localhost:7071/api/bo/getModels', {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
const json = await res.json()
return { data: json }
}
As you can see, I have accessToken variable as a text. It's a problem for me
How can make accessToken dynamic?
Thanks a lot!
P.S please, dont reference to auth0 documentation, I have tried a lot. Provide, please, a real solution/example.
Ok, so this is what worked for me.
Let's say you've got api.example.com/resources. This where data actually is. You will need to proxy via next's api.
Inside your jsx component, you fetch next's api.
// components/Dashboard.jsx
const API_URL = "api/resources";
async function fetcher(url: any) {
const res = await fetch(url);
const json = await res.json();
return json;
}
function Dashboard() {
const { data, error } = useSWR(API_URL, fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>show your resources here</div>;
}
and now inside the next's api file you can fetch the actual endpoint you need.
// api/resources.js
import {
getAccessToken,
getSession,
withApiAuthRequired,
} from "#auth0/nextjs-auth0";
export default withApiAuthRequired(async function healthcheck(req, res) {
const session = await getSession(req, res);
const token = session?.idToken;
const response = await fetch("https://api.example.com/resources", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
res.status(200).json(data);
});
if you get errors, check the jwts you're getting. Audience or scope mismatch errors are usually the main culprits.
I'm using oak/deno. I have a form that is submitted from an ejs file served up. How do I access the form body? When I log it to the console, it prints: {type: "form", value: URLSearchParamsImpl {} }
The post handler is shown below:
router.post("/add", async (ctx: RouterContext) => {
const body = (await ctx.request.body())
console.log(body)
ctx.response.redirect("/");
});
If you're sending x-www-form-urlencoded just use URLSearchParams instance available in body.value.
body.value.get('yourFieldName')
If body.type === "form-data" you can use .value.read() and you'll get the multipart/form-data fields
router.post("/add", async (ctx: RouterContext) => {
const body = await ctx.request.body({ type: 'form-data '});
const formData = await body.value.read();
console.log(formData.fields);
ctx.response.redirect("/");
});
something like this returns the values
it looked like body.value is accessed through .get(<key>) or can be iterated with .entries() or Object.fromEntries()
async register(context: RouterContext) {
const body = context.request.body({ type: 'form' })
const value = await body.value
console.log(value.get('email'))
for (const [key, val] of value.entries()) {
console.log(key, val)
}
const args = Object.fromEntries(value)
console.log(args)
context.response.body = 'test'
}