Cypress publicPath not supported with percy and nock - node.js

I'm getting an issue where I am trying to add percy to my Cypress tests using nock and webpack 5. Based on a solution found here, I tried to set the publicPath to an empty string, but with no success. The error message I get is
The following error originated from your test code, not from Cypress.
Automatic publicPath is not supported in this browser
When Cypress detects uncaught errors originating from your test code
it will automatically fail the current test.
Cypress could not associate this error to any specific test.
We dynamically generated a new test to display this failure
.../webpack/runtime/publicPath:14:1
// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
^
scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
__webpack_require__.p = scriptUrl;
In my cypress/plugins/webpack.config.js file, I have the following.
module.exports = {
resolve: { extensions: ['.ts', '.js'], fallback: { http: false } },
output: {
publicPath: '/',
},
module: {
rules: [
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
],
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
transpileOnly: true,
},
},
],
},
};
In my cypress/plugins/index.js, I have the following.
const nock = require('nock');
const http = require('http');
const next = require('next');
const webpackPreprocessor = require('#cypress/webpack-preprocessor');
module.exports = async (on, config) => {
const app = next({ dev: true });
const handleNextRequests = app.getRequestHandler();
await app.prepare();
const customServer = new http.Server(async (req, res) => {
return handleNextRequests(req, res);
});
await new Promise((resolve, reject) => {
customServer.listen(3000, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
on('task', {
clearNock() {
nock.restore();
nock.cleanAll();
return null;
},
async nock({ hostname, method, path, statusCode, body }) {
nock.activate();
// add one-time network stub like
method = method.toLowerCase();
nock(hostname)[method](path).reply(statusCode, body);
return null;
},
});
const options = {
webpackOptions: require('./webpack.config'),
watchOptions: {},
};
on('file:preprocessor', webpackPreprocessor(options));
return config;
};
How can I configure the publicPath properly?

Related

Why is my _middleware not able to route well in production (NEXTJS)?

I have the following on my _middleware.ts file with the objective of mainly routing requests to mysubdomain. Its working well on my local server but when I deploy to vercel its throwing a 404. Any help will be much appreciated.
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { request } from "http";
export default function middleware(req: NextRequest) {
// Clone the request url
const newReq = req.nextUrl.clone();
const regex = new RegExp(`\.subdomaingivenbyvercel-[\w\w\w\w\w\w\w\w\w]-username\.vercel\.app`);
const pathname = req.nextUrl.pathname.toString();
// Get hostname of request (e.g. demo.vercel.pub)
const hostname = req.headers.get("host");
if (!hostname)
return new Response(null, {
status: 400,
statusText: "No hostname found in request headers"
});
const currentHost =
process.env.VERCEL_ENV === `production` && process.env.VERCEL === `1`
? // You have to replace ".vercel.pub" with your own domain if you deploy this
example under your domain.
// You can use wildcard subdomains on .vercel.app links that are associated
with your Vercel team slug
// in this case, our team slug is "platformize", thus
*.platformize.vercel.app works
hostname
.replace(`.myrootdomain.com`, "")
.replace(`.branchname.vercel.app`, "")
.replace(`.*.vercel.app`, "")
.replace(regex, "")
: hostname.replace(`.localhost:3000`, "");
if (pathname.startsWith(`/_sites`))
return new Response(null, {
status: 404
});
if (
!pathname.includes(".") &&
!pathname.startsWith("/api")
) {
if (currentHost === "mysubdomain") {
if (
pathname === "/login" &&
(req.cookies["next-auth.session-token"] ||
req.cookies["__Secure-next-auth.session-token"])
) {
newReq.pathname = "/";
return NextResponse.redirect(newReq.toString());
}
newReq.pathname = `/mysubdomain${pathname}`;
return NextResponse.rewrite(newReq.toString());
}
newReq.pathname = `${pathname}`;
return NextResponse.rewrite(newReq.toString());
}
}
I try all of the routes and none of them can be found. I have tried everything around the way the middleware parses the URL without success. Here you can see my next.config.js just in case Im doing a redirect without knowing it:
/**
* #type {import('next').NextConfig}
*/
const plugins = require("next-compose-plugins");
const withBundleAnalyzer = require("#next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true"
});
const withOffline = require("next-offline");
function esbuildLoader(config, options) {
const jsLoader = config.module.rules.find(
(rule) => rule.test && rule.test.test(".js")
);
if (jsLoader && jsLoader.use) {
if (jsLoader.use.length > 0) {
jsLoader.use.forEach((e) => {
e.loader = "esbuild-loader";
e.options = options;
});
} else {
jsLoader.use.loader = "esbuild-loader";
jsLoader.use.options = options;
}
}
}
// the config break if we use next export
const nextConfig =
process.env.EXPORT !== "true"
? {
webpack(config, { webpack, dev, isServer }) {
config.plugins.push(
new webpack.ProvidePlugin({
React: "react"
})
);
// audio support
config.module.rules.push({
test: /\.(ogg|mp3|wav|mpe?g)$/i,
exclude: config.exclude,
use: [
{
loader: require.resolve("url-loader"),
options: {
limit: config.inlineImageLimit,
fallback: require.resolve("file-loader"),
publicPath: `${config.assetPrefix}/_next/static/images/`,
outputPath: `${isServer ? "../" : ""}static/images/`,
name: "[name]-[hash].[ext]",
esModule: config.esModule || false
}
}
]
});
config.module.rules.push({
test: /\.(glsl|vs|fs|vert|frag)$/,
exclude: /node_modules/,
use: ["raw-loader", "glslify-loader", "#svgr/webpack"]
});
config.module.rules.push({
test: /\.svg$/,
use: [`#svgr/webpack`]
});
return config;
},
images: {
domains: [
"lomplay.com/ipfs",
"nft.storage",
"avatars.githubusercontent.com"
]
},
reactStrictMode: true,
swcMinify: false // Required to fix: https://nextjs.org/docs/messages/failed-
loading-swc
}
: {};
module.exports = plugins(
[
[
withOffline,
{
workboxOpts: {
swDest: process.env.NEXT_EXPORT
? "service-worker.js"
: "static/service-worker.js",
runtimeCaching: [
{
urlPattern: /^https?.*/,
handler: "NetworkFirst",
options: {
cacheName: "offlineCache",
expiration: {
maxEntries: 200
}
}
}
]
},
async rewrites() {
return {
beforeFiles: [
{
source: "/_subdomain/api/newsletter",
destination: "https://rootdomain.com/api/newsletter"
}
],
afterFiles: [
{
source: "/service-worker.js",
destination: "/_next/static/service-worker.js"
}
]
};
}
}
],
withBundleAnalyzer
],
nextConfig
);

How to setup webpack-hot-server-middleware with vue-server-renderer and vue-router?

I'm trying to setup an own express server with webpack to use hot module replacement & server side rendering. Everything seems to work except the integration of webpack-hot-server-middleware which needs an express middleware function with (res, req, next) params - but I can't get my head around about how to implement it correctly.
This is my configuration so far:
webpack.config.js
const webpack = require('webpack');
const path = require('path');
const config = [{
name: 'client',
entry: [
'webpack-hot-middleware/client',
'./src/js/entry_client.js'
],
output: {
path: path.resolve(__dirname, 'dist/js/'),
filename: 'app.js'
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}, {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.js'
}
}
}, {
name: 'server',
target: 'node',
entry: './src/js/entry_server.js',
output: {
path: path.resolve(__dirname, 'dist/js/'),
filename: 'app.server.js',
libraryTarget: 'commonjs2'
},
externals: require('webpack-node-externals')(),
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.js',
}
}
}];
if (process.env.NODE_ENV !== 'production') {
config[0].plugins = [new webpack.HotModuleReplacementPlugin()];
}
module.exports = config;
entry_client.js
import {createApp} from './app';
const {app, router} = createApp();
router.onReady(() => {
app.$mount('#app');
});
entry_server.js
import {createApp} from './app';
export default context => {
return new Promise((resolve, reject) => {
const {app, router} = createApp();
router.push(context.url);
router.onReady(() => {
if (!router.getMatchedComponents().length) return reject({code: 404});
resolve(app);
}, reject);
});
};
app.js
import Vue from 'vue';
import App from '../vue/app.vue';
import {createRouter} from './router';
export function createApp() {
const router = createRouter();
const app = new Vue({
router,
render: h => h(App)
});
return {app, router};
}
router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../vue/home.vue';
import About from '../vue/about.vue';
Vue.use(Router);
export function createRouter() {
return new Router({
mode: 'history',
routes: [{
path: '/',
component: Home
}, {
path: '/about',
component: About
}]
});
}
server.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const bundle = require('./dist/js/app.server');
const renderer = require('vue-server-renderer').createRenderer({
template: fs.readFileSync('./src/html/index.html', 'utf-8')
});
const server = express();
// add vue HMR middleware
if (process.env.NODE_ENV !== 'production') {
const webpack = require('webpack');
const compiler = webpack(require('./webpack.config'));
server.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
serverSideRender: true
}));
server.use(require('webpack-hot-middleware')(compiler.compilers.find(compiler => compiler.name === 'client')));
// server.use(require('webpack-hot-server-middleware')(compiler));
} else {
// static file serving
server.use(require('serve-favicon')(path.join(__dirname, 'src/png/favicon-32x32.png')));
server.use(express.static(path.join(__dirname, 'dist/'), {index: false}));
}
server.get('*', (req, res) => {
bundle.default({url: req.url}).then(app => {
renderer.renderToString(app, {
title: 'Some title',
description: 'Some description'
}, (err, html) => {
if (err) {
console.error('Error in renderToString:', err);
return res.status(500).send('Internal Server Error');
}
res.send(html);
});
}, err => {
if (err.code === 404) return res.status(404).send('Page not found');
console.error(err);
return res.status(500).send('Internal Server Error');
});
});
server.listen(4040);

output.filename is required webpack

I tried to do the webpack exports in the following way. I tried to convert the async to sync ( regarding fs.readdir) to get the total files within directory.
const webpack = require('webpack');
const async = require('async');
const path = require('path');
const fs = require('fs');
var fileList = [];
var promiso = new Promise(function(res, rej) {
fs.readdir(__dirname + "/imports/components", function(err, files) {
files.forEach(function(file) {
console.log("file", file);
fileList.push(file);
});
let config = {
entry: {
'myPages': fileList[0]
},
module: {
loaders: [
// Javascript: js, jsx
{
test: /\.jsx?$/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: './bundler.js'
},
}
res(config)
})
});
promiso.then(function(mess) {
console.log("-------------------------mess", mess);
module.exports = mess;
});
I am getting following error
'output.filename' is required, either in config file or as --output-filename
Here you should return promise itself and then use it anywhere else. Because the require cannot work your way. Modules in Node that you load with require() are loaded synchronously and it is not possible for require to return any value that is loaded asynchronously.
module.exports = promise
And in another file use it as follows
require('path-to-file').then( (mess) => {
// Use mess
}).then(null, (err) => {
// Error handling
})

Truffle solidity loader Not working with Truffle 3.2.1

I was using truffle-webpack-demo but started getting errors since I upgraded to truffle 3.2.1. I found the new branch of the same repo and copied config from there. Which should be working, but now npm start gives me the following error.
Starting the development server...
Failed to compile.
Error in ./contracts/MetaCoin.sol
Module build failed: Error: You must specify the network name to deploy to. (network)
# ./src/components/AccountList/AccountListContainer.js 37:16-49
I have upgraded to the following versions of truffle, webpack-dev-server, webpack and truffle-solidity-loader
truffle-solidity-loader: "git+https://github.com/sogoiii/truffle-solidity-loader.git#1f1e213d52f033b6863218307b8968ae68220fe1"
truffle: 3.2.1
webpack-dev-server: 2.4.1
webpack: 2.2.1
Below is my config/webpack.config.dev.js
var path = require('path')
var autoprefixer = require('autoprefixer')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var precss = require('precss')
// TODO: hide this behind a flag and eliminate dead code on eject.
// This shouldn't be exposed to the user.
var isInNodeModules = path.basename(path.resolve(path.join(__dirname, '..', '..'))) === 'node_modules'
var relativePath = isInNodeModules ? '../../..' : '..'
var isInDebugMode = process.argv.some(arg =>
arg.indexOf('--debug-template') > -1
)
if (isInDebugMode) {
relativePath = '../template'
}
var srcPath = path.resolve(__dirname, relativePath, 'src')
var nodeModulesPath = path.join(__dirname, '..', 'node_modules')
var indexHtmlPath = path.resolve(__dirname, relativePath, 'index.html')
var faviconPath = path.resolve(__dirname, relativePath, 'favicon.ico')
var buildPath = path.join(__dirname, isInNodeModules ? '../../..' : '..', 'build')
var provided = {
'Web3': 'web3'
}
module.exports = {
devtool: 'eval',
entry: [
require.resolve('webpack-dev-server/client') + '?http://localhost:3000',
require.resolve('webpack/hot/dev-server'),
path.join(srcPath, 'index')
],
output: {
// Next line is not used in dev but WebpackDevServer crashes without it:
path: buildPath,
pathinfo: true,
filename: 'bundle.js',
publicPath: '/'
},
resolve: {
root: srcPath,
extensions: ['', '.js'],
alias: {
contracts: path.resolve('contracts'),
// config: require('../truffle').networks.development,
config: path.resolve('truffle.js')
}
},
module: {
preLoaders: [
{
test: /\.js$/,
loader: 'eslint',
include: srcPath
}
],
loaders: [
{
test: /\.js$/,
include: srcPath,
loader: 'babel',
query: require('./babel.dev')
},
{
test: /\.css$/,
include: srcPath,
loader: 'style!css!postcss'
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
loader: 'file'
},
{
test: /\.(mp4|webm)$/,
loader: 'url?limit=10000'
},
{
test: /\.sol/,
loader: 'truffle-solidity',
loaders: ['json-loader', 'truffle-solidity-loader?migrations_directory=' + path.resolve(__dirname, '../migrations') + '&network=development&contracts_build_directory=' + path.resolve(__dirname, '../dist/contracts')]
}
]
},
eslint: {
configFile: path.join(__dirname, 'eslint.js'),
useEslintrc: false
},
postcss: function () {
return [precss, autoprefixer]
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: indexHtmlPath,
favicon: faviconPath
}),
new webpack.ProvidePlugin(provided),
new webpack.DefinePlugin({
'myEnv': JSON.stringify(require('../truffle').networks.development),
'process.env.NODE_ENV': '"development"'
}),
new webpack.EnvironmentPlugin(['NODE_ENV']),
new webpack.HotModuleReplacementPlugin()
]
}
Here's my MetaCoin.sol
pragma solidity ^0.4.4;
import "./ConvertLib.sol";
// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!
contract MetaCoin {
mapping (address => uint) balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
function MetaCoin() {
balances[tx.origin] = 6000000000;
}
function sendCoin(address receiver, uint amount) returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Transfer(msg.sender, receiver, amount);
return true;
}
function getBalanceInEth(address addr) returns(uint){
return convert(getBalance(addr),2);
}
function getBalance(address addr) returns(uint) {
return balances[addr];
}
function convert(uint amount,uint conversionRate) returns (uint convertedAmount)
{
return amount * conversionRate;
}
}
Here's my AccountListContainer.js
import React, { Component } from 'react'
import AccountList from 'components/AccountList/AccountList'
import SendCoin from 'components/SendCoin/SendCoin'
import Contract from 'truffle-contract'
import MetaCoinArtifact from 'contracts/MetaCoin.sol';
const MetaCoin = Contract(MetaCoinArtifact)
import Web3 from 'web3';
const provider = new Web3.providers.HttpProvider('http://localhost:8545')
MetaCoin.setProvider(provider);
class AccountListContainer extends Component {
constructor(props) {
super(props)
this.state = {
accounts: [],
coinbase: ''
}
this._getAccountBalance = this._getAccountBalance.bind(this)
this._getAccountBalances = this._getAccountBalances.bind(this)
}
_getAccountBalance (account) {
return MetaCoin.deployed()
.then(meta => {
return meta.getBalance.call(account, {from: account})
})
.then(function (value) {
return { account: value.valueOf() }
})
.catch(function (e) {
console.log(e)
throw e
})
}
_getAccountBalances () {
this.props.web3.eth.getAccounts(function (err, accs) {
if (err != null) {
window.alert('There was an error fetching your accounts.')
console.error(err)
return
}
if (accs.length === 0) {
window.alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.")
return
}
this.setState({coinbase: accs[0]})
var accountsAndBalances = accs.map((account) => {
return this._getAccountBalance(account).then((balance) => { return { account, balance } })
})
Promise.all(accountsAndBalances).then((accountsAndBalances) => {
this.setState({accounts: accountsAndBalances, coinbaseAccount: accountsAndBalances[0]})
})
}.bind(this))
}
componentDidMount() {
const refreshBalances = () => {
this._getAccountBalances()
}
refreshBalances()
setInterval(()=>{
refreshBalances();
return refreshBalances
}, 5000)
}
render() {
return (
<div>
<AccountList accounts={this.state.accounts} />
<SendCoin sender={this.state.coinbase} />
</div>
)
}
}
export default AccountListContainer
Here's the truffle.js
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
},
staging: {
host: "localhost",
port: 8546,
network_id: 1337
}
}
};
Given the error Module build failed: Error: You must specify the network name to deploy to. (network), I suspect your truffle.js file has not been updated to the new truffle 3 spec.
Review how the config changed at the migration guided. It should look something like.
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
},
staging: {
host: "localhost",
port: 8546,
network_id: 1337
},
ropsten: {
host: "158.253.8.12",
port: 8545,
network_id: 3
}
}
};

HapiJS global path prefix

I'm writing an API on HapiJS, and wondering how to get a global prefix. For example, all requests should be made to:
https://api.mysite.com/v0/...
So I'd like to configure v0 as a global prefix. The docs (here) don't seem to mention it -- is there a good way to do this in HapiJS?
If you put your API routing logic inside a Hapi plugin, say ./api.js:
exports.register = function (server, options, next) {
server.route({
method: 'GET',
path: '/hello',
handler: function (request, reply) {
reply('World');
}
});
next();
};
exports.register.attributes = {
name: 'api',
version: '0.0.0'
};
You register the plugin with a server and pass an optional route prefix, which will be prepended to all your routes inside the plugin:
var Hapi = require('hapi');
var server = new Hapi.Server()
server.connection({
port: 3000
});
server.register({
register: require('./api.js')
}, {
routes: {
prefix: '/v0'
}
},
function(err) {
if (err) {
throw err;
}
server.start(function() {
console.log('Server running on', server.info.uri)
})
});
You can verify this works by starting the server and visiting http://localhost:3000/v0/hello.
I was able to get it working for all routes with
var server = new Hapi.Server()
...
server.realm.modifiers.route.prefix = '/v0'
server.route(...)
Matt Harrisson's answer is the hapi way to do it using plugins.
Alternatively if you don't want to create a plugin just to add a prefix, you can by hand, add the prefix to all your routes.
For instance I went for something like this:
var PREFIX = '/v0';
var routes = [/* array with all your root */];
var prefixize = function (route) { route.path = PREFIX + route.path;return route; }
server.route(routes.map(prefixize));
Good point is that with something like this your can perform express-like mounting. ex:
var prefixize = function (prefix, route) { route.path = prefix + route.path;return route; }
server.route(adminRoutes.map(prefixize.bind({}, "/admin"))); //currying.
server.route(apiRoutes.map(prefixize.bind({}, "/api")));
For Hapi 19, 20 ... you can simply modify the route with map path before you register it.
// Example route
const routes = [
{
method: 'GET',
path: '/test',
handler: function (request) {
return {
status: 'success'
};
}
}
];
// transform /test -> /api/test
routes.map((r) => {
r.path = `/api${r.path}`;
return null;
});
// Register
server.route([
...routes
]);
you can always start your index.js like this
if (!global.PREFIX) {
global.PREFIX = '/v0';
}
this way everywhere inside your code you'll have access to PREFIX
that's how you can access to PREFIX
console.log(PREFIX); or var name = PREFIX+ "_somename";
Take a look at hapi-auto-route. This plugin automaticaly register your routes from a directory
// Directory structure
//
// node_modules/
// routes/
// home.js
// server.js
// package.json
// routes/home.js
'use strict';
module.exports = {
method: 'GET',
path: '/',
handler: (request, h) => 'Hello';
}
// server.js
'use strict';
const Hapi = require('hapi');
const server = Hapi.Server({
port: 3000,
host: 'localhost'
});
const init = async () => {
await server.register(require('hapi-auto-route'));
await server.start();
console.log(`Server is running at: ${server.info.uri}`);
};
process.on('unhandledRejection', (error) => {
console.log(error);
process.exit();
});
init()
and add prefix to it:
I’m late to this party but this came up in search results.. FWIW, I'm using this, built off of AdrieanKhisbe’s answer. It allows for setting multiple global prefixes and using sub-route prefixes (similar to how Django urls are laid out). Here is a sample with multiple route.js files and api route versions (the route handlers moved out for clarity):
/departments/routes.js
const { getDepartments, getDepartmentById } = require('./handlers');
module.exports = [
{ method: 'GET', path: '', handler: getDepartments },
{ method: 'GET', path: '/{id}', handler: getDepartmentById }
];
/users/routes.js
const { getUsersV1, getUserByIdV1, getUsersV2, getUserByIdV2 } = require('./handlers');
const userRoutesV1 = [
{ method: 'GET', path: '', handler: getUsersV1 },
{ method: 'GET', path: '/{id}', handler: getUserByIdV1 }
];
const userRoutesV2 = [
{ method: 'GET', path: '', handler: getUsersV2 },
{ method: 'GET', path: '/{id}', handler: getUserByIdV2 }
];
module.exports = { userRoutesV1, userRoutesV2 };
index.js
const Hapi = require('#hapi/hapi');
const departmentRoutes = require('./departments/routes');
const { userRoutesV1, userRoutesV2 } = require('./users/routes');
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost',
});
const allRoutes = [];
const v1 = '/api/v1/';
const v2 = '/api/v2/';
const prefixer = (routeArray, apiPrefix, subRoutePrefix) => {
routeArray.map(route => {
route.path = `${apiPrefix}${subRoutePrefix}${route.path}`;
allRoutes.push(route);
});
};
prefixer(departmentRoutes, v1, 'departments');
prefixer(userRoutesV1, v1, 'users');
prefixer(userRoutesV2, v2, 'users');
server.route(allRoutes);
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', err => {
console.log(err);
process.exit(1);
});
init();
Here is how I implemented mine
I created a helper function that takes an Array of Hapi.ServerRoute then map through it and concatenate the prefix then return the array.
The snippets are in Typescript so if you're using JavaScript just strip off the types
// Helper function
export function routerGroup (namespace: string, routes: Hapi.ServerRoute[]) {
return routes.map(r => {
r.path = namespace + r.path
return r
})
}
// Routes declarations
export default routerGroup('/v1/api', [
{
method: 'POST',
path: '/login',
options: {
validate: {
payload: Joi.object({
email: Joi.string().required().email(),
password: Joi.string().required().min(8).max(30)
})
},
auth: false
},
handler: Authentication.adminLogin
}
] as Hapi.ServerRoute[]
)
// Register routes to Hapi server
server.route(
[
...v1Routes,
...
]
)
server.realm.modifiers.route.prefix = '/api/v2'
await server.route(yourroutes);
This should work fine, however if you want to be able to parse all the routes automatically from your routes directory/file Hapi Router. You would be able to do something like this which will save you a lot of time.
await server.register({
plugin: HapiRouter,
options: {
routes: "./src/routes/product-routes.js",
},
}, {
routes: {
prefix: "/api/v1"
}
});
Your route file should look like this.
export default [{
method: "GET",
path: "/products",
options: {
tags: ["api", "Products"],
description: "Get All Products",
},
handler: () => {...}
}]

Resources