How to make external GET request using Nuxt.js Server Middleware - node.js

I am working with a Nuxt.js v2.15.8 project and I am attempting to use the server middleware feature that Nuxt offers for A custom API endpoint. https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/#custom-api-endpoint
What I am trying to accomplish:
Use Nuxt server middleware to make a GET request to a 3rd party api to retrieve data. When I try to set this up and make the request to the endpoint in Postman, I get an error
<!doctype html>
<html data-n-head-ssr lang="en" data-n-head="%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D">
<head>
<title>This page could not be found</title> etc....
How do I use the Nuxt server middleware to make api calls to external api's?
Nuxt.config.js
serverMiddleware: [
{
path: '/api/server-middleware',
handler: '~/api/getData.js',
},
],
~/api/getData.js
const bodyParser = require('body-parser');
const app = require('express')();
app.use(bodyParser.json());
app.all('https://jsonplaceholder.typicode.com/todos/1', (req, res) => {
res.json({ data: res.data });
});
module.exports = app;
In Postman I try to make a GET request to http://localhost:3000/api/server-middleware after running npm run dev and my Nuxt project is running.
Am I misunderstanding how this is supposed to work? Is the Server Middleware for internal api calls only?

Applying the least possible amount of changes to your shared code gives us the following
getData.js
import axios from 'axios'
const app = require('express')()
app.all('/jsonplaceholder/:id', async (req, res) => {
const { data } = await axios(
`https://jsonplaceholder.typicode.com/todos/${req.params.id}`
)
res.json({ ...data })
})
module.exports = app
/pages/index.vue
<template>
<div>
<input id="name" v-model="todoId" type="text" name="name" />
<button #click="callNuxtApi">try local Nuxt API</button>
<div>
Response from the backend:
<pre>{{ response }}</pre>
</div>
</div>
</template>
<script>
export default {
name: 'JsonPlaceholderPage',
data() {
return {
todoId: 1,
response: {},
}
},
methods: {
async callNuxtApi() {
const response = await this.$axios.$get(`/api/server-middleware/jsonplaceholder/${this.todoId}`)
console.log('response', response)
this.response = response
},
},
}
</script>
As you can see, /jsonplaceholder/:id is something more reasonable considering that it will be prefixed by /api/server-middleware/ already.
Having https:// inside of a path is not really nice to the browser overall.
PS: you need to install axios and express for it to work. #nuxtjs/axios will not work here.
This answer joins my other one here: https://stackoverflow.com/a/72102209/8816585

Related

fetch and get requests URL

I'm trying to get information with a fetch (client) and a get (server) requests to get data from the server with the client and printing it.
for some reason I can't get the information I'm looking for and I think it has somthing to do with the url I'm entering, can I get an explanation, or maybe an example about the url I'm supposed to enter?
I'll enter my code as an example:
client:
//bitcoin page: url - 'http://localhost:3000/bitcoin'
//NOTE: the proxy is: 'http://localhost:3001'
import React from "react";
import { Link } from "react-router-dom";
function BitCoin() {
const [data, setData] = React.useState(null);
console.log("entered bitcoin page");
React.useEffect(() => {
fetch("NOT SURE WHAT TO WRITE HERE")
.then((res) => res.json())
.then((data) => setData(data.message));
}, []);
return (
<div style={{textAlign:"center", fontFamily:"Comic Sans MC", fontSize:"100"}}>
THIS IS THE BitCoin PAGE
<nav>
<Link to="/"> Home </Link>
</nav>
<nav>
<Link to="/coins"> Coins </Link>
</nav>
<p>{!data ? "Loading..." : data}</p>
</div>
)
}
export default BitCoin;
server:
//index.js: url - 'http://localhost:3001'
const express = require("express");
const PORT = process.env.PORT || 3001;
const app = express();
app.get('NOT SURE WHAT TO WRITE HERE', (req, res) => {
console.log("entered bitcoin query!");
let msg = "";
//some functions to get the msg I'm looking for (not relevant)
res.json({ message: msg });
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
as you can see there's a log when entering the get request but the console's not logging it, I'm guessing the client and the server are not communicating and that's what makes the problem.
thank you <3
It's important to understand the general architecture of your technology stack:
You have the React frontend running under http://localhost:3000. This is simply serving the React app through a development server on your computer.
Additionally, you have a NodeJS app (using express) running at http://localhost:3001. Notably, this runs under a different port to the React app.
In your express code, you would define a url which the React frontend can call, to fetch data. So let's call it /coin-message:
app.get('/coin-message', (req, res) => {
Now, in the React app, you can make ajax requests to this url:
fetch("http://localhost:3001/coin-message")
Note that you need to include the full URL and port in the fetch() - this is because the Node app runs under a different port.
Important
Because you want to make ajax requests to a URL which has a different port to the React app, you will encounter a Same Origin Policy problem. This is a security measure activated by browsers to prevent web app vulnerabilities. To solve this, you can use one of the CORS middlewares for express, such as this one.
server:
app.get('/bitcoin', ...)
client:
fetch('http://localhost:3001/bitcoin')

express-generator and react app - network error 500 (internal server error)

I'm creating a react app with an express backend for the first time. I've already ran into an issue at the beginning. When I run the react app in the browser, I get a network 500 error, and the information I'm trying to access from the backend isn't shown.
Here is the code I changed in express folder routes/users:
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
//res.send('respond with a resource');
res.json([{
id: 1,
username: "sam"
}, {
id: 2,
username: "allie"
}]);
});
module.exports = router;
Here is my app.js filed in my react app:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
state = {users: []}
componentDidMount() {
fetch('/users')
.then(res => res.json())
.then(users => this.setState({ users }));
}
render() {
return (
<div className="App">
<h1>Users</h1>
{this.state.users.map(user =>
<div key={user.id}>{user.username}</div>
)}
</div>
);
}
}
export default App;
All I see on the screen is the 'Users' h1 tag I have in the app.js file.
Any help on why I'm getting the network error?
You should try fetching the whole backend domain. If you're using both react and express locally, chances are you're running them on different ports, so in the first section of your fetch method write the whole localhost for example
fetch("http://localhost:8000/users)
instead.

Heroku server api call (localhost) not working

I'm on the final stretch of my web app, and I'm trying to deploy it with Heroku. The backend is very simple, and just uses express and while I was coding, a simple localhost server to create a token on login (localhost:8080/login). Unfortunately, on Heroku the login and token generation will not work unless I'm using my computer and I've ran node server.js already in my terminal. Also not sure if this context is needed, but locally, my react app is launched at localhost:3000 while the server is localhost:8080/login. I've heard that I should change them both to be the same localhost with some slight changes to the url, but nothing's worked so far. What changes do I need to make to the relevant code below so that Heroku will use its own server for the api call?
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use('/login', (req, res) => {
res.send({
token: 'test123' // simple test send token
});
});
app.listen(8080, () => console.log('API is running on http://localhost:8080/login'));
Server.js (I have to run npm start, Ctrl+C, then node server.js in order to login properly)
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './Login.css';
async function loginUser(credentials) {
return fetch('http://localhost:8080/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
export default function Login({ setToken }) {
const [username, setUserName] = useState();
const [password, setPassword] = useState();
const handleSubmit = async e => {
e.preventDefault();
const token = await loginUser({
username,
password
});
setToken(token);
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<form onSubmit={handleSubmit}>
<label>
<p>Username</p>
<input type="text" onChange={e => setUserName(e.target.value)} />
</label>
<label>
<p>Password</p>
<input type="password" onChange={e => setPassword(e.target.value)} />
</label>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
)
}
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
Login component where token will be generated
This is due to your Heroku app's localhost is not pointing to your server application.
The most straight forward way to resolve this is to publish your server application on Heroku as well, then change your function loginUser to point to the URL to your server application's Heroku URL. You may later refactor the URL to refer to an environment variable so you have zero code change when developing locally and pushing to Heroku. This deploy guide will probably come in handy for you in this case.
If you do not intend to have publish your server application on Heroku due to any reason, you may also start your server on a local machine and expose it through ngrok, but you will need to refactor the fetch URL now as ngrok will have different URL every time you start it.

Problem trying to connect to a URL and getting an image back as a response in the view - Node.js & Express

I'm trying to make a GET request to a URL and getting an image from there as a response using node and express.
This is my code:
var app = require('express')();
var http = require('http').Server(app);
app.get('http://tapas.clarin.com', (req, res) => {
res.sendFile('index.html');
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
The view is showing a Cannot GET /.
How can I make this request work properly to show the recieved image in my view ?
HTML
<div id="tapas">
// I want to show the image from the response here!
</div>
<script src="./app.js"></script>
</body>
app.get() is used to create a GET API on your server. What you need is a package that can help you to make API calls to other servers and fetch data from there.
You could use the request-promise npm package to make life easier for you.
var rp = require('request-promise');
var options = {
uri: 'https://api.github.com/user/repos',
qs: {
access_token: 'xxxxx xxxxx' // -> uri + '?access_token=xxxxx%20xxxxx'
},
headers: {
'User-Agent': 'Request-Promise'
},
json: true // Automatically parses the JSON string in the response
};
rp(options)
.then(function (repos) {
console.log('User has %d repos', repos.length);
})
.catch(function (err) {
// API call failed...
});
EDIT: After re-reading your question, you don't really need to do a GET API call and fetch the image on your server before showing it on your website. You could directly pass the URL to your img tag like this -
<img src="https://url/image.png" alt="example">

NodeJS (Express) - app.delete route not working

Working on understanding CRUD basics with setting up simple routes from my HTML5 doc to Postgres database. My GET and POST buttons are working but my DELETE is not deleting from my database. I realize the routes all look very similar (and tried renaming them to see if it would hit the callback function that is linked to the database, but it didn't work). Can anyone tell me why my HTML5 form is not working with my route to reach the database for DELETE? Thanks!
I will only include the code I'm referring to as I have all the other code working well. Starting with showing the crappy HTML first, then the index.js with the routes, and then the queries.js with the database queries. ( ////// seperate the documents where the code is pulled :) )
<h1>Let's DELETE ONE Human</h1>
<form action="/users/:id" method="delete">
ID:<input type="number" name="id">
<input type="submit" name="">
</form>
/////////////////////////////////////////////////////////////////
app.get('/', (request, response) => {
response.sendFile(path.join(__dirname + '/html/homepage.html'))
}, db.getUsers)
app.get('/newHuman.html', (request, response) => {
response.sendFile(path.join(__dirname + '/html/newHuman.html'))
})
app.get('/users', db.getUsers)
app.get('/users/:id', db.getUserById)
app.post('/users', db.createUser)
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
////////////////////////////////////////////////////////////////////////
const deleteUser = (request, response) => {
const id = parseInt(request.query.id)
pool.query('DELETE FROM users WHERE id = $1', [id], (error, results) => {
if (error) {
throw error
}
response.status(200).send(`User deleted with ID: ${id}`)
})
}
TL;DR
How can I send to the correct route (even with just POSTing twice) from my HTML when the app.delete and app.put have the exact same route? Tried renaming route, didn't work but I know you shouldn't have to rename for it to work. Here are routes:
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
HTML form method only supports GET and POST method.
Either you have to use GET or POST or you can use ajax or some library like request or axios to make the DELETE request.
For example, if you use axios, try the following code.
Ignore importing jQuery and axios, if you already imported them.
<!-- import jQuery -->
<script
src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<!-- import axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<h1>Let's DELETE ONE Human</h1>
<form id='myFormId' action="/users/:id" method="delete">
ID:<input type="number" name="id" id='deleteId'>
<input type="submit" name="">
</form>
<script>
$( document ).ready(function() {
const myForm = $('#myFormId');
myForm.submit((event) => {
event.preventDefault();
const id = $('#deleteId').val();
const url = `/users/${id}`;
axios.delete(url)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
});
});
</script>
Another easier way of doing this is using a npm module called method-override.
In your main entry point file for your server, add the following lines:
const express = require('express');
const app = express();
const methodOverride = require('method-override');
app.use(methodOverride('_method'));
In your HTML form, you can now use PUT or DELETE requests easily:
For example:
<h1>Let's DELETE ONE Human</h1>
<form id='myFormId' action="/users/:id?_method=DELETE" method="delete">
ID:<input type="number" name="id" id='deleteId'>
<input type="submit" name="">
</form>
Notice the action attribute of the form, all you have to do now is add that simple line and you are done!

Resources