I'm using prerender-spa-plugin in my Vue Webpack Cli project. Like from the documentation i'm registering the Plugin in webpack.prod.conf.js like this
...
plugins: [
...
new PrerenderSpaPlugin(
path.join(__dirname, '../dist'),
['/', '/about', '/contact'],
{
captureAfterTime: 5000
}
)
]
I'm wondering if it would be possible to get the list of routes array via an axios call.
I tried the following without success:
var routes = axios.get('http://myapi.com/api').then(function (response) {
return response.map(function (response) {
return '/base/' + response.slug
})
})
plugins: [
...
new PrerenderSpaPlugin(
path.join(__dirname, '../dist'),
routes,
{
captureAfterTime: 5000
}
)
]
Since my Javascript knowledge is poor I'm not able to solve this. Thankfull for any hints.
Best regards
This currently won't work (or at least work reliably) because Webpack assumes your configuration is synchronous by default. To get around this is to use Webpack's support for asynchronous configuration and return a promise that is resolved after your route request.
If you are in an environment that supports async/await (node 8+) then it's as simple as exporting an async function. Otherwise, return a new Promise:
// webpack.conf.js
module.exports = async function () {
const response = await axios.get('http://myapi.com/api')
const routes = response.map((response) => {
return '/base/' + response.slug
})
return {
plugins: [
// ...
new PrerenderSpaPlugin(
path.join(__dirname, '../dist'),
routes,
{
captureAfterTime: 5000
}
)
]
}
}
If that isn't an option, you can always have a task that makes this request, writes it to a json file, and then require('./route-response.json') in your config.
I had a similar requirement - to get the list of routes from my API. I ended up creating a prebuild script - prebuild.js
const fs = require('fs')
const axios = require('axios')
// Fetch data from API
axios.get('http://myapi.com/api.php')
.then(function(response) {
const data = response.data
try {
// Save the route list to local file
fs.writeFileSync('./route-response.js', data)
} catch(err) {
console.log(err)
}
})
.catch(function(err) {
console.log(err)
})
My API sends the data of the route-response.js file, ready to be directly saved and consumed by npm. You could handle this formatting in Node instead. Sample format:
module.exports = {
theList: [
'/',
'/about',
'/contact',
'/login',
'/register'
]
}
The above file is fetched in webpack.prod.conf.js as below:
...
const routeList = require('./route-response.js')
...
const webpackConfig = merge(baseWebpackConfig, {
...
plugins: [
...
new PrerenderSPAPlugin({
staticDir: path.resolve(__dirname, '../dist'),
routes: routeList.theList,
renderer: new PuppeteerRenderer()
})
...
]
...
})
and finally, add the prebuild script to the package.json
prebuild: runs before the build step.
postbuild: runs after the build step.
Here's a sample
...
"scripts": {
"dev": "node build/dev-server.js",
"prebuild": "node build/prebuild.js",
"build": "node build/build.js"
},
"dependencies": {
...
This way, I only have to run npm run build
Related
According to the Nextjs documentation I can make 301 redirect adding the following code to next.config.js
But the problem is that I am using next-routes and the solution is not working.
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true,
},
]
},
}
But the problem that it's not working with next-routes.
I need to redirect users from 'contact-us' to '/new/contact-us' page
My file with routing:
routes
.add('index', '/', 'index')
.add('contact-us', '/new/contact-us')
and next.config.js file
const withImages = require('next-images');
module.exports = withImages({
webpack: config => {
if (config.resolve.alias) {
const aliases = {
app: 'src',
};
for (const [alias, value] of Object.entries(aliases)) {
config.resolve.alias[alias] = path.resolve(__dirname, value);
}
}
if (process.env.NODE_ENV !== 'production') {
}
return config;
},
});
Please help how can I make 301 redirect?
I'm building a website using VueJs and it works perfect with no errors on the localhost, and I can access it locally in the same computer using the IP address as well. I used to be able to open the website within my network to test it using my phone with the IP address using port 8080. For some reason I can't do that anymore, my phone seems to be able to connect correctly because the console shows the firebase development warning but that's about it. It doesn't seem to be rendering the actual website since it shows blank.
last thing I remember installing before this happened was persistedState and I did so using npm like all the other plugins I use. I don't think this is causing the issue but I thought it was worth mentioning.
this is my Vue.config file:
const path = require("path");
module.exports = {
publicPath:
process.env.NODE_ENV === "production" ? "/metronic/vue/demo3/" : "/",
configureWebpack: {
resolve: {
alias: {
// If using the runtime only build
vue$: "vue/dist/vue.runtime.esm.js" // 'vue/dist/vue.runtime.common.js' for webpack 1
// Or if using full build of Vue (runtime + compiler)
// vue$: 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
}
},
chainWebpack: config => {
config.module
.rule("eslint")
.use("eslint-loader")
.tap(options => {
options.configFile = path.resolve(__dirname, ".eslintrc.js");
return options;
});
},
css: {
loaderOptions: {
postcss: {
config: {
path: __dirname
}
},
scss: {
prependData: `#import "#/assets/sass/vendors/vue/vuetify/variables.scss";`
}
}
},
transpileDependencies: ["vuetify"]
};
this is my main.js (i skipped all the imports):
router.beforeEach(async (to, from, next) => {
console.log("router");
const user = await authf.currentUser;
(user) ? await store.dispatch("fetchUserProfile", user) : "";
const requiresAuth = await to.matched.some(record => record.meta.requiresAuth);
const isAuthenticated = await store.state.userProfile;
if (isAuthenticated.email != null) {
(requiresAuth) ? next() : next("/Dashboard");
} else {
(requiresAuth) ? next("/Login") : next();
}
// reset config to initial state
store.dispatch(RESET_LAYOUT_CONFIG);
// Scroll page to top on every route change
setTimeout(() => {
window.scrollTo(0, 0);
}, 100);
});
let app;
authf.onAuthStateChanged(async(user) => {
if (user) {
await store.dispatch('fetchUserProfile', user)
}
});
new Vue({
router,
store,
i18n,
vuetify,
firebase,
axios,
render: h => h(App)
}).$mount("#application");
when I do npm run serve it gives me the local access through localhost:8080 and the network access through 192.168.1.137:8080 as it always has. I have access with no console errors in my computer, but the app wont render when accessing through network.
I am using a express custom server with next.js. Custom Server
I added the next app to my router under the path /custom/uibackend/next
const next = require('next')
const app = next({ dev: true, dir: './', conf: { } })
const handle = app.getRequestHandler()
module.exports = router => {
router.all('*', (req, res) => handle(req, res))
}
If i run my app and access the route in the browser it loads for a while and gives a empty response back.
There is just this one log i the console:
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
If i build the app and set dev to false it works as expected and i can access the route and the pages.
const app = next({ dev: false, dir: './', conf: { } })
I used the following next.config.js file:
module.exports = {
basePath: '/custom/uibackend/next'
}
My /pages folder is in the root.
Thank you.
I have found the error.
I forgot to call app.prepare()
app.prepare().then(() => {
// add handle to router/app after app.prepare has finished
})
I am converting my Nuxt application to SSR - because I want to use nuxtServerInit and asyncData. These are the steps I have taken to convert it.
Remove ssr: false from nuxt.config.js
Dispatch actions to initialize store's state in nuxtServerInit inside store/index.js
Now my nuxt.config.js looks like this
require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` });
export default {
router: {
base: "/app/",
},
target: "static",
head: {
// Some head, meta, link config
},
css: ["#/assets/scss/main.scss"],
styleResources: {
scss: ["#/assets/scss/*.scss", "#/assets/scss/main.scss"],
},
plugins: ["#/plugins/apiFactory.js"],
components: true,
buildModules: [
"#nuxtjs/eslint-module",
["#nuxtjs/dotenv", { filename: `.env.${process.env.NODE_ENV}` }],
],
modules: [
"bootstrap-vue/nuxt",
"#nuxtjs/style-resources",
["nuxt-sass-resources-loader", "#/assets/scss/main.scss"],
],
build: {
splitChunks: {
layouts: true,
},
},
};
And the store/index.js looks like this.
import axios from "axios";
export const state = () => ({
data: [],
});
export const mutations = {
setData(state, data) {
state.data = data;
},
};
export const actions = {
async nuxtServerInit({ dispatch }) {
// Before converting to SSR this action was dispatched in page/component that need this data
await dispatch("fetchData");
},
async fetchData({ commit }) {
const { data } = await axios.get("http://localhost:3030/my/api/path");
commit("setData", data);
},
};
export const getters = { /* some getters */ };
But after I restarted the development server - I was greeted with connect ECONNREFUSED 127.0.0.1:3030
These are the steps I've taken after that
Check if the API on localhost:3030 is running and accessible - It's running and accessible via direct URL and Postman
Comment out the // await dispatch("fetchData"); in nuxtServerInit - restarted the dev server - site is accessible again but without initial data.
So, I suspected that the action dispatched in nuxtServerInit cause the problem - If it is how do I fix this problem or where should I look into next? Please let me know, Thanks!
Additional Information
The API on localhost:3030 is Lumen version 7.2.2
The application will be deployed on shared hosting
The main reason is that nuxt can't connect to your host from where it resides.
In my case it is docker container where 127.0.0.1 is... the container! So you have to change baseUrl to actually accessible server from that container.
const axiosPlugin: Plugin = ({ $axios, isDev }) => {
if (isDev && process.server) $axios.setBaseURL('http://172.22.0.1:3000/api')
}
If you have your own server add your api domain in hosts file (in linux /etc/hosts)
127.0.0.1 api.domain.com
I was struggling for 2 days to understand why it wasn`t working and then it hit me pm2 server side has access only locally.
this happend becuse your axios baseURL port at nuxt.config.js not same with port of command yarn run dev
example right config of axios:-
1-
nuxt.config.js
axios: {
baseURL: 'http://localhost:5000',
},
then start server command must have --port with same axios port
yarn run dev --port 5000
Before, when I made apps with create-react-app, I would have a setupProxy.js file that would route API requests similar to this
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use('/api',
proxy({
target: 'http://localhost:8000',
changeOrigin: true,
})
);
};
But that doesn't seem to work with next.js. When I do the same thing, I get various errors.
Googling a solution, a lot say to use a custom server of some kind. But how would I accomplish a proxy like above using the default nextjs dev server? (Equivalent of npm run dev when dev in my package.json is next dev.
There is now an official feature for this in the config: Rewrites
Besides normal path rewrites, they can also proxy requests to another webserver
next.config.js:
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://localhost:8000/:path*' // Proxy to Backend
}
]
}
}
See Next.js Docs Rewrites
My server.js set up, hope it helps:
import express from 'express';
import next from 'next';
import proxy from 'http-proxy-middleware';
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use(
'/api',
proxy({
target: process.env.API_HOST,
changeOrigin: true,
}),
);
server.all('*', (req, res) => handle(req, res));
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
package.json:
"scripts": {
"dev": "NODE_ENV=development node -r esm server.js",
"build": "NODE_ENV=production next build",
"start": "NODE_ENV=production node -r esm server.js",
},
Another solution with catch-all routes + http-proxy-middleware:
// pages/api/proxy/[...slug].js
import { createProxyMiddleware } from "http-proxy-middleware"; // #2.0.6
const proxy = createProxyMiddleware({
target: process.env.BACKEND_URL,
secure: false,
pathRewrite: { "^/api/proxy": "" }, // remove `/api/proxy` prefix
});
export default function handler(req, res) {
proxy(req, res, (err) => {
if (err) {
throw err;
}
throw new Error(
`Request '${req.url}' is not proxied! We should never reach here!`
);
});
}
see: https://stackoverflow.com/a/72310680
Rewrites didn't work for me, and neither did using axios config.proxy.
I've resorted to a good old constant:
const SERVER_URL =
process.env.NODE_ENV === 'production' ? 'https://produrl.com : 'http://localhost:8000';
export async function getStaticProps() {
const data = axios.get(`${SERVER_URL}/api/my-route`)
// ...
}
I would much rather proxy requests and keep my requests cleaner, but I don't have a day to spend wrestling with this.
Maybe this very simple setup will help others.