I'm trying to use Express as a backend running a VueJS web application with hot reload, but I can't FETCH the content from the server.
vue.config.js:
module.exports = {
devServer: {
proxy: 'http://localhost:3000'
}
}
server.js:
const express = require('express');
const app = express();
const port = 3000
app.get('/hello', (req, res) => {
res.send({ "message": "Hello World" }) //Content
});
app.listen(port, () => {
console.log(`WebServer listening at port`);
});
That is running and /hello is working at port 3000.
Now, I'm starting both this way:
npm run server & nodemon server.js
Trying to fetch /hello in the Vue application, but it's not working. Am I missing anything?
<template>
<div class="flex-col">{{tasks}}
</div>
</template>
<script>
export default {
name:"ListToDo",
data(){
return{
tasks: []
}
},
methods:{
FETCH: function(){
fetch("/tasks/")
.then(res => res.json())
.then(data => this.tasks=data)
}
},
mount(){
this.FETCH()
}
}
The front-end is fetching /tasks, but the server does not have a route for /tasks, so it will respond with 404 Not Found.
One solution is to add a route for /tasks to your server's Express instance:
app.get('/tasks:myOptions(/*)?', (req, res) => {
res.send({ message: 'tasks', myOptions: req.params.myOptions })
})
Or you can update your component to use the /hello route already setup in the server:
export default {
methods: {
FETCH: function() {
fetch("/hello") 👈
.then(res => res.json())
.then(data => this.tasks=data)
}
}
}
}
If you prefer to keep your original /hello route while using /tasks from the client, the path will need to be rewritten client-side through the proxy, using the pathRewrite config shown below. However, this can't be done with the simple string proxy config, and specific route contexts (i.e., /tasks) must be specified:
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/tasks': {
target: 'http://localhost:3000',
pathRewrite: { '^/tasks' : '/hello' }
}
}
}
}
Related
Currently we are trying to run the nodejs server in angular application(net core -angular -spa).We went through this source-
https://www.simplilearn.com/tutorials/angular-tutorial/what-is-angular-node
As per the project we went on creating the angular application and set the proxy based upon the link : https://angular.io/guide/build#proxy-multiple-entries
The Step we are following this:
Step 1: Creation Node Server, which is working individually.
The 504 error shows here.
var express = require('express');
var app = express();
app.use(express.static("myApp")); // myApp will be the same folder name.
// PORT
const port = process.env.PORT || 8000
app.get('/', (req,res) => {
res.send('App Works !');
});
app.get('/externalapi/get', (req, res) => {
console.log(req);
console.log('App Works !');
res.send('Got Files!');
return "Got Files";
});
app.listen(port, () => {
console.log(`Server listening on the port no.:${port}`);
});
Step 2 : Proxy.config.js (not json)
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
},
{
context :[
"/externalapi/*",
],
target : "http://localhost:8000",
secure: false,
changeOrigin: true,
}
]
module.exports = PROXY_CONFIG;
Step 3: Creation of the Services
export class ExampleServices{
_nodeUrl : any = '/externalapi/';
constructor( private httpClient: HttpClient){
}
mergeExamples(files:any) : Observable<any> {
console.log(this._nodeUrl);
return this.httpClient.get(this._nodeUrl+"get", files);
}
}
There are two from us ->
Why 504 Error occurs in this code ?Is it due to https-> http calling?
Is there any possibility to run the nodejs command from parent folder itself(currently the externalapi is inside the angular app, have its own node_modules)
Kindly help us with relevant answer and supporting documents.
As outlined in the title, I am having difficulty setting a http cookie to be used for auth purposes when tunnelling using ngrok.
The following code works fine (obviously with the relevant endpoints specified) when i am running a query from from localhost to a localhost endpoint in my dev environment but breaks down as soon as i start to query the ngrok tunnel endpoint.
Frontend api query (simplified as part of larger application)
function fetchRequest (path, options) {
const endpoint = 'http://xxx.ngrok.io'; // the ngrok tunnel endpoint
return fetch(endpoint + path, options)
.then(res => {
return res.json();
})
.catch((err) => {
console.log('Error:', err);
});
}
function postRequest (url, body, credentials='include') {
return fetchRequest(`${url}`, {
method: 'POST',
withCredentials: true,
credentials: credentials,
headers: {'Content-Type': 'application/json', Accept: 'application.json'},
body: JSON.stringify(body)
});
}
// data to be passed to backend for authentication
let data = {pin: pin, username : username};
postRequest('/',data)
Express server on Node.js with ngrok tunnel (app.js)
const express = require('express')
const session = require('express-session')
const cors = require('cors')
const router = require('./router');
const tunnel = require('./ngrok')
const app = express()
const port = process.env.PORT || 4001;
app.use(cors({
origin: 'http://localhost:3000'
credentials: true,
}))
app.use(express.json());
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
expires: expiryDate
// sameSite: 'none'
// secure: true
}
}))
app.use(router)
let useNGROK = true;
if (useNGROK) {
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
tunnel.createHTTPtunnel().then((url) => {
console.log(`New tunnel created with endpoint: ${url}`)
});
} else {
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
}
Ngrok configuration (ngrok.js)
const ngrok = require('ngrok');
const find = require('find-process');
const port = process.env.PORT || '3000';
const tunnel = {
createHTTPtunnel: async function () {
const list = await find('name', 'ngrok');
if (list.length > 0) {
let api = ngrok.getApi();
if (api == null) {
this.kill_existing_tunnel();
} else {
let open_tunnels = await ngrok.getApi().listTunnels();
return open_tunnels.tunnels[0].public_url;
}
}
let ngrok_config = {
proto: 'http',
bind_tls: false,
name: process.env.NGROK_NAME,
hostname: process.env.NGROK_CUSTOM_DOMAIN,
// host_header: 'rewrite',
authtoken: '',
region: 'eu',
};
return ngrok.connect({ ...ngrok_config, addr: port });
},
kill_existing_tunnel: async () => {
const list = await find('name', 'ngrok');
list.forEach((p) => {
try {
process.kill(p.pid);
console.log(`Killed process: ${p.name} before creating ngrok tunnel`);
} catch (e) {
console.log(e);
}
});
}
}
module.exports = tunnel;
** router & controller (router.js & controller.js respectively) **
*router.js*
const router = require('express').Router();
const example = require('./controller')
router.post('/', example.authenticate);
module.exports = router;
*controller.js*
async function authenticate (req, res) {
try {
res.send(JSON.stringify('trying to send cookie'))
} catch (e) {
console.log('Error', e)
res.sendStatus(500)
}
}
module.exports = {
authenticate
};
The following information is provided when inspecting the Set-Cookie response header in the network requests:
This Set-Cookie header didn’t specify a “SameSite” attribute and was defaulted to “SameSite=Lax” and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with “SameSite=None” to enable cross site usage.
Attempted fix 1//
If I add the following options to the cookie {sameSite: ‘none’, secure:true}, amend the ngrok config to set {bind_tls: true} and run https on my front end (using a custom SSL certificate as per the create react app documentation), and query the https tunnel, then no cookie is received in the response from the server at all (request is sent and response 200 is received but with no cookie).
Attempted fix 2//
I also tried to change the host_header option to rewrite in the ngrok config (to mirror a response from localhost rather than from ngrok) and this did not work.
Any help would be much appreciated as I have little experience and I am stuck!
I have a React front-end that makes requests to the github API. I initially wrote the project to make use of an Express proxy server to help keep some environment variables private. I have the front-end of the project deployed on Netlify and since my little server only has 2 routes (for fetching a user's profile and repos, independently) I didn't think it made sense to deploy the back end somewhere else, and figured I'd just use the serverless-http package to wrap my server and make it essentially a Netlify Lambda function.
The issue: I have the project working locally when I set the NODE_ENV to both production and development, with a slightly modified webpack.config proxy setting (that is to say, it's slightly different than I need it to be to run it locally directly using the Express server). Further, I can query the routes on the deployed project with Postman and it appears to correctly return the data from the endpoint at the GitHub API. At present, the frontend is showing an error that indicates that there is a JSON parsing error (unexpected character at column 1)... I have had this error in local development when trying to make this work there and it was because the request initiating on the frontend was not making its way to the proxy server. Realistically my error handling for the project is dog-shit and so I don't have more info at present.
As the code is written, requests are still proxied through the serverless functions.
My feeling is this is an issue with how I have deployed things. I've spent a few hours going through the docs and I really don't know what I've done wrong, so I'm hoping someone familiar with Netlify and their implementation of serverless functions has an idea about why this isn't working.
I'm using Node v11.10.0.
For reference, the Express API I wrote is as follows:
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const serverless = require('serverless-http');
require('dotenv').config();
// Initialize app
const app = express();
const router = express.Router();
const port = process.env.PORT || 3000;
// Configuration
app.use(bodyParser.json({ type: 'application/json' }));
const routerBasePath = (process.env.NODE_ENV === 'development')
? `/index`
: `/.netlify/functions/index`
app.use(routerBasePath, router);
console.warn('Node environment: ', process.env.NODE_ENV)
// Constants
const id = process.env.GITHUB_CLIENT_ID;
const secret = process.env.GITHUB_CLIENT_SECRET;
const params = `?client_id=${id}&client_secret=${secret}`
// Route(s)
router.post('/profile', (req, res) => {
const { username } = req.body;
const endpoint = `https://api.github.com/users/${username}${params}`;
axios({
method: 'get',
url: endpoint,
})
.then(response => {
res.status(200)
res.send(response.data)
})
.catch(error => {
console.log('Error with Axios profile res: ', error)
res.send({ error })
})
return
})
router.post('/repos', (req, res) => {
const { username } = req.body;
const endpoint = `https://api.github.com/users/${username}/repos${params}&per_page=100`;
axios({
method: 'get',
url: endpoint,
})
.then(response => {
res.status(200)
res.send(response.data)
})
.catch(error => {
console.log('Error with Axios repos res: ', error)
res.send({ error })
})
return
})
// DJ, spin that shit:
app.listen(port, () => console.log(`App is running on port ${port}`));
process.on('SIGINT', () => {
console.log(`Process on port ${port} successfully terminated.`);
process.exit();
})
// Serverless
// app.use('/.netlify/functions/index', router);
module.exports.handler = serverless(app);
The relevant portion of webpack.config:
mode: process.env.NODE_ENV === 'production'
? 'production'
: 'development',
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api': '.netlify/functions/index' }
}
},
historyApiFallback: true
},
The relevant code from the front-end:
function getProfile(username) {
return fetch('/api/profile', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ 'username': username })
})
.then(res => {
return res.json()
})
.then(profile => {
if (profile.message) {
throw new Error(getErrorMsg(profile.message, username))
}
return profile
})
}
function getRepos(username) {
return fetch('/api/repos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ 'username': username })
})
.then(res => {
return res.json()
})
.then(repos => {
if (repos.message) {
throw new Error(getErrorMsg(repos.message, username))
}
return repos
})
}
And my package.json:
"scripts": {
"build": "NODE_ENV='production' webpack",
"build-for-windows": "SET NODE_ENV='production' && webpack",
"build-for-netlify": "NODE_ENV='production' webpack && npm run build:lambda",
"start": "concurrently \"webpack-dev-server\" \"npm run start:lambda\"",
"server": "nodemon server/index.js",
"start:lambda": "NODE_ENV='production' netlify-lambda serve ./server",
"build:lambda": "netlify-lambda build ./server"
},
"babel": {
"presets": [
"#babel/preset-env",
"#babel/preset-react"
],
"plugins": [
"#babel/plugin-proposal-class-properties",
"syntax-dynamic-import"
]
},
"proxy": "http://localhost:3000"
[build]
command = "npm install && npm run build-for-netlify"
functions = "functions"
publish = "dist"
To test this locally, I've run the npm run start script, changing the NODE_ENV between production and development. When it's in development mode, the routes are accessible via /index (as seen in the express API) and when it's in production they're accessed via ./netlify/functions/index, and the behavior is as expected both in the local environment and from Postman.
I'm fairly new to setting up this kind of environment so I'm sure there are some dumb errors but I'm at a loss for what to search for at this point. I did see something about needing to bundle your Netlify Functions for deploy but I am not sure if the build step handles that or not.
Thanks in advance all, this has been a headscratcher for me.
EDIT1: Added netlify.toml configuration as well.
EDIT2: Added Node version info.
EDIT3: Adding info about error
I have a form on my site and I want to send the data from fields to my email. I am using nodemailer and node js for this things. But when I submit form I have an 404 error on POST request.
form-component:
this.http.post('api/sendForm',{
to: environment.contactUsEmail,
from: 'zzz',
subject: 'zzz',
mailInfo: contactUsData,
}
).subscribe(() => {
this.cooperationFormGroup.reset();
});
server.ts: (path:backend/server.ts) folder backend is near folder src
const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const PORT = process.env.PORT || 3000;
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('api/sendForm', (req, res) => {
const payload = req.body;
const mailInfo = payload.mailInfo;
const transporter = nodemailer.createTransport({
service: 'gmail',
host: 'smtp.gmail.com',
secure: 'true',
port: '465',
auth: {
user: 'email',
pass: 'pass',
}
});
const text = [...];
const mailOptions = {
from: 'zz',
to: payload.to,
subject: payload.subject,
text: text.join('\n'),
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
res.status(200).json({
message: 'successfully sent!'
})
}
});
});
app.listen(PORT, () => {
console.log(`Server is running in ${PORT}`);
});
I run server.ts in the folder backend using node server.ts and run angular app using npm start
As mentioned above in my comment: you need to pass the complete URL of your backend to post: use http://localhost:3000/api/sendForm instead of api/sendForm.
However, to manage different values during development and production, you might want to use environment.ts and environment.prod.ts:
environments/environment.ts:
export const environment = {
production: false,
urlToBackend: 'http://localhost:3000'
}
environments/environment.prod.ts:
export const environment = {
production: true,
urlToBackend: 'http://<IP>:3000'
}
service.ts:
While building the production build with npm run build, environment.ts will be replaced by environment.prod.ts as mentioned in the angular.json (see the object fileReplacements).
import { environment } from '../../environments/environment';
...
#Injectable()
export class AppService {
url = environment.urlToBackend;
constructor(private http: HttpClient) {
}
foo() {
return this.http.post(`${this.url}/api/sendForm`,{ ... });
}
}
My code is not accurate and you need to arrange it for your needs. However, I hope, you get the idea.
You need to mention the complete backend server URL in the first argument of the .post.
Change 'api/sendForm' to 'Your complete backend url'.
this.http.post( 'complete backend server url' ,
since you are running the node server on PORT 3000. Your backend URL will be http://localhost:3000/api/sendForm
I'm building a fullstack React application with a Postgres database connected by Express/Node. I'm not using create-react-app. To test API calls, I'm calling the API in componentDidMount() with axios. The server is running on port 3000 and the react application is running on port 8080. I've tested the /users endpoint in Postman to ensure it works.
componentDidMount() {
// failing config for webpack dev server:
axios.get('/api/users')
.then((response) => {
console.log(response)
})
.catch((err) => {
console.log(err);
console.log(err.response)
})
// fails:
// axios.get('/users')
// .then((response) => {
// console.log(response)
// })
// .catch((err) => {
// console.log(err);
// console.log(err.response)
// })
// works fine:
// axios.get('http://localhost:3000/users')
// .then((response) => {
// console.log(response)
// })
// .catch((err) => {
// console.log(err)
// })
}
Note that calling localhost:3000 directly works, but I want to be able to call /users to prepare for a production environment. So I added the following in my webpack.config.js file:
devServer: {
contentBase: __dirname,
hot: true,
historyApiFallback: true,
open: true,
proxy: {
'/api': 'http://localhost:3000',
changeOrigin: true
}
}
Is there something else I need to add for proxying API calls when running webpack-dev-server? From the tutorials I've seen, having the proxy configuration in webpack.config.js is sufficient, but I'm only getting 404s.
Thanks in advance.
The solution, at least for me, ended up being using react environment variables that will dynamically determine the url to point to. I followed this tutorial: https://medium.com/#trekinbami/using-environment-variables-in-react-6b0a99d83cf5. Just point your dev env to localhost and then whatever port your server is on.