How to pass file(Image) via NextJS API Routes to external API? - javascript

I use NextJS (combined: SSR and SPA for authorized dashboard) with Django Rest FW on the backend. For auth I use JWT token, which is stored in cookies. For that reason, I need a middleware at /pages/api/* for each request to append from cookie access token.
Question:
How to implement a protected request to send file to /pages/api/upload and send it to DRF with an access token?
Sample of small API middleware
export default async (req, res) => {
const { id } = req.query
const cookies = cookie.parse(req.headers.cookie ?? "");
const access = cookies["access"] ?? false;
if (access === false) {
return res.status(401).json({
error: "User unauthorized to make this request"
});
}
if (req.method === "GET") {
try {
const apiRes = await fetch(`${LOCAL_API_URL}/items/${id}`, {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${access}`
}
});
const data = await apiRes.json();
if (apiRes.status === 200) {
return res.status(200).json(data);
} else {
return res.status(apiRes.status).json({
error: data.error
});
}
} catch(err) {
console.log(err);
return res.status(500).json({
error: "Something went wrong"
});
}
} else
res.setHeader("Allow", ["GET"]);
return res.status(405).json({
error: `Method ${res.method} is not allowed`
});
}

For sending image you should use FormData.
Firstly create an instance of FormData.
const formData = new FormData()
Then, you can add image into that.
formData.append('fieldName', someFileInput.current.files[0])
Also, if you want to add some more data with the image, you can append it to FormData too, the similar way.
formData.append('fieldName', someMoreData)
Then, you should set Content-Type to 'multipart/form-data', this is to server understand you pass the FormData.
And, finally, send the form data via Fetch.
I was glad to answer you, I hope it helps you!

the solution was raiser simple. Just passed everything I received and appended token to headers/
export default async (req, res) => {
// all extra validation
const apiRes = await fetch(`${LOCAL_API_URL}/upload/`, {
method: "POST",
headers: { ...req.headers, ...{ "Authorization": `Bearer ${access}` } },
body: req.body
});
// all extra validation
}

Related

Why is my fetch() sending an empty JSON body?

I've been trying to send a JSON data using fetch but the backend receives an empty object.
In my Client JS I have
const user = "company1";
const username = "muneeb";
const data = {user, username};
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data)
}).then((response) => {
console.log(response);
});
The server side has:
router.post('/users/api/login', async (req, res, next) => {
try {
// console.log(request.body);
const request = JSON.stringify(req.body);
let imageTitles = [];
console.log(request);
*its random from here on out but you get the idea*
await components.getImages(imageTitles);
const finalKey = imageTitles.join("");
let images = await components.output(req.body.user ,req.body.username);
res.send(components.jsonConverter(imageTitles, images)); //---Top priority
db.setPassword(req.body.user, req.body.username , finalKey);
} catch (err) {
console.log(err);
res.send(err).sendStatus(500);
};
})
A few things I have already tried :
It works perfectly in Insomnia(postman).
express.json() is present , it helped me go from undefined to blank JSON.
I have enabled cors settings.
That's it for now.
The body parser express.json will only be applied for requests with Content-Type: application/json. You have to add the content type to your fetch call:
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
})

PostMan Pre-request script for fetching JWT using Laravel

I am trying to write a pre-request script for getting JWT for my postman tests using Laravel. I tried the js code that works perfect when I use .NET REST API, but now in Laravel it is not working. If I hit the login endpoint it works I got my JWT, and the response look like this:
{
"status_code": 200,
"access_token": "15|we59pMz1wA6TqwALTJg9IT8pNs3mc4Omwibm7Lkd",
"token_type": "Bearer"
}
Here is my pre-request JS script:
const requestBody =
{
"Email" : "username",
"Password" : "password"
}
pm.sendRequest
({
url: 'http://localhost:8000/api/login',
method: 'POST',
header:
{
'content-type': 'application/json'
},
body:
{
mode: 'raw',
raw: requestBody
}
}, function (err, res)
{
if(err)
{
console.log("Login failed:");
console.log(JSON.stringify(err));
return;
}
else
{
const response = res.json();
const token = 'Bearer ' + response.access_token;
pm.environment.set("TOKEN", token);
console.log("Login succeeded!");
}
});
The response in pre-request is this:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="refresh" content="0;url='http://localhost:8000'" />
<title>Redirecting to http://localhost:8000</title>
</head>
<body>
Redirecting to http://localhost:8000.
</body>
</html>
Your token doesn't seem like a JWT.
This is not base64 and does not contain the 3 parts: header, payload, signature.
Don't hesitate to look at my Postman pre request gist for JWT there are some explanations here too.
It contains one function to check the token validity:
/** Checks if the JWT is present and not expired
The token is expected to be found in `token` environment variable
*/
function isValidToken() {
const token = pm.environment.get("token");
if (!token) {
console.log("Token is missing");
return false;
}
// Payload is retrieved by
// JSON parsing the base64 decoded `atob()` 2nd part of the JWT `[1]`
// (1st is the header, 3rd is the signature)
const payload = JSON.parse(atob(token.split('.')[1]));
// Expiration timestamp (in seconds) is located in the `exp` key
const millisecBeforeExpiration = (payload.exp * 1000) - (new Date()).getTime();
if (millisecBeforeExpiration <= 0) {
console.log("Token is expired");
return false;
}
console.log("Token is valid",
`will expire in ${millisecBeforeExpiration / 1000} seconds`);
return true;
}
and one to get a new token
/** Gets a new JWT
This can be entirely custom authentication.
Here we rely on `user`/`pass` environment variables.
`host` also needs to be set, feel free to use another route instead of /give-me-a-jwt :)
*/
function login() {
const body = JSON.stringify({
"user": pm.collectionVariables.get("user"),
"pass": pm.collectionVariables.get("pass")
});
const request = {
url: pm.collectionVariables.get("host") + "/give-me-a-jwt",
method: "POST",
header: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body,
};
pm.sendRequest(request, (err, res) => {
if (res.code !== 200) throw new Error(res.status);
console.log("Token refreshed");
pm.environment.set("token", res.json().token);
});
}
Now you can just get a new token if not valid:
if (!isValidToken()) login();
Of course don't forget to use your brand new token in your Postman's Bearer Token authentication type

Axios POST request returns 404 error when sending to Node.js API

I am developing a web application using a React frontend and a Node.js backend. The frontend sends a POST request to the backend using Axios like this:
Register.js
...
handleSubmit = (e) => {
e.preventDefault();
const { email, password, name, dateofbirth } = this.state;
const user = { email, password, name, dateofbirth };
const url = "http://localhost:9000/register";
axios
.post(url, user, {
headers: {
"Content-Type": "application/json",
},
})
.then((response) => console.log(response))
.catch((error) => {
console.error("You have made a big error. " + error);
console.log(user);
});
};
...
While the backend receives the request like this:
./routes/register.js
...
router.post("/register", async (req, res) => {
console.log("Inside Home Login");
res.writeHead(200, {
"Content-Type": "application/json",
});
console.log("Users : ", JSON.stringify(users));
res.end(JSON.stringify(users));
})
...
However I get the error "POST http://localhost:9000/register 404 (Not Found)" upon trying to send anything.
My guess would be that you are routing in your index.js. If you can provide a code sample to figure it out.
If so, the thing is defining a routing like,
app.use('/register', yourImportedVariable);
does define a route at http://localhost:9000/register.
So, if in your routes/register.js file you define a GET endpoint with '/register' your front-end call must be http://localhost:9000/register/register
To fix it, either rename your route as '/', or fix your front-end call with the above url.

Need help understanding logic

Could someone please explain to me how this line works:
https://github.com/sveltejs/realworld/blob/master/src/routes/login/index.svelte#L13
const response = await post(auth/login, { email, password });
post is being called from utils.js, which is this:
utils.js
export function post(endpoint, data) {
return fetch(endpoint, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(r => r.json());
}
So the function enters here, and then fetches the endpoint provided, which was auth/login.
This confuses me becauseauth/login is not an endpoint, it's a file that exports a function, under auth/login.js. Does this second post function in auth/login.js get called automatically? I am unsure where this (req, res) gets passed in as well, since we are just fetching this file from above and not passing any arguments.
auth/login.js
import * as api from 'api.js';
export function post(req, res) {
const user = req.body;
api.post('users/login', { user }).then(response => {
if (response.user) req.session.user = response.user;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(response));
});
}
This is where the user is being set in a cookie, which my code isn't currently doing, and the session is lost upon refresh. I am trying to understand how to persist sessions in Sapper.
This line is making a call to a relative path:
const response = await post(auth/login, { email, password });
So the url that fetch is calling is something like : http://yourdomain.com/auth/login
According to the docs, what happens when a route ending in .js is called is that Sapper looks for a function with the name of the HTTP request method on that file.
More info here : sapper.svelte.dev/docs#Server_routes

How to set headers while requesting a page in nodejs?

I saw many tutorials on jwt authentication but every video maker uses Postman to show what's happening and they pass on the header in the headers section while requesting a URL in Postman. I tried to do it with JavaScript but I was not able to do it.
I want to do jwt authentication but after token generation, I send it to client side to use it for further requests but I failed to do so after trying it a few times. I also tried to set req.headers in server side but it didn't do what I wanted to..
I want to set request headers for authentication of the form "Bearer {token}" for every request after token generation. How to do it with JS??
What I am most concerned about is that every tutorial does it with postman but they didn't show how they implemented it in their own app. I hope my question is clear.
You can easily add header on your http request like that
it has been solved here Node.JS: How to send headers with form data using request module
In vanilla nodejs:
const uri = "http://example.com";
const options = {
headers: {
"Authorization": "Bearer ...."
}
}
// require http/https lib
let req = require("http").request(uri, options, (res) => {
const chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.once("end", () => {
// concat body chunks
let body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.on("error", (err) => {
console.log(err);
});
req.end();
https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_options_callback
Something like that:
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("custom_header", "value");
},
success: function(data) {
}
});
First install jwt and express framework using npm then make a middleware file which will check if the tokek is set or not.
Middleware.js :
let jwt = require('jsonwebtoken');
const config = require('./config.js');
let checkToken = (req, res, next) => {
let token = req.headers['authorization']; // Express headers are auto converted to lowercase
if (token) {
if (token.startsWith('Bearer ')) { // Checks if it contains Bearer
// Remove Bearer from string
token = token.slice(7, token.length); //Separate Bearer and get token
}
jwt.verify(token, config.secret, (err, decoded) => { //Inser the token and verify it.
if (err) {
return res.json({
status: false,
message: 'Token is not valid'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.json({
status: false,
message: 'Access denied! No token provided.'
});
}
};
Next, create a config file which will contain the secrets.
Config js:
module.exports = {
secret: 'worldisfullofdevelopers'
};
Finally, create a token route which will create your token and after that the rest of the calls will be authenticated for that token.
Index.js :
const middleware = require('./middleware');
const jwt = require("jsonwebtoken");
const config = require('./config.js');
//Call token Route
app.use('/token', (req, res, next) => {
//Generate Token
let token = jwt.sign({ username: "test" },
config.secret,
{
expiresIn: '1h' // expires in 1 hours
}
);
//Send Token
res.json({
success: true,
message: 'Authentication successful!',
token: token
});
});
//Add Authentication to all routes
app.use(middleware.checkToken);
//===> All the routes after middleware will be checked for token
app.use('/getUser', (req, res, next) => {;
console.log('do something')
});
If I understand correctly, you want to set the HTTP header on the client, in order to pass an authentication token to the server. I would recommend that you use a library like **axios*.
Using axios, once you receive the toke, set the header for every outgoing communication with the following line of code:
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
This will set the authentication http header to the form you need.

Categories

Resources