Vite forwad POST request to GET - vite

Here is my vite config
server: {
host: "0.0.0.0",
proxy: {
"/api": {
target: "https://xxx.xxx.inc",
changeOrigin: true,
rewrite: (path) => {
return path.replace(/^\/api/, "");
},
secure: false,
},
},
},
When I post a data, weired things happend!
The POST was "301 Moved Permanently" And it became GET!
vite:proxy /api/core/login -> https://xxx.xxx.inc +0ms
vite:time 475.67ms /core/login +10s
vite:spa-fallback Not rewriting GET /core/login/ because the client prefers JSON. +10s
vite:time 0.73ms /core/login/ +7ms
vite:proxy /api/core/login -> https://xxx.xxx.inc +15m
vite:time 476.08ms /core/login +15m
vite:spa-fallback Not rewriting GET /core/login/ because the client prefers JSON. +15m
vite:time 0.66ms /core/login/ +5ms
For Proxy server,
A POST should be always POST after

Related

Astro: How to proxy service calls

I am setting up an Astro site which will display data fetched from a simple service running on the same host but a different port.
The service is a simple Express app.
server.js:
const express = require('express')
const app = express()
const port = 3010
const response = {
message: "hello"
}
app.get('/api/all', (_req, res) => {
res.send(JSON.stringify(response))
})
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
Since the service is running on port 3010, which is different from the Astro site, I configure a server proxy at the Vite level.
astro.config.mjs:
import { defineConfig } from 'astro/config';
import react from '#astrojs/react';
export default defineConfig({
integrations: [react()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
proxy: {
'/api/all': 'http://localhost:3010'
}
}
},
});
Here is where I am trying to invoke the service.
index.astro:
---
const response = await fetch('/api/all');
const data = await response.json();
console.log(data);
---
When I run yarn dev I get this console output:
Response {
size: 0,
[Symbol(Body internals)]: {
body: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
stream: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
boundary: null,
disturbed: false,
error: null
},
[Symbol(Response internals)]: {
type: 'default',
url: undefined,
status: 404,
statusText: '',
headers: { date: 'Tue, 02 Aug 2022 19:41:02 GMT' },
counter: undefined,
highWaterMark: undefined
}
}
It looks like the network request is returning a 404.
I'm not seeing in the doc much more about server configuration.
Am I going about this the right way?
I have this working correctly with a vanilla Vite app and the same config/setup.
How can I proxy local service calls for an Astro application?
Short Answer
You cannot proxy service calls with Astro but also you don't have to
For direct resolution answer see section functional test without proxy
Details
Astro does not forward the server.proxy config to Vite (unless you patch your own version of Astro), the Astro Vite server config can be seen empty
proxy: {
// add proxies here
},
reference https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L125
there is a merge of Astro server with Astro vite.server config but it does not take the proxy param. This is not obvious to get from the code, see tests later.
let result = commonConfig;
result = vite.mergeConfig(result, settings.config.vite || {});
result = vite.mergeConfig(result, commandConfig);
reference https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L167
Tests
Config tests
I tried all possible combinations of how to input config to Astro and in each location a different port number to show which one takes an override
a vite.config.js file on root with
export default {
server: {
port:6000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
in two locations in the root file astro.config.mjs
server
vite.server
export default defineConfig({
server:{
port: 3000,
proxy: {
'/api': 'http://localhost:4000'
}
},
integrations: [int_test()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
port:5000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
});
in an Astro integration
Astro has a so called integration that helps update the config (sort of Astro plugins) the integration helps identify what was finally kept in the config and also gives a last chance to update the config
integration-test.js
async function config_setup({ updateConfig, config, addPageExtension, command }) {
green_log(`astro:config:setup> running (${command})`)
updateConfig({
server:{proxy : {'/api': 'http://localhost:4000'}},
vite:{server:{proxy : {'/api': 'http://localhost:4000'}}}
})
console.log(config.server)
console.log(config.vite)
green_log(`astro:config:setup> end`)
}
this is the output log
astro:config:setup> running (dev)
{ host: false, port: 3000, streaming: true }
{
optimizeDeps: { esbuildOptions: { define: [Object] } },
server: { port: 5000, proxy: { '/api': 'http://localhost:4000' } }
}
astro:config:setup> end
the proxy parameter is removed from astro server config, the vite config is visible but has no effect as it is overridden, and not forwarded to Vite
test results
dev server runs on port 3000 which is from Astro config server all other configs overridden
the fetch api fails with the error
error Failed to parse URL from /api
File:
D:\dev\astro\astro-examples\24_api-proxy\D:\dev\astro\astro-examples\24_api-proxy\src\pages\index.astro:15:20
Stacktrace:
TypeError: Failed to parse URL from /api
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
functional test without proxy
Given that Astro front matter runs on the server side, in SSG mode during build and in SSR mode on page load on the server then the server sends the result html, Astro has access to all host ports and can directly use the service port like this
const response = await fetch('http://localhost:4000/api');
const data = await response.json();
console.log(data);
The code above runs as expected without errors
Reference Example
All tests and files mentioned above are available on the reference example github repo : https://github.com/MicroWebStacks/astro-examples/tree/main/24_api-proxy
You can add your own proxy middleware with the astro:server:setup hook.
For example use http-proxy-middleware in the server setup hook.
// plugins/proxy-middleware.mjs
import { createProxyMiddleware } from "http-proxy-middleware"
export default (context, options) => {
const apiProxy = createProxyMiddleware(context, options)
return {
name: 'proxy',
hooks: {
'astro:server:setup': ({ server }) => {
server.middlewares.use(apiProxy)
}
}
}
}
Usage:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import proxyMiddleware from './plugins/proxy-middleware.mjs';
// https://astro.build/config
export default defineConfig({
integrations: [
proxyMiddleware("/api/all", {
target: "http://localhost:3010",
changeOrigin: true,
}),
],
});

Vite cannot forward the request normally

I'm at vite config. TS configures the request broker, as follows:
// 开发服务器配置
server: {
host: true,
port: VITE_PORT,
open: VITE_OPEN,
proxy: {
"/MockServer": {
// target: "http://172.16.10.215:32340/",
target: "http://172.18.30.123:8510/gdm-analysis/",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/MockServer/, ""), //替换访问api的url适配服务端
},
},
strictPort: false, //接口被占用时尝试使用下个可用端口,
hmr: true,
},
I start the project locally and visit, and the request is correctly forwarded to http://172.18.30.123:8510/gdm-analysis/,but when I use my colleague's computer to access my local project, the request cannot be forwarded normally, header show:Provisional headers are shown

webpack-dev-server returns 504 while backend returns 204

Here is an error text (app crashes at all 204 reponses):
[HPM] Error occurred while trying to proxy request /rest/profiles/change-password from site.loc:3005 to https://site.loc (ECONNRESET) (https://nodejs.org/api/errors.html#errors_common_system_errors)
Debugged backend response. It returns 204 status code with empty body (data = null). If I build an app everything is working fine. One more moment: it doesn't occur at other developer's MAC OS.
My webpack devServer config:
devServer: {
contentBase: PATHS.dist,
host: appDomain,
proxy: {
'/rest': {
target: appUrl,
secure: false,
changeOrigin: true
},
...
}
My enviroment:
Windows 10 Pro x64
node version: v12.18.4
webpack-dev-server: "^3.11.0",
Please, help!
I spent about 3 days on that error, myabe this answer could help anyone...
I didn't figure out why this error occur, good people helped me with this code:
proxy: {
'/rest': {
target: appUrl,
secure: false,
changeOrigin: true,
onProxyRes: function (proxyRes, req, res) {
console.log(proxyRes.statusCode);
},
onError: function (err, req, res) {
console.log(`req.body: ${req.body}`); // here it returned undefined
console.log(`err.code: ${err.code}`);
if (err.code === 'ECONNRESET') {
res.writeHead(204, {'Content-Type': 'application/json'});
res.end();
}
},
},
Previously tried to do this using webpack-dev-server bypass method, axios interceptors, but with no success.

Using Angular CLI to reverse proxy to a service in API Gateway/lambda

I am trying to reverse proxy Angular using the proxy.conf.json to a lambda behind API gateway.
{
"/api/profile/*": {
"target": "http://asdasdfsdf.execute-api.ap-southeast-2.amazonaws.com",
"secure": false,
"logLevel": "debug",
"changeOrigin": false,
"headers": {
"host":"asdasdfsdf.execute-api.ap-southeast-2.amazonaws.com"
},
"pathRewrite": {
"^/api/profile": "/dev/profile"
}
},
}
I think there is an issue with the host header.
If I hit this now, I get unauthorized
However when I add the host header explicitly in postman, it works.
I used the bypass option.
To use it we have to change the proxy.conf.json to proxy.conf.js.
Check that all the references to proxy.conf.json now point to proxy.conf.js ( maybe you have it in angular.json , in options.proxyConfig or in the package.json).
After this we add the code to add the bypass option(in proxy.conf.js and it would end looking something like this:
const PROXY_CONFIG =
{
"/api/profile/*": {
target: "http://asdasdfsdf.execute-api.ap-southeast-2.amazonaws.com",
secure: false,
logLevel: "debug",
changeOrigin: false,
bypass: function (req, res, proxyOptions) {
req.headers["host"] = "asdasdfsdf.execute-api.ap-southeast-2.amazonaws.com";
},
pathRewrite: {
"^/api/profile": "/dev/profile"
}
},
};
module.exports = PROXY_CONFIG;
"target": "http://asdasdfsdf.execute-api.ap-southeast-2.amazonaws.com" was doing a redirect to https:// .... The proxy respected the redirect but it did not forward the headers in the config.
The solution was to use the https directly.

GatsbyJS/ReactJS not proxying requests correctly

Here's some context. I have a ReactJS site, built with Gatsby. This site is entirely static and has no backend. I've recently been working on a backend integration, as I wanted to do some datavis stuff with the GitHub API. Anyway, I was working on building my own backend API with NodeJS, and then proxying the frontend requests (using the fetch API).
The frontend, during development, is at localhost:8000, and the backend at localhost:5000. This is my gatsby-config.js file. It has some other bits unrelated to this question, but the proxy bit is just at the bottom:
module.exports = {
siteMetadata: {
title: "Reece Mercer",
description: "Personal website"
},
plugins: [
'gatsby-plugin-react-helmet',
{
resolve: `gatsby-plugin-manifest`,
options: {
name: 'gatsby-starter-default',
short_name: 'Reece Mercer',
start_url: '/',
background_color: '#663399',
theme_color: '#663399',
display: 'minimal-ui',
icon: 'src/assets/images/website-icon.png', // This path is relative to the root of the site.
},
},
'gatsby-plugin-sass',
'gatsby-plugin-offline',
'gatsby-plugin-favicon',
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/blog_posts`,
name: 'blog_posts'
}
},
'gatsby-transformer-remark'
],
proxy: {
prefix: "/myRepoAPI",
url: "http://localhost:5000",
},
}
Essentially, any requests made from the frontend that are to /myRepoAPI/anything should be proxied away from the frontend, to the backend. I've used Postman to validate the backend, and this endpoint functions as it should.
Here is the fetch call I used in a frontend React component:
componentDidMount(){
fetch('/myRepoAPI/hello')
.then(res => console.log(res))
}
Now analysing that console log:
Response {type: "basic", url: "http://localhost:8000/myRepoAPI/hello", redirected: false, status: 200, ok: true, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://localhost:8000/myRepoAPI/hello"
__proto__: Response
The url value is at localhost:8000, not 5000. The proxy hasn't worked. I have tried different combinations of routes, trailing /'s, and even trying the advanced proxy method of developMiddleware. Nothing seems to get it working as it should.
Any help would be much appreciated!
Since Gatsby's internal development server (default at port 8000) handles the proxy, in the response object the url value will always be http://localhost:8000/... regardless whether the proxy's working or not.
You may have more clue when looking at the log from your local server at port 5000, see if the request from Gatsby is actually hitting that end; or log out the actual data you get from the request.

Resources