Closed alexdilley closed 6 years ago
I have a PR here that you can look at, but one other note is that I wouldn't recommend keeping the API at a subdomain (e.g. https://api.example.com
). When it's expected to be at the same domain as the frontend, then you have a convention that will automatically use the correct API per environment (e.g. the API for example.com
will be at example.com/api
and the API for staging.example.com
will be at staging.example.com/api
. Then when you make an API request, you can use root-relative URLs like /api/version/specific-endpoint
in the source and it will always point to the correct API.
Does that make sense?
Oh - and in cases where you might want to point to the production API from another domain, such as in a hypothetical beta.example.com
, you could hypothetically use Webpack's DefinePlugin
to manually replace all instances of /api/
with https://example.com/api/
, but that feels a little fragile to me so I instead prefer to use something like nginx on the backend to proxy all API calls on beta.example.com
to the production API.
When it's expected to be at the same domain as the frontend, then you have a convention that will automatically use the correct API per environment (e.g. the API for example.com will be at example.com/api and the API for staging.example.com will be at staging.example.com/api. Then when you make an API request, you can use root-relative URLs like /api/version/specific-endpoint in the source and it will always point to the correct API.
If the API is hosted on a different server to that of the frontend then that can only be achieved via proxying such requests, though? If the frontend is distributed via a CDN then the API can be geographically disparate. When I tested this set up in the past I found API response times to be significantly increased (10-fold with my particular test case because of the extra hops), hence I opted for a more painful CORS-supported solution to retain snappy API responses. But thinking about it this is cheating since I'm relying on my geographic location.
So taking onboard your advice (I like the simplicity of your suggestion, obviously!) I revisited what I'd done before using, as a test, Netlify (to distribute the front-end) and a Heroku instance at a region most local to me (for the API). Sure enough, API response times were more inline with what I'd expect. I can only think that maybe the CDN hadn't yet propagated to a more local node when I tried this solution previously...or that Netlify is just simply a more distributed/appropriate CDN given my location (and the location of the API).
For reference, this is what I did (in case there's anything in it you may consider good practice and a possible candidate for the project):
// src/utils/http.js
import axios from 'axios';
// Add custom configuration here.
const axiosInstance = axios.create({});
export default axiosInstance;
// src/utils/__mocks__/http.js
// I haven't found this necessary since the `testURL` setting in `jest.config.js`
// caters for what `tests/unit/__mocks__/axios.js` looks to accommodate.
// somewhere in code...
import http from '@utils/http';
...
http.get('/session');
For development/test...
# .env.local (why .env.development.local doesn't work, I'm not sure)
API_BASE_URL=http://localhost:3000
// vue.config.js
devServer: {
...(process.env.API_BASE_URL
? // Proxy API endpoints to the local/live base URL.
{
proxy: {
'/api': {
pathRewrite: { '^/api': '' },
target: process.env.API_BASE_URL,
},
},
}
: // Proxy API endpoints to a local mock API.
{ before: require('./tests/mock-api') }),
},
For production...
Netlify uses a _redirects
file to rewrite paths:
# proxy API requests
/api/* https://api.example.com/:splat 200
# support history pushState (SPA)
/* /index.html 200
Anyway...long-winded response that doesn't totally relate to the issue discussed! For one, the above approach circumvents the issue discussed (since it doesn't require any environment variables to be set). But I'm not sure whether what I raised as an issue is a red herring anyway. Given the VUE_APP_
prefix thing is documented by vue-cli, should we deem this concern outside of the remit of this project and hence I should close this issue? wrt the PR, I think the refactoring of API_BASE_URL
to VUE_APP_API_BASE_URL
is therefore unnecessary given it's intention is for dev/test. The rest of the PR LGTM.
Just to wrap this issue in a bow, I think proxying /api
to api.example.com
is mostly outside of scope for this project. We do link to documentation that can help people figure out how to do this, but that's probably as far as I want to take it.
I found a neat way of doing this, by using Nginx for production build. The vue code can be exactly the same no matter development or production, no axios custom instance, no if else. Hopefully can help someone.
nginx.conf file:
......
http {
......
server {
location / {
root /YOUR_VUE_DIST_FOLDER
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/* {
proxy_pass http://YOUR_API_ENDPOINT;
}
......
}
......
}
The trick here is to use 2 location
blocks inside 1 server
block. Nginx is able to automatically match axios api calls to the 2nd location
block, while still routing requests coming from the outside to the 1st location
block (your static /dist folder).
As per vue-cli docs, environment-specific variables are configured separately from code – a la the "twelve-factor app" methodology – in
.env
files. Only environment variables that start withVUE_APP_
will be statically embedded into the client bundle. So for a production build, you'd presumably have something like:And in code use a pattern similar to the following:
However, this doesn't seem to play well with this project's use of Axios in tests: the Jest-backed mock,
test/unit/__mocks__/axios.js
, will be used when an equivalently named module is imported, i.e.import axios from 'axios'
. But we're obviously not importing the node module directly like this in code now, so you end up with an error such as:_axios.default.create is not a function
. The project is also not callingvue-cli-service test
but thejest
command directly, so variables from relevant.env
files are not being installed because the mode is not set totest
– but with that you could hack something akin to:And remove
test/unit/__mocks__/axios.js
. T'is a bit nasty though.Therefore, what would be a "best practice" solution to this?
If I remove
test/unit/__mocks__/axios.js
and replace with the following, then this is working for me currently both in testing and development, as well as for production builds:This still feels a bit icky, though. Hence I'm wondering if I'm missing something obvious/slicker.