I am trying to make a request to 3rd party API, and then show it to the user on the website. I am using Gatsby and Node.js. But I have some problem passing data from server to client.
This code is responsible for making a request to the server :
import React from "react"
import axios from "axios"
const Test = () => {
const getDataFromServer = async () => {
try {
const resp = await axios.get("http://localhost:8080/data")
console.log("From frontend:" + resp.data)
} catch (err) {
console.log(err)
}
}
return (
<div>
<button onClick={getDataFromServer}>Get Data</button>
</div>
)
}
export default Test
And here code responsible to make request to 3rd party API from Node:
require("dotenv").config()
const morgan = require("morgan")
const axios = require("axios")
const express = require("express")
const cors = require("cors")
const dataRoutes = require("./routes/dataRoute")
const app = express()
app.use(cors())
app.use("/api", dataRoutes)
app.use(morgan("dev"))
const api = `${process.env.GATSBY_API_URL}?key=${process.env.GATSBY_API_KEY}&scope=jobPost&fields=name,ingress,startDate,endDate,logo`
const axiosInstance = axios.create({
baseURL: api,
})
app.get("/data", async (req, res, next) => {
try {
const response = await axiosInstance.get("/<path>")
return response.data
} catch (error) {
console.log(error)
}
})
app.listen(8080, () => {
console.log("server is listening on port 8080")
})
I retrieve data from API but when I want to pass it to a client then nothing happening.
Thank you in advance! :)
Related
I am having some issues while creating second route on my project. While router.get("/getallrooms") works just fine, router.post("/getroombyid") is giving me 404 not found message:
POST http://localhost:3000/book/api/rooms/getroombyid 404 (Not Found)
I have attached my routes code, Booking screen, where I am trying to use this getroombyid route and the server.js code. I will gladly accept any suggestions.
const express = require('express');
const router = express.Router();
const Room = require('../models/room')
router.get("/getallrooms", async(req, res) =>{
try{
const rooms = await Room.find({})
res.send(rooms)
}
catch(error){
return res.status(400).json({message: error});
}
});
router.post("/getroombyid", async(req, res) => {
const roomid = req.body.roomid
try {
const room = await Room.findOne({'_id' : req.body.roomid})
res.send(room)
} catch (error) {
return res.status(400).json({ message: error });
}
});
module.exports = router
Bookingscreen
import React, {useState, useEffect} from 'react'
import axios from 'axios'
function Bookingscreen({match}) {
const [loading, setloading] = useState()
const [error, seterror] = useState()
const [room, setroom] = useState()
useEffect(() => {
try {
setloading(true)
async function gettingRoom() {
const data = (await axios.post('./api/rooms/getroombyid', {roomid : match.params.roomid})).data
setroom(data)
setloading(false)
}
gettingRoom()
}
catch (error) {
seterror(true)
console.log(error)
setloading(false)
}
}, [])
return (
<div>
<h1>Bookingscreen</h1>
<h1>Room id = {match.params.roomid}</h1>
</div>
)
}
export default Bookingscreen
server.js
const express = require("express");
const app = express();
const dbConfig = require('./db')
const roomsRoute = require('./routes/roomsRoute')
app.use(express.json())
app.use('/api/rooms', roomsRoute)
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`App listening on port ${port}`)
});
The problem is caused by the below line, specifically the ./api part:
const data = (await axios.post('./api/rooms/getroombyid', {roomid : match.params.roomid})).data
What you are doing is taking into account your current url inside the browser, which I assume is /book, so the final request is made to:
http://localhost:3000/book/api/rooms/getroombyid
Which doesn't exist on your back end. Change the above line with this one:
const data = (await axios.post('/api/rooms/getroombyid', {roomid : match.params.roomid})).data
I have a project where I use Next.js on the front-end and Express.js on the back.
Front-end side
The 'pages' file contains 'index.js'. In it, I am sending the following request.
import Home from "./home/home";
import axios from "axios";
import { useState } from "react";
export default function Index({ data }) {
const [products, setProducts] = useState(data);
const [start, setStart] = useState(1);
const getMoreProducts = async () => {
setStart(start + 1);
const { newProducts } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/test`
);
setProducts([...products, ...newProducts]);
};
return (
<div>
<Home data={products} />
<button onClick={getMoreProducts}> Load more {start}</button>
</div>
);
}
export async function getServerSideProps(context) {
// Fetch data from external API
const { data } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/productlist/pagination`,
{
params: {
page: 1,
limit: 5,
},
}
);
return {
props: {
data: data || {},
},
};
// Pass data to the page via props
}
Back-end side
const express = require("express");
var cors = require('cors')
const app = express();
require("dotenv").config();
const mongoose = require("mongoose");
mongoose.connect(
"*********",
{ useNewUrlParser: true }
);
const db = mongoose.connection;
db.on("error", (err) => console.error(err));
db.once("open", () => console.log("Connected to Database "));
app.use(express.json());
const productlistRouter = require("./routes/productlist");
const test = require("./routes/test");
app.use("/productlist", productlistRouter);
app.use("/test", test);
app.use(cors())
app.listen(3000, () => console.log("Server is running"));
And here is my Route code :
const express = require("express");
const router = express.Router();
const Product = require("../models/product");
const cors = require("cors");
const corsOptions = {
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
// ...
],
origin: "*",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
};
router.get("/showall", async (req, res) => {
try {
const product = await Product.find();
res.json(product);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
router.get("/pagination", cors(corsOptions), async (req, res) => {
const page = req.query.page;
const limit = req.query.limit;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const products = await Product.find();
const result = products.slice(startIndex, endIndex);
res.json(result);
});
So, When the page is first built with Next.js, the api works, but when I click the 'Load more' button, it gives a CORS error. When I use the same query with 'postman' and other tools, it does not give any error.
On the Next.js side, it works when I send a query to another 3rd party API., but it doesn't work when I send it to my own back-end. And no matter what page or component I do this in, only the APIs that are created at the build time are working.
What could be the reason for this? and how can i solve it? I've read and searched a few articles about cors, but I still haven't found a solution for days.
CORS should be placed on top level as javascript is executed one by one line. Place app.use(cors()) just above the line of app.use("/productlist", productlistRouter);
I have a express.js route whose code is below: -
const path = require("path");
const express = require("express");
const hbs = require("hbs");
const weather = require("./weather");
const app = express();
app.get("/weather", (req, res) => {
if (!req.query.city) {
return res.send({
error: "City Not Found",
});
}
res.send({
currentTemp: weather.temp,
});
});
And I also have a file to fetch data from api using axios whose code is here
const axios = require("axios");
axios
.get(
"https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d"
)
.then(function (response) {
module.exports = {
temp: response.data.main.temp
}
})
.catch(function (error) {
console.log("Error Spotter");
});
As I am using res.send I should get a json with currentTemp and the value of current temp should be temperature that I will get from weather.js file but I am getting a blank json array.
Try this.
You'll get the temperature in the localhost:3000
If you want to render the data for EJS (or something) instead of .then((data) => res.json(data.main.temp)) use:
.then((data) => res.render("index", { weather: data })
--
const URL =
"https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d"
const express = require("express")
const axios = require("axios")
const app = express()
const PORT = 3000
app.get("/", (req, res) => {
axios
.get(URL)
.then((response) => response.data)
.then((data) => res.json(data.main.temp))
.catch((err) => console.log(err))
})
app.listen(PORT, () => {
console.log(`Listening at http://localhost:${PORT}`)
})
module.exports is processed when the module is being defined. when you import weather, it does not exists therefore you get no data.
try to export a function which does the request and add a callback function as argument so you can pass the request result to it.
Change your weather.js to the following example:
const axios = require("axios");
let temperature;
async function getTemp() {
await axios
.get("https://api.openweathermap.org/data/2.5/weather?q=samalkha&appid=91645b79f9eac8808153c90472150f2d")
.then(function (response) {
temperature = response.data.main.temp
})
.catch(function (error) {
console.log("Error Spotter");
});
}
module.exports = {
temp: getTemp
}
This will actually return the fetched temperature.
I have built my portfolio webpage with next.js now I need to test it. to test the express server I use supertest. But the problem is I need to refactor express to use it. Because supertest need to access to app() before listening.
I started the way how I used to implement in node.js app. Put the express code in app.js and call it in index.js.
const express = require("express");
const server = express();
const authService = require("./services/auth");
const bodyParser = require("body-parser");
//put all the middlewares here
module.exports = server;
and then in index.js
const server = require("express")();
// const { parse } = require("url");
const next = require("next");
const routes = require("../routes");
const path = require("path");
require("./mongodb");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
// const handle = app.getRequestHandler(); //this is built in next route handler
const handle = routes.getRequestHandler(app);
app
.prepare()
.then(() => {
const server = require("./app");
//I required this outside too but it did not solve the issue
server.listen(3000, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
with this set up, express is listening, I am able connect to mongodb, during the start up there is no issue.
When i request to localhost:3000, there is no response from localhost, it is spinning till timeout
Create a test client:
// test-client.ts
import { createServer, RequestListener } from "http";
import { NextApiHandler } from "next";
import { apiResolver } from "next/dist/next-server/server/api-utils";
import request from "supertest";
export const testClient = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
return apiResolver(
req,
res,
undefined,
handler,
{
previewModeEncryptionKey: "",
previewModeId: "",
previewModeSigningKey: "",
},
false
);
};
return request(createServer(listener));
};
Test your APIs with:
// user.test.ts
import viewerApiHandler from "../api/user";
import { testClient } from "../utils/test-client";
const request = testClient(viewerApiHandler);
describe("/user", () => {
it("should return current user", async () => {
const res = await request.get("/user");
expect(res.status).toBe(200);
expect(res.body).toStrictEqual({ name: "Jane Doe" });
});
});
For those who want to add query parameters, here's the answer:
import { createServer, RequestListener } from 'http'
import { NextApiHandler } from 'next'
import { apiResolver } from 'next/dist/server/api-utils/node'
import request from 'supertest'
export const handlerRequester = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
let query = {}
let queryUrl = req.url.split('?')[1]
if (queryUrl) {
queryUrl
.split('&')
.map((p) => [p.split('=')[0], p.split('=')[1]])
.forEach((k) => {
query[k[0]] = k[1]
})
}
return apiResolver(
req,
res,
query,
handler,
{
previewModeEncryptionKey: '',
previewModeId: '',
previewModeSigningKey: '',
},
false
)
}
const server = createServer(listener)
return [request(server), server]
}
I've just released a new npm package which handle this case here:
https://www.npmjs.com/package/nextjs-http-supertest
Feel free to test it and give me feedback !
The app is using next.js connected to express, which is in turn connected to AWS MySql.
I'm trying to initially load some products I have stored on my database. However, i get the following network error:-
TypeError: NetworkError when attempting to fetch resource.
I troubleshooted the frontend code by using an external api and that works fine, so it's something to do with the express middleware.
See code excerpts below...
index.js
import '../scss/style.scss';
import NavBar from '../components/Navbar/Navbar';
import fetch from 'isomorphic-unfetch';
const Index = (props) => (
<div>
<NavBar />
<h1>See our products below yeah</h1>
</div>
)
Index.getInitialProps = async function() {
const res = await fetch('localhost:3000/');
const data = await res.json();
return {
posts: data
}
}
export default Index;
server.js
const express = require('express');
const next = require('next');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const db = require('../lib/db')
const escape = require('sql-template-strings')
app.prepare().then(() => {
const server = express()
server.get('/', (req, res, next) => {
let sql = 'SELECT * FROM products'
db.query(sql, [], (err, rows) => {
if (err) {
throw err;
} else {
return res.json({
data: rows
})
}
})
})
...
db.js
const mysql = require('serverless-mysql')
const db = mysql({
config: {
host: "XXX",
database: "XXX",
user: "XXX",
password: "XXX"
}
})
exports.query = async query => {
try {
const results = await db.query(query)
await db.end()
return results
} catch (error) {
return { error }
}
}
whenever you have api request from a different server you have to setup CORS on both frontend and backend
Link: https://expressjs.com/en/resources/middleware/cors.html