gulp-webserver serving static files with middleware - gulp

I am trying to serve some static content for a protractor test using gulp in conjunction with gulp-webserver.
For all routes that start with '/mocks' I want to serve from base path test/mocks (this is the way that the build server is set up - I can't change that).
After much trial and error I got to a configuration that looks like this:
gulp.task('serve-e2e', ['build-dev'], function() {
gulp.src('app').pipe(webserver({
port: 9001,
middleware: [
{route: '/mocks', handle: serveStatic('test/mocks')}
]
}));
});
However, that still gives me 404 errors (I've verified the folder location - nothing wrong there) for everything starting with /mocks.
I've even tried this - looking at the code in gulp-webserver, middleware only accepts functions - still to no avail:
gulp.task('serve-e2e', ['build-dev'], function() {
gulp.src('app').pipe(webserver({
port: 9001,
middleware: [
getStatic({route: /^\/mocks/, handle: serveStatic('test/mocks')})
]
})
);
function getStatic(opts) {
return function(req, res, next) {
if (parseurl(req).pathname.match(opts.route)) {
return opts.handle(req, res, next);
} else {
return next();
}
}
}
I'm sure there's a better way to do this but I'm stumped. Can anybody help me?

So it seems that the original route is included in the path which under the above code would result in test/mocks/mocks.
While it is still necessary to add the getStatic method (gulp-webserver doesn't forward an object to connect - which would be an option if the object contained keys 'route' and 'handle'), the problem was solved for me when I changed it to
getStatic({route: /^\/mocks/, handle: serveStatic('test')})

Related

How to prevent Vercel routing to not finding the page

I am building a project using NextJs and Vercel, but, when the users try to access a new page or route, the Vercel gives them the 404 error.
In other projects, I used Netlify as router and this error was fixed using the netlify.toml config file, but, I am not able to do the same using the vercel.json file.
Can you guys help me to turn this file:
netlify.toml
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Into a vercel.json config file?
I was trying with this settings:
vercel.json
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html"}]
}
But it did not solved my issue.
A workaround is to use a catch all route that immediately redirects to the index page. For example:
// [...404].jsx
export default function Page() {
return null;
}
export function getServerSideProps() {
return {
redirect: {
destination: '/',
permanent: false,
},
};
}

Nextjs/Vercel error: only absolute URLs are supported. Where is the '.json' in my route params coming from?

The Problem:
I'm new to Next.js (1 month) and Vercel (1 day), and between them something appears to be inserting .json in my urls on the search route, causing them to fail with error:
[GET] /_next/data/9MJcw6afNEM1L-eax6OWi/search/hand.json?term=hand
10:21:52:87
Function Status:None
Edge Status:500
Duration:292.66 ms
Init Duration: 448.12 ms
Memory Used:88 MB
ID:fra1:fra1::ldzhz-1644484912454-0a30b71b6c90
User Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (/var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:61917)
at /var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:63448
at new Promise (<anonymous>)
at Function.fetch [as default] (/var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:63382)
at fetchWithAgent (/var/task/node_modules/next/dist/server/node-polyfill-fetch.js:38:39)
at getServerSideProps (/var/task/.next/server/chunks/730.js:238:28)
at Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:566:26)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async doRender (/var/task/node_modules/next/dist/server/base-server.js:855:38)
at async /var/task/node_modules/next/dist/server/base-
server.js:950:28
2022-02-10T09:21:53.788Z 994c9544-0bbe-4a68-af83-f0e4c322151e ERROR
Error: Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?
at Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:592:19)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async doRender (/var/task/node_modules/next/dist/server/base-server.js:855:38)
at async /var/task/node_modules/next/dist/server/base-server.js:950:28
at async /var/task/node_modules/next/dist/server/response-cache.js:63:36 { page: '/search/[term]'}
RequestId: 994c9544-0bbe-4a68-af83-f0e4c322151e Error: Runtime exited with error: exit status 1
Runtime.ExitError
Though the browser says https://.../search/hand as it should.
No such thing is happening on my local server build though, and it works perfectly well.
Background/Code Snippets:
The search route is the only route that uses SSR, and is also the only route with this issue. It is a dynamic route, so it seems either next in production or vercel expects some kind of json for it -presumably pre-rendered content-, and is replacing the route URL with json.
Also I have had to use the VERCEL_URL environment variable to prepare a URL for fetch requests, so this may also be messing up the URL, but the .json in the error message makes me think otherwise, since search should not be pre-rendered.
The Page Structure For the Search Route (Index imports the component in [term] and defines its own getServerSide props to accommodate a search route without a param):
|-Search
|- [term].js
|- Index.js
The Code For [term].js:
...
export default function Search({results, currentSearch}){
...
}
export async function getServerSideProps(req) {
const { criteria, page } = req.query;
const { term } = req.params || { term: '' };
try {
const data = await fetch(`${process.env.VERCEL_URL}/api/search/${term}?criteria=${criteria || 'name'}&page=${page}`);
const searchRes = await data.json();
return {
props: {
results: searchRes.data,
currentSearch: searchRes.query
}
}
} catch (e) {
console.log(e)
}
}
Index.js is similar:
import Search from "./[term]";
export default Search;
export async function getServerSideProps(req) {
const { criteria, page } = req.query;
const { term } = req.params || { term: '' };
if(!term){
return {
props: {
results: [],
currentSearch: {}
}
}
}
try {
const data = await fetch(`${process.env.VERCEL_URL}/api/search/${term}?criteria=${criteria || 'name'}&page=${page}`);
const searchRes = await data.json();
return {
props: {
results: searchRes.data,
currentSearch: searchRes.query
}
}
} catch (e) {
console.log(e)
}
}
The API I'm trying to fetch from is confirmed to be working, so this problem is strictly regarding pages, or .json being provided to the fetch method from router params.
It would turn out that VERCEL_URL is actually an absolute URL (It does not include a protocol). I had to deploy console.log statements to find this. A little embarrassed that I missed it in the docs.
The .json was not actually in the query or params, and therefore not in the fetch request. The fetch failed because the url had no protocol.
The .json in the page url must be from Next's internal operations, and does not mean the page is being built ahead of time. Yes it is being rendered using some json, but my thinking that the json indicates a pre-rendered page(SSG/ISR) was wrong. This must mean Server Side Rendering will also make use of such json, but only at runtime, when the request is made.
The use of .json after the params slug in the GET requests for a page has no bearing on the internal flow of your app, provided it has worked correctly. If you see it in error messages, know that it is from Next and examine other parts of the code at the point of failure.
The page structure I attempted ([param].js + index.js in the same
directory) is fine, which is why my local build could work properly.
I want to delete this question because the solution is essentially one that a thorough look in the docs would have revealed, but at the same time I think the mistake itself is an easily made one and that some of the conclusions listed above(particularly the one about json being used in all next routes) could save time spent debugging for some new users of Next/Vercel.

node express: Issue with res.render() after writing file to disk

I'm writing a method that uses async/await and promises to write some JSON to a file and then render a pug template. But for some reason the code that writes the JSON conflicts with the res.render() method resulting in the browser not being able to connect to the server.
The weird thing is that I don't get any errors in the console, and the JSON file is generated as expected — the page just won't render.
I'm using the fs-extra module to write to disk.
const fse = require('fs-extra');
exports.testJSON = async (req, res) => {
await fse.writeJson('./data/foo.json', {Key: '123'})
.then(function(){
console.log('JSON updated.')
})
.catch(function(err){
console.error(err);
});
res.render('frontpage', {
title: 'JSON Updated...',
});
}
I'm starting to think that there is something fundamental I'm not getting that conflicts with promises, writing to disk and/or express' res.render method. It's worth noting that res.send() works fine.
I've also tried a different NPM module to write the file (write-json-file). It gave me the exact same issue.
UPDATE:
So I'm an idiot. The problem has nothing to do with Express og the JSON file. It has to do with the fact that I'm running nodemon to automatically restart the server when files are changed. So as soon as the JSON file was saved the server would restart, stopping the process of rendering the page. Apologies to the awesome people trying to help me anyway. You still helped me get to the problem, so I really appreciate it!
Here's the actual problem:
The OP is running nodemon to restart the server whenever it see filechanges, and this is what stops the code from running, because as soon as the json file is generated the server restarts.
Efforts to troubleshoot:
It's going to take some trouble shooting to figure this out and since I need to show you code, I will put it in an answer even though I don't yet know what is causing the problem. I'd suggest you fully instrument things with this code:
const fse = require('fs-extra');
exports.testJSON = async (req, res) => {
try {
console.log(`1:cwd - ${process.cwd()}`);
await fse.writeJson('./data/foo.json', {Key: '123'})
.then(function(){
console.log('JSON updated.')
}).catch(function(err){
console.error(err);
});
console.log(`2:cwd - ${process.cwd()}`);
console.log("about to call res.render()");
res.render('frontpage', {title: 'JSON Updated...',}, (err, html) => {
if (err) {
console.log(`res.render() error: ${err}`);
res.status(500).send("render error");
} else {
console.log("res.render() success 1");
console.log(`render length: ${html.length}`);
console.log(`render string (first part): ${html.slice(0, 20}`);
res.send(html);
console.log("res.render() success 2");
}
});
console.log("after calling res.render()");
} catch(e) {
console.log(`exception caught: ${e}`);
res.status(500).send("unknown exception");
}
}

Server not returning JSON from Express to React (proxy)

I'm attempting to make an application that has a React frontend (running on port 8080) and an Express-Node.js backend (on port 3000). I'd like for my client to use fetch to request data from my server. So far, what I've read online indicates that I need to add a proxy entry to my package.json with the value of http://localhost:3000. I've done this, my server receives the request correctly, but its response is not what I expect (a JSON object). What am I doing wrong?
//Server
app.get('/search', function(req, res) {
...
//console.log(section) <-- Is the correct value
res.json(section);
})
...
app.listen(3000)
//Client
handleTouchTap() {
fetch('/search?crn=10001').then(function(response) { //<-- Hard-coded for testing
return response; //<-- Does not contain the value of "section" from server
}).then(function(data) {
console.log(data); //<-- Likewise, does not contain the value
});
}
//From package.json
...
"proxy": "http://localhost:3000",
...
You need to pull the json out of your response:
fetch('/search?crn=10001')
.then(response => response.json())
.then(section => console.log(section));

Component not rendered when calling "replace" in onEnter

I am trying to implement simple authentication with react-router. I think there is an issue with replace and callback. Consider following code:
1) Routing configuration
function getRoutes() {
return {
path: "/",
indexRoute: require("./Home"),
component: require("./Shared/Layout"),
onEnter: handleEnter,
childRoutes: [
require("./Login"),
require("./Secured"),
require("./Any")
]
}
}
function handleEnter(nextState, replace, callback) {
let state = store.getState()
if (!state.hasIn(["shared", "user"])) {
store.dispatch(fetchUser())
.then(callback)
}
}
2) ./Secured route configuration
export = {
path: "/secured",
component: require("./Secured"),
onEnter(nextState, replace) {
let state = store.getState()
if (!state.getIn(["shared", "user"])) {
replace("/login")
}
}
}
It should work like this:
Fetch user when entering root route (async operation, we need callback)
Go to /secured and check whether user is authenticated when entering the route
If user is not authenticated go to /login
The problem is that the /login page will not be rendered. The URL is changed to /login, but nothing is displayed and there are no error messages in console. When I remove callback parameter from the root route configuration, it starts working as expected.
Am I doing something wrong?
Well, it was really stupid :) I've forgot to call callback when user is already authenticated.