I have a question related to the static generation of Next.js:
I'm creating whitelabel websites for my customers; it means that I'm reading the domain where the request is coming from to load a config file and some specific CSS files. That's working fine and looks like this:
export const readConfig = async ({req}) => {
const configs = await import('../configs.json')
const domain = req ? req.headers['host'].split(':')[0] : window.location.hostname
const config = configs[domain]
return {domain, config}
}
Page.getInitialProps = readConfig
However, I'm using getInitialProps for that and my understanding is that because I rely on req, this code will be loaded for every page.
Now, let's say that I want to have a static generation of some pages, how should I proceed? Can I avoid having count_different_domains * count_different_items combinations? Is that somehow possible to cache the result of some queries and revalidate it later (but not as entire Page)?
Related
I implemented a dynamic server side sitemap using next-sitemap package.
And it works smoothly on my local, but when i visit the production sitemap url (https://www.my-url.com/sitemap-dynamic.xml ) it takes too long to load sometimes.
The issue is that the proxy cuts of the request with a 499 response or even 500, after its timeout (30/60 seconds+), resulting in search engines not being able to read the sitemap.
FYI what I did is the same as it's explained in the demo with my customization :
// pages/server-sitemap-index.xml/index.tsx
import { getServerSideSitemapIndex } from 'next-sitemap'
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (ctx) => {
// Method to source urls from cms
// const urls = await fetch('https//example.com/api')
return getServerSideSitemapIndex(ctx, [
'https://example.com/path-1.xml',
'https://example.com/path-2.xml',
])
}
// Default export to prevent next.js errors
export default function SitemapIndex() {}
For instance check if we can prewarm the sitemap and refresh it at interval x (hourly/daily ?)
Did someone face or hear about this problem before ? please a hint , and thanks in advance
I need to forward a header from the browser to an external API I call from the server side.
The external API is called from getServerSideProps and API routes.
I was thinking about implementing some sort of a request context (using AsyncLocalStorage for example) that I can access from anywhere in the server side code.
That way I could create a middleware that will save the header to the context, and in the external API client I'll fetch it from the context and add it to the requests.
For example:
// context.ts
export const context = new AsyncLocalStorage<string>();
// middleware.ts
export function middleware(request: NextRequest) {
const store = request.headers[SOME_HEADER];
return context.run(store, () => NextResponse.next());
}
// client.ts
axios.post(EXTERNAL_API, DATA, {
headers: {
SOME_HEADER: context.getStore()
}
}).then(...)
Currently I simply send it as a parameter which is pretty tedious.
Is there a way of achieving it?
I tried adding async_hooks to my project but it got really messy when I tried to build the project.
I have a simple express app like this.
const express = require("express");
var app = express();
app.get("/", (req, res)=>{
...handle request...
});
...
exports.app = functions.https.onRequest(app);
I am deploying on firebase. and when I deploy it it creates a new route by the name of the function like this: https://us-central1-[projectname].cloudfunctions.net/app
this is because I put exports.app = functions.https.onRequest(app);
so how can I deploy and make it work without the /app at the end? I need this because I am having issue with the references on my front end which use routes like "/login", which in this case won't work since it had added app requiring all routes to be like "/app/login"
I even tried export default app but no luck.
how can I deploy with out the function name as a route?
Removing /[exportname] from the first part of a URL hosted on cloudfunctions.net is not possible as this is how the functions are triggered.
Ideally rather than serve resources from the cloudfunctions.net domain, you place your functions behind Firebase Hosting where you can instead use a URL like https://yourapp.example.com/login which will play nicely with Express.
However, if you wish to call https://us-central1-[projectname].cloudfunctions.net/app/login and have it behave as if it was called from https://us-central1-[projectname].cloudfunctions.net/login, you can make use of a conditional URL rewrite. The example below will strip /app from the URL if-and-only-if the hostname ends in cloudfunctions.net and the URL also starts with "/app", then handing over to the other routes.
import express from "express";
function removePathForCloudFunctionsDomain(path) {
return function(req, res, next) {
const rawUrl = req.url; // stash original URL
// do nothing if not on cloudfunctions.net or path doesn't match
if (!req.hostname.endsWith("cloudfunctions.net") || !rawUrl.startsWith(path)) {
return next();
}
// if here, trim path off of the request's URL
req.url = req.originalUrl = rawUrl.slice(path.length);
// hand over to other app.get(), app.use(), etc.
next('route');
}
}
const app = express();
app.use(removePathForCloudFunctionsDomain("/app"));
/* other routes */
export app;
My app is in Koa JS.
The code below generates a website with full css (http://localhost:3000/product):
module.exports = (router, productsLoader) => {
router.get("/product", async ctx => {
const product = await productsLoader.single(ctx.params.slug)
ctx.state.model = {
title: 'Hey there,',
products: product
}
await ctx.render('product');
})
}
However if I add a key (:slug) to the url, the website can show the bare content but not any css features (http://localhost:3000/product/abc):
module.exports = (router, productsLoader) => {
router.get("/product/:slug", async ctx => {
const product = await productsLoader.single(ctx.params.slug)
if (product) {
ctx.state.model = {
title: product.name,
product: product
}
await ctx.render('product')
}
})
}
My intuition tells me it is something to do with async and await, but I couldn't find out exactly what went wrong.
edit:
In the website below, it said "So CSS doesn't get sent along with the pages in a dynamic site like this. The HTML gets sent to the client, and then the client requests the files from the server. So what I did was I added a router to the CSS in my app.js" & "But on a dynamic site, when a request is made, and a response is sent, the response is sent to the '/', and NOT to the subfolder that's on the server side (at least by default). There is no subfolder on the client's side. Responses are simply just sent to the '/' by default. So make sure that the HTML is referencing the client side's files, and not the server side's files!"
But if I have lots of css files to reference to, then that means I have to manually reference to those files again? that sounds quite counter intuitive as Koa Js is supposed to be more convenient!
https://teamtreehouse.com/community/cant-get-the-css-to-load-in-the-nodejs-server
i followed the sample of authorized-https-endpoint and only added console.log to print the req.cookies, the problem is the cookies are always empty {} I set the cookies using client JS calls and they do save but from some reason, I can't get them on the server side.
here is the full code of index.js, it's exactly the same as the sample:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();
const validateFirebaseIdToken = (req, res, next) => {
console.log(req.cookies); //// <----- issue this is empty {} why??
next();
};
app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
res.send(`Hello!!`);
});
exports.app = functions.https.onRequest(app);
store cookie:
curl http://FUNCTION_URL/hello --cookie "__session=bar" // req.cookies =
{__session: bar}
doesn't store:
curl http://FUNCTION_URL/hello --cookie "foo=bar" // req.cookies =
{}
If you are using Firebase Hosting + Cloud Functions, __session is the only cookie you can store, by design. This is necessary for us to be able to efficiently cache content on the CDN -- we strip all cookies from the request other than __session. This should be documented but doesn't appear to be (oops!). We'll update documentation to reflect this limitation.
Also, you need to set Cache-Control Header as private
res.setHeader('Cache-Control', 'private');
Wow this cost me 2 days of debugging. It is documented (under Hosting > Serve dynamic content and host microservices > Manage cache behavior, but not in a place that I found to be useful -- it is at the very bottom "Using Cookies"). The sample code on Manage Session Cookies they provide uses the cookie name session instead of __session which, in my case, is what caused this problem for me.
Not sure if this is specific to Express.js served via cloud functions only, but that was my use case. The most frustrating part was that when testing locally using firebase serve caching doesn't factor in so it worked just fine.
Instead of trying req.cookies, use req.headers.cookie. You will have to handle the cookie string manually, but at least you don't need to implement express cookie parser, if that's a problem to you.
Is the above answer and naming convention still valid? I can't seem to pass any cookie, to include a session cookie named "__session", to a cloud function.
I setup a simple test function, with the proper firebase rewrite rules:
export const test = functions.https.onRequest((request, response) => {
if (request.cookies) {
response.status(200).send(`cookies: ${request.cookies}`);
} else {
response.status(200).send('no cookies');
}
});
The function gets called every time I access https://www.xxxcustomdomainxxx.com/test, but request.cookies is always undefined and thus 'no cookies' is returned.
For example, the following always returns 'no cookies':
curl https://www.xxxcustomdomainxxx.com/test --cookie "__session=testing"
I get the same behavior using the browser, even after verifying a session cookie named __session was properly set via my authentication endpoint. Further, the link cited above (https://firebase.google.com/docs/hosting/functions#using_cookies) no longer specifies anything about cookies or naming conventions.