I would like my nextjs13 app to serve content based on the subdomain / domain / slug / get_vars in the request.
I need to have access to the request data from within the new appfolder of nextjs 13, so that I can serve the right data on the server side.
e.g., sub-domain.my-domain.com/my-slug?my_var=1&another_var=2
How can I access the following data:
subdomain
domain
slug (this I can achieve using a folder wrapped in [])
get vars
Many thanks to any responders.
Accessing domain & subdomain using headers function:
import { headers } from 'next/headers'
const Page = () => {
const headersInstance = headers()
const [subdomain, domain] = headersInstance.get('host').split('.')
...
}
export default Page
Accessing slug through params prop:
const Page = ({ params: { slug } }) => {
console.log(slug)
...
}
export default Page
Accessing query parameters through searchParams prop (server-components):
Given sub-domain.my-domain.com/my-slug?my_var=1&another_var=2:
const Page = ({ searchParams }) => {
console.log(searchParams) // Logs { my_var: '1', another_var: '2' }
console.log(searchParams.my_var) // Logs 1
console.log(searchParams.another_var) // Logs 2
...
}
export default Page
NextJS 13 has a new router API for params.
You can check the client component documentation for the following methods as they also give examples on how to use it for server side purposes:
useSearchParams
useSelectedLayoutSegment
useSelectedLayoutSegments
Note that everything should be imported from 'next/navigation' (it was from 'next/router' before NextJS 13).
Regarding the domain and subdomain maybe the 'next/headers' documentation can help you.
Related
I have added a custom domain to the API Gateway due to CORS/Cookies/Infosec and other fun reasons.
I then added the following code to hack the correct domain into my Amplify configuration:
import { Amplify, API } from "aws-amplify"
const myfunc () => {
const amplifyEndpoint = API._restApi._options.endpoints[0]
Amplify.configure({
aws_cloud_logic_custom: [
{
...amplifyEndpoint,
endpoint: process.env.REACT_APP_API_URL || amplifyEndpoint.endpoint,
},
]
})
const response = await API.post("MyApiNameHere", "/some-endpoint", {data:"here"})
}
This works but a) is this really the correct way to do it? and b) I'm seeing a weird issue whereby the first API.post request of the day from a user is missing the authorization & x-amz-security-token headers that I expect Amplify to be magically providing. If a user refreshes the page, the headers are sent and everything works as expected.
[edit] turns out my missing headers issue is unrelated to this override, so still need to get to the bottom of that!
The more correct place looks to be in the index.js:
import { Amplify, API } from "aws-amplify"
import awsExports from "./aws-exports"
Amplify.configure(awsExports)
const amplifyEndpoint = API._restApi._options.endpoints[0]
Amplify.configure({
aws_cloud_logic_custom: [
{
...amplifyEndpoint,
endpoint: process.env.REACT_APP_API_URL || amplifyEndpoint.endpoint,
},
]
})
That way it's done once - rather than needing to be done in each API call.
Naturally you would need some more complicated logic if you were dealing with multiple endpoints.
I'm building an app with NextAuth's email provider as the sole means of user registration and login.
In local development everything worked fine, but when deploying the app to Vercel I keep getting Unhandled Promise Rejection errors from Mongo, so I decided to switch to NextAuth's unstable_getServerSession and everything is running very smoothly now.
Before making the switch I used an extra parameter to communicate whether the user was just logging in or newly signing up. Based on this, I sent different emails (a welcome email with login link vs. a shorter message with just the login link).
Here is how the [...nextauth].js file looked before:
export default async function auth(req, res) {
const signup = req.body.signup || false
return await NextAuth(req, res, {
adapter: MongoDBAdapter(clientPromise),
...
providers: [
EmailProvider({
...
sendVerificationRequest(server) {
customLogin(
server.identifier,
server.url,
server.provider, // passing the above data for email content
signup // custom parameter to decide which email to send
)
}
})
],
...
})
}
I adjusted the code sample from the documentation, but wasn't able to pass a custom parameter to it.
Here is my new code:
export const authOptions = {
adapter: MongoDBAdapter(promise),
...
providers: [
EmailProvider({
...
sendVerificationRequest(server) {
customLogin(
server.identifier,
server.url,
server.provider,
)
}
})
],
...
}
export default NextAuth(authOptions)
The customLogin function doesn't do anything except construction the different email options.
Among the things I have tried are wrapping authOptions in a handler function with req and res, setting up a separate API route, and passing parameters to the NextAuth function, but none of those worked.
Any advise on how to implement a custom parameter here would be highly appreciated.
Project:
I am working on an E-commerce application and it has more than 1,600 products and 156 categories.
Problem:
Initially, on the first product page, 30 products will be fetched (due to the page limitation), but on the left sidebar, I need filters that will be decided on the basis of tags of all 1,600 products. So that's why I need all the products in the first fetch and then I will extract common tags by looping over all the products and immediately show them on the sidebar.
What do I want?
I am not sure but I think it would be the best solution if I generate a JSON file containing all the products and store it somewhere, where I can fetch just hitting the URL using REST API in Next JS (either in getServerSideProps or getStaticProps).
Caveat:
I tried by storing JSON file in ./public directory in next js application, it worked in localhost but not in vercel.
Here is the code I wrote for storing JSON file in ./public directory:
fs.writeFileSync("./public/products.json", JSON.stringify(products, null, 2)); //all 1,600 products
One solution it to fetch it directly from front-end (if the file is not too big) otherwise, for reading the file in getServerSideProps you will need a custom webpack configuration.
//next.config.js
const path = require("path")
const CopyPlugin = require("copy-webpack-plugin")
module.exports = {
target: "serverless",
future: {
webpack5: true,
},
webpack: function (config, { dev, isServer }) {
// Fixes npm packages that depend on `fs` module
if (!isServer) {
config.resolve.fallback.fs = false
}
// copy files you're interested in
if (!dev) {
config.plugins.push(
new CopyPlugin({
patterns: [{ from: "content", to: "content" }],
})
)
}
return config
},
}
Then you can create a utility function to get the file:
export async function getStaticFile(file) {
let basePath = process.cwd()
if (process.env.NODE_ENV === "production") {
basePath = path.join(process.cwd(), ".next/server/chunks")
}
const filePath = path.join(basePath, `file`)
const fileContent = await fs.readFile(filePath, "utf8")
return fileContent
}
There is an open issue regarding this:
Next.js API routes (and pages) should support reading files
I tried to find how to change the default page path (when you access the server on the "/" path) but couldn't find anything. I have a structure in the pages directory in the form index/index.jsx and I want this page to be returned by default when accessing the server. I did not find a similar question on this forum, maybe someone will need your help, except me.
The way pages structure works in Next.js is very opiniated. If you need to send a file from a different pages structure when accessing / you would need to configure your own little express server.
See this link for official doc.
You would then have something like :
// Some code
if (pathname === '/') {
app.render(req, res, '/index', query) // Or /index/index.jsx
}
// Some code
Also if you do not want to create your own express server, you could have an index.js that redirects to your index page with something like this :
import Router from 'next/router'
const Index = () => null
Index.getInitialProps = async ({ res }) => {
if (res) {
res.writeHead(302, {
Location: `/index`
})
res.end()
}
else
Router.push(`/index`)
return {}
}
export default Index
I'm working with nextjs and this example https://github.com/zeit/next.js/tree/master/examples/with-static-export
in next.config.js i have code:
module.exports = {
async exportPathMap(defaultPathMap, { dev, dir, outDir, distDir, buildId, incremental }) {
// we fetch our list of posts, this allow us to dynamically generate the exported pages
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=3'
)
const postList = await response.json()
// tranform the list of posts into a map of pages with the pathname `/post/:id`
const pages = postList.reduce(
(pages, post) =>
Object.assign({}, pages, {
[`/post/${post.id}`]: { page: '/post/[id]' },
}),
{}
)
// combine the map of post pages with the home
return Object.assign({}, pages, {
'/': { page: '/' },
})
},
}
Its fetch 3 posts and generate files - [id].html - its great!
But now i need to fetch new post and build page only for this new post but commad next export remove all files from out and create only one post.
What i need to do to keep old post and add new one on next export?
Example:
First next export with request for 3 posts from api
generate 3 post in folder "out"
change api url and run next export for 1 new post
summary i have 3 old post pages and 1 new in my "out" directory
How to do that?
Next can't do this out of the box, but you can set it up to do so. First, you'll need a system (database) of which pages have already been built. Second, you'll need some method of communicating with that database (api) to ask which pages should be built (eg, send over a list of pages and the api responds telling you which ones have not yet bene built). Then, tell your exportPathMap which pages to build. And finally, move your built pages out of out and into a new final/public directory.
By default, Next will build/export anything in the pages directory plus anything you set in exportPathMap, and put all of those in the out directory. You can override what it builds by passing a custom exportPathMap, and how you handle what goes into the out directory is up to you, so you can move those files to a different actual public directory and merge them with the old files.