How to get PDF from lambda to React - javascript

I am trying to use pdf-lib to write to a government PDF form. The form is located on a government website so I have to use a server to server architecture due to CORS. I have set up a lambda function to grab the form and send the PDF Bytes as a response. The problem is when I get it on the front end and make an Object URL it does not appear on the front end as anything I am confused what I am doing wrong.
Here is the lambda code:
const { PDFDocument } = require('pdf-lib');
const axios = require('axios');
/**
* #type {import('#types/aws-lambda').APIGatewayProxyHandler}
*/
exports.handler = async (event) => {
console.log(`EVENT: ${JSON.stringify(event)}`);
async function fillForm() {
const formUrl = 'https://pdf-lib.js.org/assets/dod_character.pdf';
const formPdfBytes = await axios.get(formUrl, {
responseType: 'arraybuffer',
});
const formPdfBytesArrayBuffer = formPdfBytes.data;
const marioUrl = 'https://pdf-lib.js.org/assets/small_mario.png';
const marioImageBytes = await axios.get(marioUrl, {
responseType: 'arraybuffer',
});
const marioImageBytesArrayBuffer = marioImageBytes.data;
const emblemUrl = 'https://pdf-lib.js.org/assets/mario_emblem.png';
const emblemImageBytes = await axios.get(emblemUrl, {
responseType: 'arraybuffer',
});
const emblemImageBytesArrayBuffer = emblemImageBytes.data;
const pdfDoc = await PDFDocument.load(formPdfBytesArrayBuffer);
const marioImage = await pdfDoc.embedPng(marioImageBytesArrayBuffer);
const emblemImage = await pdfDoc.embedPng(emblemImageBytesArrayBuffer);
console.log(marioImage, emblemImage);
const form = pdfDoc.getForm();
console.log(form);
const pdfBytes = await pdfDoc.save();
return pdfBytes;
}
const docURL = await fillForm();
return {
statusCode: 200,
// Uncomment below to enable CORS requests
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET',
},
body: JSON.stringify({ data: { docURL } }),
};
};
Here is my front end code that is currently not causing anything to display:
const handleButtonClick = async () => {
try {
const pdfBytes = await fetchPDF();
const bytes = new Uint8Array(pdfBytes.docURL);
const blob = new Blob([bytes], { type: 'application/pdf' });
const docUrl = URL.createObjectURL(blob);
setPdf(docUrl);
} catch (err) {
console.log(err);
}
};

Related

error Failed to set title: TypeError: Cannot read properties of undefined (reading '0')

this code is supposed to fetch a URL and pull the only text there and then make the change to the twitch stream title. what am i doing wrong?
#!/usr/bin/env node
const twitchAPI = 'https://api.twitch.tv/helix';
const clientId = 'censored';
const clientSecret = 'censored';
const refreshToken = 'censored';
const accessToken = 'censored';
const channelName = 'censored';
const url = 'https://api.kyroskoh.xyz/valorant/v1/mmr/eu/CEN%20Astro/EUWE?show=combo&display=0';
// Set initial title
let currentRank = '';
setTitle('OMW to Diamond [Unknown Rank] | !rank !sens');
// Poll for updates every 5 seconds
setInterval(async () => {
try {
const module = await import('node-fetch');
const fetch = module.default;
const response = await fetch(url);
const data = await response.text();
// Extract rank from response data
const rankRegex = /<body>(.*)<\/body>/;
const match = data.match(rankRegex);
if (match) {
const rank = match[1];
if (rank !== currentRank) {
currentRank = rank;
setTitle(`OMW to Diamond [${rank}] | !rank !sens`);
console.log(`Title updated to: OMW to Diamond [${rank}] | !rank !sens`);
}
}
} catch (error) {
console.error(`Failed to fetch rank: ${error}`);
}
}, 5000);
// Helper function to set the stream title using Twitch API
async function setTitle(title) {
try {
const module = await import('node-fetch');
const fetch = module.default;
const response = await fetch(`${twitchAPI}/channels?broadcaster_name=${channelName}`, {
headers: {
'Client-ID': clientId,
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
});
const data = await response.json();
const broadcasterId = data.data[0].broadcaster_id;
const patchResponse = await fetch(`${twitchAPI}/channels?broadcaster_id=${broadcasterId}`, {
method: 'PATCH',
headers: {
'Client-ID': clientId,
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ title }),
});
console.log(`Successfully set title to: ${title}`);
} catch (error) {
console.error(`Failed to set title: ${error}`);
}
}
i tried adding some console.log statements to my code to print out the data returned by the API at various points. came out with
{
error: 'Bad Request',
status: 400,
message: 'Missing required parameter "broadcaster_id"'
}
Failed to set title: TypeError: Cannot read properties of undefined (reading '0')
i expected it to have changed the stream title

Implementing SSE React

Im implementing a notification system for my app and its working well but it is not real time, How can I implement SSE using my setup below
Client Side
const [fetchnotification, setfetchnotification] = useState('');
const getadminnotif = async()=>{
const api = axios.create({
baseURL: http://localhost:3000/books,
withCredentials: true,
headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json' }
});
const query = await api.get(`/adminNotifications?page=${notifpage}`)
setfetchnotification(query.data.findnotifs)
}
useEffect(async()=>{
getadminnotif()
})
return(
// here i mapped the fetchnotification array to get the notifications
)
Server Side
here im using express router to get the api calls from client
const {getadminNotifs} = require('../controllers/bookControllers');
router.get('/adminNotifications', getadminNotifs )
getadminNotifs
const getadminNotifs = asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const pageSize = 4;
const skip = (page - 1) * pageSize;
const findallnotifs = await AdminNotif.find().sort({'dateAdded':-1}).exec()
const findnotifs = await AdminNotif.find().sort({'dateAdded':-1}).skip(skip).limit(pageSize).exec()
const notiflength = findallnotifs.length
var unreadcounts = findallnotifs.filter(({ status }) => status === 'unread').length
const pages = Math.ceil(notiflength / pageSize)
res.status(200).json({ findnotifs, notiflength, pages, unreadcounts })
})
what ive tried so far is this but its not working
I changed the getadminotif function to call an eventsource and then on the client side,instead of using router, i fetched the api calls in app.js
const getadminnotif = async()=>{
const eventSource = new EventSource(`http://localhost:3000/realtime`,{
headers: {
Accept: "text/event-stream",
},
});
eventSource.onmessage = (e) => setfetchnotification(e.data.findnotifs);
eventSource.onerror = err => {
console.log('EventSource error: ', err);
};
return () => {
eventSource.close();
};
}
app.get("/realtime", function (req, res) {
res.writeHead(200, {
Connection: "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
setInterval(() => {
res.write(
`data: ${getadminNotifs}`
);
res.write("\n\n");
}, 5000);
});

I want to upload an image to Imgur using Next.js API route

I am trying to create a process that uploads an image, previews it once, and then uploads it to Imgur if the image is OK.
The code is as follows.
const [img, setImg] = useState([])
const previewImg = ({ target: { files } }) => {
if (img.length > 5) return
const reader = new FileReader()
reader.onload = ({ target: { result } }) => {
setImg((img) => [...img, { id: generateID(), src: result }])
}
reader.readAsDataURL(files[0])
}
const uploadImugr = async (e) => {
e.preventDefault();
const base64 = img[0].src.toString().replace(/data:.*\/.*;base64,/, '');
const res = await fetch('/api/upload/', {
method: 'POST',
body: base64,
});
console.log(await res.json());
}
return (
<>
<input type="file" onChange={previewImg} />
{img.length > 0 && img.map((item) => {
return <img key={item.id} src={item.src} />}
}
<button onClick={uploadImgur}>Upload Imgur</button>
</>
)
The following is the API route for next.js.
Imgur API
const uploadImugrAPI = async (req: NextApiRequest, res: NextApiResponse) => {
const formData = new FormData();
 formData.append('image', req.body);
const resImgur = await fetch("https://api.imgur.com/3/upload", {
method: 'POST',
headers: {
Authorization: 'Client-ID MY-CLIEND-ID',
},
body: formData,
})
res.status(200).json(resImgur.json());
};
export default uploadImugrAPI;
When the above API is executed, the following error message will be displayed.
POST http://localhost:3000/api/upload 500 (Internal Server Error)
Uncaught (in promise) SyntaxError: Unexpected token I in JSON at position 0
I'm new to Next.js and external APIs, so I'm not sure what keywords to search on Google for to solve this problem.
Please help me.
Thank you.
Add
When I tried with Postman, I was able to upload images to Imugr by passing a binary file.
Therefore, I changed the code as follows to pass a binary file instead of base64 and tried it.
const [imgArray, setImgArray] = useState([])
+ const [srcArray, setSrcArray] = useState([])
const uploadImg = ({ target: { files } }) => {
if (imgArray.length > 5) return
+ setImgArray((imgArray) => [...imgArray, files[0]])
const reader = new FileReader()
reader.onload = ({ target: { result } }) => {
const uploadImgSrc = result.toString()
setSrcArray((srcArray) => [
...srcArray,
{ id: generateID(), src: uploadImgSrc.toString() },
])
formRef.current.inputImg.value = ''
}
reader.readAsDataURL(files[0])
}
const uploadImugr = async (e) => {
e.preventDefault();
+ const formData = new FormData();
+ formData.append("image", imgArray[0])
const res = await fetch('/api/upload/', {
method: 'POST',
body: formData,
});
console.log(await res.json());
}
The result was that the following error was displayed in the console.
POST http://localhost:3000/api/upload 500 (Internal Server Error)
Request failed with status code 500
After 2 days of frustration, I've patched together a solution based on several answers I stumbled upon. Convert the file to base64 client side and send that as json to the API.
//client.tsx
async function submit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
if (!file) return;
let base64Img = await getBase64(file);
if (typeof base64Img == 'string') {
base64Img = base64Img.replace(/^data:.+base64,/, '')
}
const result = await fetch('/api/upload', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({image: base64Img}),
})
const response = await result.json() // response.data is an object containing the image URL
}
function getBase64(file: File): Promise<string | ArrayBuffer | null> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = error => reject(error)
})
}
//upload.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const fd = new FormData();
fd.append('image', req.body.image)
fd.append('type', 'base64')
const response = await fetch('https://api.imgur.com/3/image', {
method: "POST",
headers: {
Authorization: "Client-ID process.env.IMGUR_ID",
},
body: fd,
redirect: 'follow',
})
const data = await response.json();
return res.json(data)
}
Also I found using https://api.imgur.com/3/image instead of https://api.imgur.com/3/upload better as the errors were more helpful.

How to pass headers in axios as secret-key

I am using jsonbin.io and hosted a json data as private bin.
I am calling a axios get request to fetch that data. I have to pass my secret-key in headers as
headers{
secret-key: mysecretkey
}
but axios does not allow key in header as secret-key
const secretKey = process.env.REACT_APP_SECRET_KEY;
const localUrl = process.env.REACT_APP_LOCAL_URL;
const fetchPhotosFlocal = async () => {
const response = await axios.get(localUrl, {
headers: {
secret-key: secretKey,
},
});
console.log(response); };
secret-key gives error at compile time and "secret-key" can not fetch data what should i do now ?
secret-key is not a valid identifier in JavaScript, as they cannot contain hyphens (see the specification for more details.
In your case, you can simply put the name of your header in quotes, like this:
const secretKey = process.env.REACT_APP_SECRET_KEY;
const localUrl = process.env.REACT_APP_LOCAL_URL;
const fetchPhotosFlocal = async () => {
const response = await axios.get(localUrl, {
headers: {
"secret-key": secretKey,
},
});
console.log(response);
};
it should be
const fetchPhotosFlocal = async () => axios.get(localUrl, {
headers: {
'secret-key': secretKey,
},
});
const resp = await fetchPhotosFlocal();
console.log(resp);
Problem solved i checked their documentation they are passing secret_key in string code.
req.setRequestHeader("secret-key", "<SECRET_KEY>");
This runs fine
const secretKey = process.env.REACT_APP_SECRET_KEY;
const localUrl = process.env.REACT_APP_LOCAL_URL;
const fetchPhotosFlocal = async () => {
const response = await axios.get(localUrl, {
headers: {
"secret-key": secretKey,
},
});
console.log(response); };

node js fire a new GET request to fetch the data from server

HHi,
I'm working on a project for an online course and I need to make one change to the project. I don't exactly understand what the code reviewer is saying I'm doing wrong. His comment is:
Here you need to fire a new GET request to fetch the data from server.
The requests GET and POST have a specific purpose.
GET request to fetch data from server/db
POST is used to create new data in server/db
These requests must do the task they are designed for, nothing else.
This is the problem area of my code:
let postData = async(url = '', data = {})=>{
console.log(data);
let temp = data.main.temp;
let zip = document.getElementById('zip').value;
let feelings = document.getElementById('feelings').value;
let date = newDate;
let response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( { temp, zip, feelings, date }),
});
try {
const newData = await response.json();
console.log(newData);
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("content").innerHTML = newData.feelings;
return newData
}catch(error) {
console.log("error", error);
}
}
This is my full code:
app.js:
let apiURL = 'http://api.openweathermap.org/data/2.5/weather?id=524901&APPID=' + apiKey + '&zip=';
const endURL = ',us';
// Create a new date instance dynamically with JS
let d = new Date();
let newDate = d.getMonth()+'.'+ d.getDate()+'.'+ d.getFullYear();
document.getElementById('generate').addEventListener('click', performAction);
let content = document.getElementById('feelings').value;
function performAction(e){
let zip = document.getElementById('zip').value;
let url = apiURL + zip + endURL;
apiCall(url)
.then(async function(data){
console.log(data);
let res = await postData('/', data);
console.log(res);
});
};
const apiCall = async (url) =>{
const res = await fetch(url);
try {
const data = await res.json();
console.log(data)
return data;
} catch(error) {
console.log(error)
}
};
let postData = async(url = '', data = {})=>{
console.log(data);
let temp = data.main.temp;
let zip = document.getElementById('zip').value;
let feelings = document.getElementById('feelings').value;
let date = newDate;
let response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( { temp, zip, feelings, date }),
});
try {
const newData = await response.json();
console.log(newData);
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("content").innerHTML = newData.feelings;
return newData
}catch(error) {
console.log("error", error);
}
}
server.js:
let projectData = {};
// Require Express to run server and routes
const express = require('express');
// Start up an instance of app
const app = express();
/* Middleware*/
//Here we are configuring express to use body-parser as middle-ware.
const bodyParser = require('body-parser');
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const cors = require('cors');
app.use(cors());
// Cors for cross origin allowance
// Initialize the main project folder
app.use(express.static('website'));
// Setup Server
const port = 8000;
const server = app.listen(port, listening);
function listening(){
console.log(`running on localhost: ${port}`);
};
app.get('/weather', getData);
function getData(req, res){
res.send(projectData)
console.log(projectData)
};
app.route('/')
.get(function (req, res) {
res.sendFile('index.html', {root: 'website'})
})
.post(getWeather);
function getWeather(req, res){
console.log(req.body);
projectData = req.body;
console.log(projectData);
res.status(200).send(projectData);
};
I have no idea what the changes would look like because I wrote this code following the same structure that they taught in the lessons. Any help would be greatly appreciated.
Thanks alot,
Mike
I think you are trying to fetch data from API so he is trying to say that when you are trying to fetch data from api you need GET request.
I figured it out. I needed to write this function:
const updateUI = async () =>{
const res = await fetch('/weather');
try {
const allData = await res.json();
console.log(allData)
document.getElementById("date").innerHTML = allData.date;
document.getElementById("temp").innerHTML = allData.temp;
document.getElementById("content").innerHTML = allData.feelings;
return allData
} catch(error) {
console.log(error)
}
};
and edit my postData function to this:
let postData = async(url = '', data = {})=>{
console.log(data);
let temp = data.main.temp;
let zip = document.getElementById('zip').value;
let feelings = document.getElementById('feelings').value;
let date = newDate;
let response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( { temp, zip, feelings, date }),
});
try {
updateUI()
/* const newData = await response.json();
console.log(newData);
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("content").innerHTML = newData.feelings;
return newData
*/
}catch(error) {
console.log("error", error);
}
}

Categories