Axios calls not working with localhost in production - node.js

My title states my problem, but I'm actually looking for a best practice. In the case of my current project I'm using a vue.js app to call out to a nodejs server I have running on the same box. In development I just hardcoded http://localhost/api/etc.... into my axios calls and figured since I was deploying both in production to live on the same box too, that would be fine, but once I deployed I started getting 404s for the axios calls. I had to refactor my code to use the actual dns name of the server.
It works now because of that, but I feel like I'm missing something. I'm not sure if it's a node (for api server) or apache (hosting frontend) issue.
What's the best way to deal with urls?

Usually people create a module which exports a custom axios instance with a baseUrl. I think that it is a good practice because you can call the endpoints with the relative url only and centralize the api url in a single place as well, making it easier to switch between development and production urls.
my-api-client.js
import axios from 'axios';
export const MyApiClient = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
app.js
import { MyApiClient } from './my-api.js';
MyApiClient.get('my-collection').then(...
If your javascript version doesn't support import and export statements, you can use require and module.exports respectively.

Related

Running two NestJS applications in the same process

I've been working with Express for a couple of years now and recently got introduced to NestJS. I decided to experiment and write an application on this, as it seemed to me, interesting framework.
I want my application to be able to handle the following routes:
General routes
/policy, /docs
API routes (with versioning)
/api/v1/users, /api/v1/chats
In Express, this is very easy to do, but as I understand it, in NestJS everything is different. As I understand from the documentation, in NestJS you cannot set the path prefix for the entire module. You can only set a global prefix for the entire application.
The documentation also talked about the RouterModule. But I didn’t like it, because it won’t be possible to enable versioning specifically for routes /api and the use of RouterModule itself is very inconvenient. As the application grows, using the RouterModule becomes a pain.
Then I tried to convert the application to a monorepository. It seems to be possible to achieve the desired functionality, but it seems inconvenient for me to run each part of the application separately. Perhaps it would be worth using concurrently, but in my opinion, this is also not the best option.
As a result, I came to the following solution.
The structure of my project at the moment looks like this:
project structure
The api directory will contain all modules related to the api (it will process /api/v1/... routes), and everything else will be in the core (it will process /docs, /policy routes, etc.).
In fact, everything is the same as when creating a monorepository, but I decided to run all this from a single main.ts
main.ts
And I have a question, what are the pitfalls of this solution? Will it affect performance? Are there any better options for solving my problem?
To enable versioning with NestJS :
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI,
});
await app.listen(3000);
Then, it's true that every routes will need version in URL: /v1/...
But you can use VERSION_NEUTRAL to mark default endpoint version :
Some controllers or routes may not care about the version and would
have the same functionality regardless of the version. To accommodate
this, the version can be set to VERSION_NEUTRAL symbol.
An incoming request will be mapped to a VERSION_NEUTRAL controller or
route regardless of the version sent in the request in addition to if
the request does not contain a version at all.
So, for example, here, route /v1/users for Users V1 only :
#Controller({
version: '1',
})
export class UsersControllerV1 {
#Get()
getUsers()...
}
and another route (controller) without any version :
#Controller({
version: VERSION_NEUTRAL,
})
export class PolicyController {
#Get()
getPolicy()...
}
Note that it's possible to provide more than one unique version :
version: ['1', VERSION_NEUTRAL]
More details on official docs.

Conditionally use next.js app or create react app with the same website domain according to users login

I have a website which I've built with CRA, node.js and I want to use ISR for most of the pages.
The problem is that about 20% of the users are registered users, which get their own content, and different header, which means I can only use SSR, and not ISR.
My thought is to use something like:
In my node.js server I would check if the user is logged
If he is not logged, I would send a get request to the next.js server, get the static html file and serve it.
If the user is logged I would just send him my CRA app.
Another option that I thought about is to use a proxy server with filter on the request which check if the session ID or cookie ID is set
Is it possible? Which option is better?
Shall I be able to use CDN to serve those static files?
Is there any better idea to solve this problem?
Just keep everything in the Nextjs application.
If you need static generated pages use getStaticProps and getStaticPaths or nothing (to have the same result as a CRA app) in that page.
If you need some server related logic use getServerSideProps in that page.
UPDATE
After run next build
The page test is a simple component
const Test = () => <div>test</div>;
export default Test;
In the other pages (/gpp, /gpp/[id]) getServerSideProps is defined
.....
const GppPage: NextPage = () => (
<>
<Head>
<title>GPP</title>
</Head>
<Box>
....
</Box>
</>
);
export async function getServerSideProps(context) {
return {
props: {
session: "mysession"
}
};
}
export default GppPage;
In the image the Test page is clearly a static page (look at symbols)
If you define in _app.tsx some getInitialProps or getServerSideProps in that case you will inherit the SSR behaviour
Instead of using next.js I ended up creating my own ISR for my website. This way I could use SSR for registered users and ISR for unregistered users. While using SSR in CRA app is not recommended by the create react app team, I found out that it is pretty simple with the help of this article for SSR, this article for SSR with react router. I used react 18 and react router 5.
While this solution worked for me, it is not guaranteed that it will work later so I won't recommend it until the create react app team will recommend it.
The pros of the solution:
More freedom with the code. For example, I could serve different pages for mobile and desktop (and still serve static pages).
Reduced costs compared to next.js
The cons of the solution
Unstable because the create react team doesn't recommend it
Missing out of some of the next.js features like images optimization
The html pages are not served from CDN (I used EBS)

node Vue.js different code scenario if run in dev mode

I have Vue.JS front app with nodeJS backend based on expressJS. ExpressJS also used as web server for statically built Vue.JS app
Front app communicates with express backend via rest and websocket. It uses url host from window.location instance and easily communicates with backend
In production mode, when built application in static expressJS server area, everything work perfect
In dev mode, Vue use it's own web server, and backend urls based on window.location are incorrect because no expresJS on same host and port.
So my question is it possible change some code blocks if running in dev mode ?
Like something this :
if( devmode)
{
const url = "http://somebackendhost/rest"
}
else {
const url = location.host ....
}
}
I will assume you are developing your Vue app using Vue CLI
Changing app behavior depending on environment
In Vue CLI you can use Environment Variables
if(process.env.NODE_ENV === "development")
{
}
This works thanks to Webpack's Define plugin and big advantage is that process.env.NODE_ENV is replaced at build time by the real value. So in production build Webpack will see just if("production" === "development") {} and happily removes the code in optimization phase because it knows this can never be true
Better solution
But I would not use this approach for your problem. Using different API server (not same as the server used for serving Vue SPA) can easily lead to CORS problems
Exactly for this use case, Vue CLI (and Webpack Dev server used under the hood) supports proxying
vue.config.js
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'http://localhost:58300/',
ws: true, // websockets
changeOrigin: true,
}
}
},
},
This config makes Vue Dev server to proxy any request to /api to other server running at http://localhost:58300/ (your node/express app) and change the origin (so browser thinks response came from the dev server)
All of this can be done without Vue CLI but you will need to set it up by yourself in Webpack config...
The problem
You can't access this information from your browser.
But there are three solutions:
Solution #1
On compilation time create a variable in code which defines devmode (const devmode = true;)
Solution #2
Because your bundler can minify your variable names or changing the scope for security reasons, may be the situation where you can't access it.
So second solution is to define devmode in your localStorage.
Solution #3
Third solution is almost the best.
If you are developing, you are probably accessing your web app via localhost.
location.hostname will return the name of host, so you can make something like:
const devmode = location.hotname == 'localhost';
Best solution
Do not do this. Develop a fully working web app using local REST API and define the URL of REST API in some variable, so when you are preparing your production app, you or compiler just changes the URL adress variable in code of your REST API.
Why is this the best solution?
Because it do not impacts your end-user's performance and they will be loading less code, which is the best practise.
Post Scriptum
Don't forget to remove all devmode codepaths when compiling production version!

Heroku Deployment MEVN Stack

I've deployed my MEVN stack app to Heroku and can access it from my development machine. I can add and delete items on the mlab mongodb I have setup. If I try to access from my phone or another machine the website works but I can't see the data. I have searched and can't find anything related to this. I am afraid it is obvious but I don't see it. Below is the website link.
https://dry-earth-62210.herokuapp.com/#/users
import axios from 'axios'
export const http = axios.create({
baseURL: 'http://localhost:8080/api', //base URL goes here
})
As baseUrl, use 'https://dry-earth-62210.herokuapp.com/api' instead of 'http://localhost:8080/api'. You are good to go.
Right now you are trying to invoke local server from Heroku app and your local server is not available to the Heroku app.
Your API request code should be,
import axios from 'axios'
export const http = axios.create({
baseURL: 'https://dry-earth-62210.herokuapp.com/api', //base URL goes here
});
Also, make sure, your CORS is enabled for all sites or well configured. Otherwise, there might be some Cross-Origin Error issue.
Look at your API call - if you look at the request URL, it's pinging localhost:8080. You'll need to change this from the hardcoded localhost:8080 value to dynamically get the server's address. (This is probably where you're doing app.listen).
Feel free to post your main index.js file and I can take a closer look :)

Why is config.proxy ignored when making an axios request within a webpack project?

My goal
I want to perform a request with axios#0.18.0 using an http proxy fully efficient (squid). My project is a vue project based on the webpack template. vue init webpack proxytest
The issue
When I try to perform the request, axios 'ignores' the proxy property inside the config object passed.
I noticed that when I run the exact same code with pure nodejs, everything works just perfectly fine.
Is there some configuration that I need to specify excepting the axios request configuration when using axios as a npm module within webpack ?
The code
import axios from 'axios';
const config = {
proxy: {
host: 'host',
port: 3128,
},
method: 'GET',
};
axios('http://www.bbc.com/', config).then((res) => {
console.log(res);
}).catch((err) => {
console.error(err);
});
Of course, when testing, I change 'host' into the proxy IP.
I tried to change the method property to POST in order to check if axios considered the config. It does consider the config passed.
I tried to put a fake port so I could check if the proxy property was considered. It's not considered.
The output
output...
Now, I'm aware of what CORS is. The point is I'm constently getting this output when performing the requests. And if the proxy was used by axios, I think no CORS "error" would show up as my proxy is a VPS.
Thank you.
You need to configure your server to receive the requests and then test. This does not seem to have anything to do with the webpack, because in your mistake, for example, you make a request for the BBC from localhost and it is very likely that you are making that mistake. So it's important to test with your server by running Front and Back locally.

Resources