const express = require('express');
const router = express.Router();
const fetch = import('node-fetch');
const btoa = require('btoa');
const CLIENT_ID = 'this is an id';
const CLIENT_SECRET = 'this is a secret';
const redirect = encodeURIComponent('http://localhost:8080/discord/callback');
router.get('/login', (_req, res) => {
res.redirect(`https://discordapp.com/api/oauth2/authorize?client_id=${CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirect}`);
});
router.get('/callback', (req, res) => {
if (!req.query.code) throw new Error('NoCodeProvided');
const code = req.query.code;
const creds = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const response = fetch(`https://discordapp.com/api/oauth2/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirect}`,
{
method: 'POST',
headers: {
Authorization: `Basic ${creds}`,
},
});
const json = response.json();
console.log(json);
res.redirect(`/?token=${json.access_token}`);
});
module.exports = router;
I know js very poorly and don’t understand why node-fetch is no longer a function.
>Solution :
The node-fetch documentation shows you how to handle this:
// mod.cjs const fetch = (...args) => import('node-fetch').then(({default: fetch}) => > fetch(...args));
(I had just come up with that myself, when to check whether it dida default export or a named one, and ran across that when searching for import.)
For instance, you might put that in my-fetch.js and do:
const fetch = require("./my-fetch.js");
What that does:
- Gets the promise for the module from
import(). - Exports a stand-in
fetchfunction. - When the stand-in function is called, it waits for the promise from
import()to be fulfilled with the module namespace object of thenode-fetchmodule, gets thefetchfunction from thedefaultproperty on it, and passes on the call to that.
That’s fairly seamless (one very quick extra async tick), since fetch returns a promise.
Separately, it looks like you’re using fetch incorrectly. fetch returns a promise, so you have to consume the promise. You can do that with an async function or explicitly with .then and .catch:
With an async function and await:
router.get('/callback', async (req, res) => {
// When passing an `async` function to Express, it's important to ensure that
// you handle **all** errors locally, because Express won't do anything with
// the promise the function returns, so rejections will not be handled.
try {
if (!req.query.code) throw new Error('NoCodeProvided');
const code = req.query.code;
const creds = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const response = await fetch(`https://discordapp.com/api/oauth2/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirect}`, {
method: 'POST',
headers: {
Authorization: `Basic ${creds}`,
},
})
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const data = await response.json();
res.redirect(`/?token=${json.access_token}`);
} catch (error) {
// ...send an appropriate error response...
}
});
Or with .then and .catch:
router.get('/callback', (req, res) => {
if (!req.query.code) throw new Error('NoCodeProvided'); // Don't do this, send an error response instead
const code = req.query.code;
const creds = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
fetch(`https://discordapp.com/api/oauth2/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirect}`, {
method: 'POST',
headers: {
Authorization: `Basic ${creds}`,
},
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
})
.then(data => {
res.redirect(`/?token=${json.access_token}`);
})
.catch(error => {
// ...send an appropriate error response...
});
});