How can I fix nuxt js static site links leading to network error? - vue.js

I have a very basic nuxt.js application using JSON in a local db.json file, for some reason the generated static site links leading to network error, but I can access them from the url or page refresh.
nuxt config
generate: {
routes () {
return axios.get('http://localhost:3000/projects')
.then((res) => {
return res.data.map((project) => {
return '/project/' + project.id
})
})
}
},
main root index page
data() {
return {
projects: []
}
},
async asyncData({$axios}){
let projects = await $axios.$get('http://localhost:3000/projects')
return {projects}
}
single project page
data() {
return {
id: this.$route.params.id
}
},
async asyncData({params, $axios}){
let project = await $axios.$get(`http://localhost:3000/projects/${params.id}`)
return {project}
}
P.S. I have edited the post with the code for the main and single project page

Issues with server-side requests of your application are caused by conflicts of ports on which app and json-server are running.
By default, both nuxt.js and json-server run on localhost:3000 and requests inside asyncData of the app sometimes do not reach correct endpoint to fetch projects.
Please, check fixed branch of your project's fork.
To ensure issue is easily debuggable, it is important to separate ports of API mock server and app itself for dev, generate and start commands.
Note updated lines in nuxt.config.js:
const baseURL = process.env.API_BASE_URL || 'http://localhost:3000'
export default {
server: {
port: 3001,
host: '0.0.0.0'
},
modules: [
['#nuxtjs/axios', {
baseURL
}]
],
generate: {
async routes () {
return axios.get(`${baseURL}/projects`)
.then((res) => {
return res.data.map((project) => {
return '/project/' + project.id
})
})
}
}
}
This ensures that API configuration is set from a single source and, ideally, comes from environmental variable API_BASE_URL.
Also, app's default port has been changed to 3001, to avoid conflict with json-server.
asyncData hooks have been updated accordingly to pass only necessary path for a request. Also, try..catch blocks are pretty much required for asyncData and fetch hooks, to handle error correctly and access error specifics.

Related

Self Signed Cert error in Nuxt trying to generate static site locally

I'm building a Vue/Nuxt (2) site running against a .NET Web Api. This site is already deployed in a staging capacity and is building and running as a statically generated site on Netlify. Now, I know it's not quite right as my content is not being rendered into the deployed files so effectively it's running as a SPA. Not quite what I saw happening in Dev at the time 5 weeks ago but I didn't think anything of it, I'd fix it later.
I've finally got a chance to work on this project again and proceeded to make the necessary changes so the content should be fetched via my dynamic route builder in nuxt.config.js (existing) and output during build via the asyncData hook in my pages (new).
Nuxt.config
// Generate dynamic page routes
let dynamicRoutes = async () => {
console.log( `${ process.env.API_BASE_URL }/page/main/generate` );
const fetchedConditions = await axios.get( `${ process.env.API_BASE_URL }/page/main/generate` );
const routesForConditions = fetchedConditions.data.map( ( condition ) => {
return {
route: `/conditions/${ condition.id }/${ condition.urlPath }`,
payload: condition
}
} );
console.log( `${ process.env.API_BASE_URL }/faq/top/generate?count=10` );
const fetchedFaqs = await axios.get( `${ process.env.API_BASE_URL }/faq/top/generate?count=10` );
const routesForFaqs = fetchedFaqs.data.map( ( faq ) => {
return {
route: `/frequently-asked-questions/${ faq.categoryId }/${ faq.id }/${ faq.urlPath }`,
payload: faq
}
} );
const routes = [ ...routesForConditions, ...routesForFaqs ];
return routes;
}
export default {
target: 'static',
ssr: false,
generate: {
crawler: true,
routes: dynamicRoutes
},
server: {
port: 3001
}...
Condition page
async asyncData(ctx) {
util.debug('Async data call...');
if (ctx.payload) {
ctx.store.dispatch("pages/storePage", ctx.payload);
return { condition: ctx.payload };
} else {
const pageResponse = await ctx.store.dispatch('pages/getCurrentPage', { pageId: ctx.route.params.id });
return { condition: pageResponse };
}
}
So far so good except now, when I try to generate the site in development i.e. "npm run generate", the dynamic route generator code cannot reach my local API running as HTTPS and fails with a "Nuxt Fatal Error: self signed certificate".
https://localhost:5001/api/page/main/generate 12:06:43
ERROR self signed certificate 12:06:43
at TLSSocket.onConnectSecure (node:_tls_wrap:1530:34)
at TLSSocket.emit (node:events:390:28)
at TLSSocket._finishInit (node:_tls_wrap:944:8)
at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:725:12)
This worked 5 weeks ago and as far as I am aware I have not changed anything that should impact this. No software or packages have been updated (except windows updates perhaps). The API is still using .NET 5.0 and running on Kestrel using the default self signed cert on localhost (which is listed as valid in Windows). I simply added the payload to the routes, added the asyncData hook, and modified the page code accordingly.
I've Googled a few tidbits up but none have resolved the issue and now I'm at a loss. It really shouldn't be this blimmin opaque in 2022.
Tried disabling SSL via a proxy in nuxt.config;
proxy: {
'/api/': {
target: process.env.API_BASE_URL,
secure: !process.env.ENV === 'development'
}
}
Tried modifying my Axios plugin to ignore auth;
import https from 'https';
export default function ( { $axios } ) {
$axios.defaults.httpsAgent = new https.Agent( { rejectUnauthorized: false } );
}
And a variation of;
import https from 'https';
export default function ( { $axios, store } ) {
const agent = new https.Agent( {
rejectUnauthorized: false
} );
$axios.onRequest( config => {
if ( process.env.dev )
{
config.httpsAgent = agent;
}
} );
}
None of these 'worked for other people' solutions is working for me.
Also, the client side apps (public/admin) themselves have no problem working against my API locally, it's only the route builder within nuxt.config or asyncData code which is throwing this error.
Any suggestions would be appreciated. Happy to add other relevant code if needed, just not sure which atm.

Is it possible for Nuxt JS plugins to only run once?

I have several VueX actions (that run on the server only) and are dispatched from nuxtServerInit. They make HTTP requests to external services, which is slowing down the TTFB.
I would like to implement a cache plugin that can store and retrieve values from Redis. The aim is to avoid making the HTTP requests in actions on every request.
I started out by adding a line to the nuxt.js config file.
{ src: '~/plugins/cache', ssr: true, mode: 'server' },
I then created the following in resources/plugins/cache.js
import redis from 'redis';
export default ({ app }, inject) => {
console.log('Creating redis client');
inject('cache', redis.createClient({
//options removed for brevity
}));
}
I run the app and can see 'Creating redis client' is printed to the console on every page refresh. Is it possible to create a plugin that is instantiated when the server is started and the same instance is used for every request? Or if that is not possible, what is the best way to implement the cache?
As you want to share a data/instance, plugin is not the right place to do that because plugins are created (called) every time new Vue instance is created, which on server means on every request...
So you need something instantiated only once per server...and that's Nuxt module
modules/cacheModule.js
export default function (_moduleOptions) {
// any data you want to share between all requests
const data = {
message: `Hello from cache - ${new Date().toLocalTimeString()}`
};
this.nuxt.hook("vue-renderer:ssr:prepareContext", (ssrContext) => {
ssrContext.$cache = data;
});
}
And use it in server plugin or nuxtServerInit...
store/index.js
export const state = () => ({
cache: {}
});
export const mutations = {
setcache(state, payload) {
state.cache = payload;
}
};
export const actions = {
nuxtServerInit({ commit }, context) {
commit("setcache", context.ssrContext.$cache);
}
};
Demo
Same technique can be used for applying cacheAdapterEnhancer from axios-extensions package on server/client (or both) Axios instance so you can keep your original code (fetching in nuxtServerInit) - more details here

Env Variable in nuxt

I'm using the asyncData with axios to get a local.json file from my static folder. I just want to get it locally for the moment as i've added all my methods as i'm waiting for my API to be built.
To use async I need the full path and URL so I need an env variable, however I keep getting a 403 on my server or I get a random error. I need the path to be whatever the URL is hosted on in my axios call.
The URL needs to be dynamic because I'm using gitlab CI and the URL changes depending what branch i'm on, so I can't set a Dev, Prod URL
If I replace context.env.baseUrl with my localIP it works but I need the URL to be "my hosted URL". I need this to be a variable as i'm using gitlab with different URL's
Async Data
asyncData(context) {
return axios.get(context.env.baseUrl+'/products.json')
.then(response => {
return {
servers: response.data.products
}
})
.catch(e => context.error(e))
}
nuxt.config.js
env: {
// The baseUrl needs to be dynamic - whatever the server is on
baseUrl: process.env.BASE_URL || 'http://localhost:3000'
}
If you want to use static file present in same project then just import/require it instead of using axios. See example below
<script>
export default {
asyncData() {
const servers = require('#/static/local.json')
return {
servers
}
}
}
</script>
You can create an axois instance and set a base URL to avoid the headache.
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
});
See: https://github.com/axios/axios#axioscreateconfig
One of many ways that I know and the easiest is using axios module for nuxt. Many of the axios config pain points are addressed via this module instead of using standalone axios package.
Then in your nuxt.config.js
Add this like so
axios: {
baseURL: () => {
if (process.env.NODE_ENV !== "production") {
return 'localhost:5000/api/';
}
return https://www.example.com/api/;
}
}
USAGE IN NUXT PAGES
async asyncData({ $axios }) {
try {
let response = await $axios.get("/your-api-route");
return response;
} catch (err) {
console.log(err);
}
}

CORS axios Nuxt.js

I'm using an API with my Nuxt, here is my nuxt.config.js to allow the requests :
axios: {
prefix: '/api/',
proxy: true,
},
proxy: {
'/api/': {
target: process.env.API_URL || 'http://localhost:1337',
pathRewrite: {
'^/api/': '/',
}
},
}
On a specific page of my app, I need to send requests to another API from another domain. I'm using axios direclty in this Vue component :
axios.post('mysite.com/widget.rest')
As response, I obtain CORS error. Can I allow multiple API in my config ?
If you mean being able to access APIs under different URLs, AFAIK it's not possible out of the box. We tried adding proxy to different targets, but it only worked on client side, not during SSR.
What we ended up doing is having the default axios instance for our main API, and creating a plugin that creates extra axios instances for our other APIs for SSR.
Add extra "suburl" to proxy - this sorts out client side and means no CORS issues.
proxy: {
'/forum/': {
target: 'https://other.domain.com/',
pathRewrite: {'^/forum/': '/'}
},
'/api/': ...
},
For SSR to work, axios should hit directly the other API
import Vue from 'vue'
var axios = require('axios');
const forumAxios = axios.create(process.client ? {
baseURL: "/"
} : {
baseURL: 'https://other.domain.com/'
});
// this helps Webstorm with autocompletion, otherwise should not be needed
Vue.prototype.$forumAxios = forumAxios;
export default function (context, inject) {
if (process.server) {
forumAxios.interceptors.request.use(function (config) {
config.url = config.url.replace("/forum/", "");
return config;
}, function (error) {
return Promise.reject(error);
});
}
inject('forumAxios', forumAxios);
In components, you can then use something like:
async asyncData({app}) {
let x = await app.$forumAxios.get("/forum/blah.json");
You can use process.env prop of course instead of hardcoded URL.
This will hit https://other.domain.com/blah.json.

Nuxt custom module hooks not called

I want to pass some extra data from the ssr server that's present after the middleware has run, and use that on client side middleware. A bit similar to what nuxt already does with vuex.
Documentation at the render:context hook:
Every time a route is server-rendered and before render:route hook. Called before serializing Nuxt context into window.__NUXT__, useful to add some data that you can fetch on client-side.
Now my custom plugin defines some hooks as stated in the documentation, but not all seem to be called properly:
module.exports = function() {
this.nuxt.hook('render:route', (url, result, context) => {
console.log('This one is called on every server side rendering')
}
this.nuxt.hook('renderer', renderer => {
console.log('This is never called')
}
this.nuxt.hook('render:context', context => {
console.log('This is only called once, when it starts loading the module')
}
}
What am I doing wrong and how can I pass custom ssr data to the client side renderer?
Ok, just found the solution to the core problem of passing custom data from the (ssr) server to the client:
Create a plugin: plugins/my-plugin.js
export default ({ beforeNuxtRender, nuxtState }) => {
if (process.server) {
beforeNuxtRender(({ nuxtState }) => {
nuxtState.myCustomData = true
})
} else {
console.log('My cystom data on the client side:', nuxtState.myCustomData)
}
}
Then register the plugin in your nuxt.config.js:
module.exports = {
plugins: ['~/plugins/my-plugin']
}
Docs here.