This question already has an answer here:
Fetch error when building Next.js static website in production
(1 answer)
Closed last year.
I have a very simple NextJS 9.3.5 project.
For now, it has a single pages/users and a single pages/api/users that retrieves all users from a local MongoDB table
It builds fine locally using 'next dev'
But, it fails on 'next build' with ECONNREFUSED error
page/users
import fetch from "node-fetch"
import Link from "next/link"
export async function getStaticProps({ params }) {
const res = await fetch(`http://${process.env.VERCEL_URL}/api/users`)
const users = await res.json()
return { props: { users } }
}
export default function Users({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
<Link href="/user/[id]" as={`/user/${user._id}`}>
<a>{user.name}</a>
</Link>
</li>
))}
</ul>
);
}
pages/api/users
import mongoMiddleware from "../../lib/api/mongo-middleware";
import apiHandler from "../../lib/api/api-handler";
export default mongoMiddleware(async (req, res, connection, models) => {
const {
method
} = req
apiHandler(res, method, {
GET: (response) => {
models.User.find({}, (error, users) => {
if (error) {
connection.close();
response.status(500).json({ error });
} else {
connection.close();
response.status(200).json(users);
}
})
}
});
})
yarn build
yarn run v1.22.4
$ next build
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`
> Info: Loaded env from .env
Creating an optimized production build
Compiled successfully.
> Info: Loaded env from .env
Automatically optimizing pages ..
Error occurred prerendering page "/users". Read more: https://err.sh/next.js/prerender-error:
FetchError: request to http://localhost:3000/api/users failed, reason: connect ECONNREFUSED 127.0.0.1:3000
Any ideas what is going wrong ? particularly when it works fine with 'next dev' ?
Thank you.
I tried the same few days ago and didn't work... because when we build the app, we don't have localhost available... check this part of the doc - https://nextjs.org/docs/basic-features/data-fetching#write-server-side-code-directly - that said: "You should not fetch an API route from getStaticProps..." -
(Next.js 9.3.6)
Just to be even more explicit on top of what Ricardo Canelas said:
When you do next build, Next goes over all the pages it detects that it can build statically, i.e. all pages that don't define getServerSideProps, but which possibly define getStaticProps and getStaticPaths.
To build those pages, Next calls getStaticPaths to decide which pages you want to build, and then getStaticProps to get the actual data needed to build the page.
Now, if in either of getStaticPaths or getStaticProps you do an API call, e.g. to a JSON backend REST server, then this will get called by next build.
However, if you've integrated both front and backend nicely into a single server, chances are that you have just quit your development server (next dev) and are now trying out a build to see if things still work as sanity check before deployment.
So in that case, the build will try to access your server, and it won't be running, so you get an error like that.
The correct approach is, instead of going through the REST API, you should just do database queries directly from getStaticPaths or getStaticProps. That code never gets run on the client anyways, only server, to it will also be slightly more efficient than doing a useless trip to the API, which then calls the database indirectly. I have a demo that does that here: https://github.com/cirosantilli/node-express-sequelize-nextjs-realworld-example-app/blob/b34c137a9d150466f3e4136b8d1feaa628a71a65/lib/article.ts#L4
export const getStaticPathsArticle: GetStaticPaths = async () => {
return {
fallback: true,
paths: (await sequelize.models.Article.findAll()).map(
article => {
return {
params: {
pid: article.slug,
}
}
}
),
}
}
Note how on that example, both getStaticPaths and getStaticProps (here generalized HoC's for reuse, see also: Module not found: Can't resolve 'fs' in Next.js application ) do direct database queries via sequelize ORM, and don't do any HTTP calls to the external server API.
You should then only do client API calls from the React components on the browser after the initial pages load (i.e. from useEffect et al.), not from getStaticPaths or getStaticProps. BTW, note that as mentioned at: What is the difference between fallback false vs true vs blocking of getStaticPaths with and without revalidate in Next.js SSR/ISR? reducing client calls as much as possible and prerendering on server greatly reduces application complexity.
Related
Is it possible to host a Flutter web app on a local environment using a Flutter desktop-based app?
The google-search for a solution like this can be difficult, since it involves many keywords that lead to similar situations (online hosting when you need a local solution, command-line only solution, and so on).
After some digging, I ended up using the shelf package to deploy my own Flutter web app on a local network. I developed this for Windows only, so I can't guarantee it will work on other platforms.
First thing to do is obviously adding the shelf package in your pubspec.yaml: after that, this is how my main method looks like
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf_router/shelf_router.dart' as shelf_router;
[...]
void main() async{
[...]
var secureContext = SecurityContext();
try {
//privKey and cert are the String names of the two files for the SSL connection,
//placed in the root directory of the flutter project or along with the .exe file (when released)
secureContext.usePrivateKey(privKey);
secureContext.useCertificateChain(cert);
} catch (error) {
logger.e("Error on init SecurityContext");
}
try {
//this is the handler that deploys the files contained in 'webAppFolder': I just simply pasted the result of
//the flutter webapp building inside (the index.html file is the default one for flutter web)
//and put the folder in the root of the flutter project (or, again, in the same folder with the .exe file when released)
final _staticHandler = createStaticHandler("webAppFolder", defaultDocument: 'index.html');
//this I kept just for a reminder on how to deploy a static page, if needed
final _router = shelf_router.Router()
..get(
'/time',
(request) => shelf.Response.ok(DateTime.now().toUtc().toIso8601String()),
);
final cascade = shelf.Cascade()
.add(_staticHandler)
.add(_router);
try {
var server = await shelf_io.serve(
cascade.handler,
InternetAddress.anyIPv4,
mainPort, //this is the number of the port on which the webapp is deployed (I load this from a .ini file beforehand
securityContext: secureContext,
);
// Enable content compression
server.autoCompress = true;
logger.i("Serving at https://${server.address.host}:${server.port}");
} catch (err) {
logger.e("Error while serving");
logger.e(err.toString());
}
} catch (err) {
logger.e("Error while creating handler");
logger.e(err.toString());
}
runApp(MaterialApp(
[...]
This is the part related to the deploy of a web app: since the flutter desktop app already provides a GUI, I used that to add some maintenance and testing utilities to check if everything is working fine.
For more details regarding shelf, refer to their API on their pub.dev page.
I'm new and learnig React and mySQL. I've just finished my app with React and mySQL(WorkBench) and I'd like to deploy my app with Amplify. I tried to deploy it through my github repo but URL created for app shows 'Page not found'.
(I'm asked to deploy AWS Amplify from company.)
To be honest, I don't know whether my folder composition is correct to deploy and how to deploy full stack app with amplify.(I could deploy SPA app...)
Below link is my app folder structure.
Below is my app.js code(lambda function with Express)
const express = require('express')
const bodyParser = require('body-parser')
const awsServerlessExpressMiddleware = require('aws-serverless-
express/middleware')
const mysql = require('mysql2')
const connection = mysql.createConnection({
host: 'database-1.clw5xqnhqbio.us-east-1.rds.amazonaws.com',
user: 'root',
password: 'Ryotakagi',
database: 'awstaskmanager'
})
const app = express()
app.use(bodyParser.json())
app.use(awsServerlessExpressMiddleware.eventContext())
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "*")
next()
});
app.get('/tasks', (req, res) => {
const TASK_QUERY = "select * from awstaskmanager.tasks"
connection.query(TASK_QUERY, (err, response) => {
if (err) console.log(err);
else res.send(response)
})
})
app.post('/addTask', (req, res) => {
const ADD_QUERY = `insert into awstaskmanager.tasks (task)
values ('${req.body.task}')`
connection.query(ADD_QUERY, (err) => {
if (err) console.log(err);
else res.send('add task')
})
})
app.delete('/deleteTask/:taskid', (req, res) => {
console.log(req.params.taskid);
const DELETE_QUERY = `DELETE FROM awstaskmanager.tasks where
(taskid=${req.params.taskid})`
connection.query(DELETE_QUERY, (err, res) => {
if (err) console.log(err);
})
})
app.listen(3000, function() {
console.log("App started")
});
module.exports = app
Of course, my local environment is correctly working.
I need to submit this app to my company I want to join, so I REALLY want to complete this app...
So, could you tell me the folder structure is right and how to deploy full stack app which is already finished?
You need to convert your node/express server into Lambda function.
Specific server directory is not necessary, since Amplify won't run this server. So you need to move your node/express code into Lambda function you've created.
If you're using MySQL, you can use AWS RDS using AWS SDK
When using Amplify(serverless stack), all your backend should be separated from the project. Amplify Hosting can't run NodeJS server like for example using pm2
so since you've created an Amplify project, you can add Lambda function using amplify add function command and choose Express server.
and copy your node/express code into that and run amplify push
for your frontend, you can access the node/express server using the name you provided when creating a Lambda function. Basically when Lambda function created and deployed (using amplify push command), Amplify CLI stores the API Gateway URL in your project, in aws-exports.js.
EDIT
If you want to have your React app inside client folder, you need to edit build settings, since you need to cd into client and run npm run build
But if your structure would be like this, you don't need to configure.
> amplify
> node_modules
> public
> src
> package.json
...
I'm trying to make a request to the testnet but getting the following error:
HTTP Request Error: An error occurred when interacting with the Access API.
transport=FetchTransport
error=Failed to fetch hostname=access.devnet.nodes.onflow.org:9000
path=/v1/scripts?block_height=sealed
method=POST
requestBody={
"script":"aW1wb3J0IEZ1bmdpYmxlVG9rZW4gZnJvbSAweDlhMDc2NmQ5M2I2NjA4YjcKaW1wb3J0IEZVU0QgZnJvbSAweGUyMjNkOGE2MjllNDljNjgKCnB1YiBmdW4gbWFpbihhZGRyZXNzOiBBZGRyZXNzKTogVUZpeDY0IHsKICAgIGxldCBhY2NvdW50ID0gZ2V0QWNjb3VudChhZGRyZXNzKQoKICAgIGxldCB2YXVsdFJlZiA9IGFjY291bnQuZ2V0Q2FwYWJpbGl0eSgvcHVibGljL2Z1c2RCYWxhbmNlKSEKICAgICAgICAuYm9ycm93PCZGVVNELlZhdWx0e0Z1bmdpYmxlVG9rZW4uQmFsYW5jZX0+KCkKICAgICAgICA/PyBwYW5pYygiQ291bGQgbm90IGJvcnJvdyBCYWxhbmNlIHJlZmVyZW5jZSB0byB0aGUgVmF1bHQiKQoKICAgIHJldHVybiB2YXVsdFJlZi5iYWxhbmNlCn0=",
"arguments":["eyJ0eXBlIjoiQWRkcmVzcyIsInZhbHVlIjoiMHg1ODA2MjJlNzQ1MTgzYjE2In0="]
}
The base64 decoded script is:
import FungibleToken from 0x9a0766d93b6608b7
import FUSD from 0xe223d8a629e49c68
pub fun main(address: Address): UFix64 {
let account = getAccount(address)
let vaultRef = account.getCapability(/public/fusdBalance)!
.borrow<&FUSD.Vault{FungibleToken.Balance}>()
?? panic("Could not borrow Balance reference to the Vault")
return vaultRef.balance
}
and the arguments are:
{"type":"Address","value":"0x580622e745183b16"}
When I run the following command in the cli, it works: flow scripts execute -n testnet scripts/getFUSDBalance.cdc --arg "Address:580622e745183b16"
not sure why I'm getting issues with this
EDIT:
didn't mention that the error is coming from FCL
Here's the code I'm using to interact with Flow:
async getFUSDBalance(): Promise<Result<number, string>> {
const scriptText = getFUSDBalance as string;
const user = await this.getCurrentUser();
return await flow.query<number>({
cadence: scriptText,
payer: fcl.authz,
authorizations: [fcl.authz],
args: (arg, t) => [
arg(user.addr, t.Address)
]
})
}
flow.query<T> is just a wrapper for fcl.query
It works in the CLI but not from FCL
EDIT 2:
So I found that the real issue I was facing was I was getting this error:
Fetch API cannot load access.devnet.nodes.onflow.org:9000/v1/scripts?block_height=sealed. URL scheme "access.devnet.nodes.onflow.org" is not supported
access.devnet.nodes.onflow.org:9000 is gRPC (I think). I should have been using https://rest-testnet.onflow.org. However, when I change to that I get a CORS violation. I think I read somewhere that you can't access the testnet from localhost (why?), but I deployed to a *.app domain and I'm getting the same error No 'Access-Control-Allow-Origin' header is present on the requested resource. Is the CORS policy not set up for *.app domains?
So turns out changing to https://rest-testnet.onflow.org for the accessNode.api value did work. Not sure why it didn't when I posted the second edit, but whatever. This works for localhost too.
In my Postman Mock Server, I have set up a GET request to return JSON, in which the following is returned
“due_date":"2021-10-10"
What I would like is to adjust the response so that the date is returned is two days in the past. So if today is “2021-10-10”, I would like the response to contain
“due_date":"2021-10-08”
And if today is “2022-01-01”, I would like the response to contain
“due_date":"2021-12-30”
And so on. How do I set up my Postman mock server request to return such data?
I think it's a good question besides I'm curious so I made some research and found a workaround for this. It's a bit complex. I'm not sure worth it or not.
The first thing all, Postman Mock Server (in short Mock Server) cannot execute any test and pre-script so it is not capable to compute things. You need a calculation here so what are you gonna do? Well, you can define an environment for Mock Server which gives you the ability to use dynamic values in mock responses.
I will continue step by step to show the process.
1 - Open a Mock Server with an environment:
1.1 - Create a collection for the new Mock Server:
Your mock response will look like below:
{"due_date": "{{date}}"}
1.2 - Create an environment:
1.3 - Finish to create:
1.4 - When you finish, Postman creates a collection like below:
1.5 - You can test your Mock Server from this collection:
As you can see, Mock Server uses the environment variable in their response.
Now, We have to figure out how to update the environment variable.
You have to use an external service to update your environment variable. You can use Postman Monitor for this job because it can execute tests (means any code) and works like a CRON job which means you can set a Postman Monitor to update a specific environment variable every 24 hours.
2 - Open a Postman Monitor to update your environment:
2.1 - This step is pretty straightforward, create a Postman Monitor like the below configuration:
2.2 - Write a test to update the environment:
The test will look like below:
// you have to use pm.test() otherwise Postman Monitor not execute the test
const moment = require("moment");
pm.test("update date", () => {
// set date 2 days past
let startdate = moment();
const dayCount = 2;
startdate = startdate.subtract(dayCount, "days");
startdate = startdate.format("YYYY-MM-DD");
// this is not work on Postman Monitor, use Postman API like below
//pm.environment.set('date', startdate);
const data = JSON.stringify({
environment: {
values: [
{
key: "date",
value: startdate,
},
],
},
});
const environmentID = "<your-environment-id>";
// Set environment variable with Postman API
const postRequest = {
url: `https://api.getpostman.com/environments/${environmentID}`,
method: "PUT",
header: {
"Content-Type": "application/json",
"X-API-Key":
"<your-postman-api-key>",
},
body: {
mode: "raw",
raw: data,
},
};
pm.sendRequest(postRequest, (error, response) => {
console.log(error ? error : response.json());
// force to fail test if any error occours
if (error) pm.expect(true).to.equal(false);
});
});
You cannot change an environment variable with pm.environment when you using Postman Monitor. You should use Postman API with pm.sendRequest in your test.
You need to get a Postman API key and you need to learn your environment id. You can learn the environment id from Postman API.
To learn your Environment ID, use this endpoint: https://www.postman.com/postman/workspace/postman-public-workspace/request/12959542-b7ace502-4a5a-4f1c-8164-158811bbf236
To learn how to get a Postman API key: https://learning.postman.com/docs/developer/intro-api/#generating-a-postman-api-key
2.3 - Run Postman Monitor manually to make sure tests are working:
2.4 - As you can see Postman Monitor execute the script:
2.5 - When I check the environment, I can see the result:
You can test from browser to see results:
I have answered this question earlier but I have another solution.
You can deploy a server to update the variable from your mock environment. If you want to do it for free, just use Heroku.
I wrote a Flask app in Python and deploy it to Heroku, check below code:
from flask import Flask
import os
import json
import requests
from datetime import datetime, timedelta
app = Flask(__name__)
# the port randomly assigned and then mapped to port 80 by the Heroku
port = int(os.environ.get("PORT", 5000))
# debug mode
debug = False
#app.route('/')
def hello_world():
N_DAYS_AGO = 2
# calculate date
today = datetime.now()
n_days_ago = today - timedelta(days=N_DAYS_AGO)
n_days_ago_formatted = n_days_ago.strftime("%Y-%m-%d")
# set environment
payload = json.dumps({
"environment": {
"values": [
{
"key": "occupation",
"value": n_days_ago_formatted
}
]
}
})
postman_api_key = "<your-postman-api-key>"
headers = {
'Content-Type': 'application/json',
'X-API-Key': postman_api_key
}
environment_id = "<your-environment-id>"
url = "https://api.getpostman.com/environments/" + environment_id
r = requests.put(url, data=payload, headers=headers)
# return postman response
return r.content
if __name__ == '__main__':
app.run(debug=debug, host='0.0.0.0', port=port)
Code calculates the new date and sends it to Mock Environment. It worked, I tested it in Heroku before this answer.
When you go to your Heroku app's page the code will trigger and the date environment automatically will update, use the environment variable in your mock server to solve the problem.
You need to automate this code execution so I suggest you use UptimeRobot to ping your Heroku app 1 time a day. On every ping, your environment variable will update. Don't overuse it because Heroku has a usage quota for the free plan.
To use this code you need to learn how to deploy a Flask app on Heroku. By the way, Flask is just an option here, you can use NodeJS instead of Python, the logic will stay the same.
I am trying to convert a simple webpage I have into a PWA in case the site it uses goes down.
I think I have done the majority of the work. The page is installable on my phone and passes all the Chrome lighthouse tests. But I get the following warning,
Web app manifest meets the installability requirements
Warnings: Page does not work offline. The page will not be regarded as installable after Chrome 93, stable release August 2021.
I also get the following warning and error in console,
The FetchEvent for "https://dannyj1984.github.io/index.html" resulted in a network error response: the promise was rejected.
Promise.then (async)
(anonymous) # serviceWorker.js:30
serviceWorker.js:1 Uncaught (in promise) TypeError: Failed to fetch
There is then a warning saying the site cannot be installed as does not work offline. I have read the chrome dev article which says from the chrome release in Aug21 apps that dont work offline wont be installable. But I am stuck on which part of my fetch is causing an issue. The code in my service worker is,
const TGAbxApp = "TG-ABX-App-v1"
const assets = [
//paths to files to add
]
self.addEventListener("install", installEvent => {
installEvent.waitUntil(
caches.open(TGAbxApp).then(cache => {
cache.addAll(assets)
})
)
})
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
The above code for the fetch part of the service worker I took from Google and as I understand it, it first checks if there is data in the cache stored on install, if not it will request it from the network.
https://developer.chrome.com/blog/improved-pwa-offline-detection/
From Chrome 89 March 2021, it gives a warning if this check does not pass:
The installed service worker fetch event returns an HTTP 200 status code (indicating a successful fetch) in simulated offline mode.
So, in your case, the service worker should return a cached 'index.html' when fetch(event.request) is failed.
I've had the same problem. I re enabled the cache through the developer console->network. fixed