The route with GET method works but the route with the POST method doesn't work. The postman is keep running and doesn't respond with a response.
Here is what I have tried,
import * as hapi from "#hapi/hapi";
const init = async () => {
const server: hapi.Server = new hapi.Server({
port: 3000,
host: 'localhost'
});
server.route({
method: 'POST', // if you change it to GET, it works
path: '/test',
handler: function (request, h) {
const payload = { username: 'testUserNames'};
return payload.username;
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
}
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
What could be the issue? where am I wrong?
i had the same problem and i solved it by downgrading to node v14. for some reason the hapi framework isn't compatible with node v16.
Related
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'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' }
}
}
}
}
I'm finding some difficulties to follow the guide for hapijs-react-views package setup (npm hapi-js-react-views).
I can run the server but I only get this error on localhost:3000
{"statusCode":500,"error":"Internal Server Error","message":"An internal server error occurred"}
My repo on github is: hapi-react GitHub
My code is:
-routes
--index.js
-views
--index.jsx
-app.js
-package.js
// routes/index.js
exports.index = function(request, reply){
reply.view('index', { name: 'John' });
};
// views/index.js
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
module.exports = HelloMessage;
//app.js
var hapi = require('hapi');
var vision = require('vision');
var path = require('path');
var engine = require('hapijs-react-views')();
// Create a server with a host and port
var server = new hapi.Server();
server.connection({
host: 'localhost',
port: 3000
});
// Register Hapi plugins
server.register(vision, function (err) {
if(err) throw err;
});
var options = { jsx: { harmony: true } };
server.views({
defaultExtension: 'jsx',
engines: {
jsx: require('hapijs-react-views')(options), // support for .jsx files
js: require('hapijs-react-views')(options) // support for .js
},
relativeTo: __dirname,
path: 'views'
});
// Add the route
server.route({
method: 'GET',
path: '/',
config: {
handler: require('./routes').index
}
});
// Start the server
server.start((err) => {
if (err) {
throw err;
}
console.log('Server running at:', server.info.uri);
});
//package.json
"dependencies": {
"hapi": "^13.4.1",
"hapijs-react-views": "^0.7.3",
"react": "^15.1.0",
"vision": "^4.1.0"
}
Can you help me?
Thanks in advance.
For anyone who sees this in the future, there's a working example of jsx rendering with vision here: https://github.com/hapijs/vision/tree/master/examples/jsx
I was having this exact issue. The documentation doesn't say but you still require React in your view files. That fixed the problem for me.
I have a hapi.js code as below:
`
'use strict';
const hapi = require("hapi");
const server = new hapi.Server();
server.connection({
host: 'localhost',
port: 2922,
});
server.route({
method: 'GET',
path: '/hello',
handler: function (req, res) {
res('Hello world from hapi.js');
}
});
server.start((err) => {
});
`
The problem is that when I run this server with port number 22291 it executes and exit immediately without any error, What is the reason for?
I am using linux centos 7
You need to check for the error in the start callback:
server.start((err) => {
if (err) throw err;
});
That will give you the details you need to debug. Always make sure to check for errors in callbacks or you'll repeatedly run into this problem in Node.
I am trying to host api and web servers on the same node code stack. I have used labels in order to apply configurations independently to each server but only one server works. Below is the code:
var hapi = require('hapi');
// server definition
var server = new hapi.Server();
var runningPort = process.env.PORT || 3000;
// setting up connection
server.connection({
host: '0.0.0.0',
port: runningPort,
labels: ['api']
});
server.connection({
host: '0.0.0.0',
port: runningPort,
labels: ['web']
});
var webServer = server.select('web');
var apiServer = server.select('api');
// registering view engine
webServer.views({
engines: { html: require('handlebars') },
relativeTo: __dirname,
path: './views',
layoutPath: './views/layout',
layout: 'default',
partialsPath: './views/partials'
});
// registering hapi auth cookie and application authentication
webServer.register(
{
register: require('hapi-auth-cookie')
},
function (err) {
if (err) {
throw err;
}
var cache = webServer.cache({ segment: 'sessions', expiresIn: 3 * 24 * 60 * 60 * 1000 });
webServer.app.cache = cache;
webServer.auth.strategy('session', 'cookie', true, {
password: 'secret',
cookie: 'sid-example',
redirectTo: '/account/login',
isSecure: false
});
});
// registrations for api server
apiServer.register(
{
register: require('lout')
},
function (err) {
if (err) {
throw err;
}
});
apiServer.register(require('hapi-auth-bearer-token'), function (err) {
apiServer.auth.strategy('simple', 'bearer-access-token', {
allowQueryToken: true, // optional, true by default
allowMultipleHeaders: false, // optional, false by default
accessTokenName: 'access_token', // optional, 'access_token' by default
validateFunc: function( token, callback ) {
// For convenience, the request object can be accessed
// from `this` within validateFunc.
var request = this;
// Use a real strategy here,
// comparing with a token from your database for example
if(token === "1234"){
//## user object to be looked up here
callback(null, true, { token: token })
} else {
callback(null, false, { token: token })
}
}
});
});
//To do something to request before they passed on to routes
apiServer.ext('onRequest', function (request, reply) {
//## we can get user object here off of the authToken
utils.log('info', 'apiCall', {method: request.method, path: request.path})
return reply.continue();
});
// register routes
webServer.route(webRoutes);
apiServer.route(apiRoutes);
server.start(function () {
console.log('Web servers running at: ', 'localhost:' + runningPort);
console.log('Api server running at: ', 'localhost:' + runningPort);
});
Currently only api routes work.
As mentioned by the commenters, you can't create 2 connections to the same port on the same network interface. This goes all the way back to the listen syscall giving a EADDRINUSE error if two sockets try to listen on the same port.
Creating two connections on separate ports or separate network interfaces is perfectly ok though:
server.connection({
host: '127.0.0.1',
port: 4000,
labels: ['api']
});
server.connection({
host: '127.0.0.1',
port: 4001,
labels: ['web']
});
As Matt already said, you can't run different connections on the same port. Make sure to apply different ports, like
server.connection({
host: 'localhost',
port: process.env.PORT || port
});
server.connection({
host: 'localhost',
port: process.env.PORT + 1 || port + 1
});
If you want to read more on running an API and your web server within the same hapi project, you might scan through those tutorials:
https://futurestud.io/tutorials/hapi-how-to-run-separate-frontend-and-backend-servers-within-one-project
https://futurestud.io/tutorials/hapi-how-to-use-server-labels
Hope that helps!