Dynamic Routes with AWS Object Keys

Hi, I'm currently building a website where I need to fetch PDF's from S3. I've managed to get that working however the object key is currently hardcoded. I want to change this so that my object key gets taken from the URL. For example, if the user visits /exams/biology/photosynthesis1 it sets the object key to biology/photosynthesis1. If they visit /exams/physics/forces, it sets the object key to physics/forces. How would I do that? I've attached my code below:
6 Replies
wlvz
wlvz16mo ago
"use client";
import React, { useState, useEffect } from "react";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { Credentials } from "aws-sdk";
import { GetStaticProps } from "next/types";

const PDFViewer = (presignedUrl: RequestInfo | URL) => {
const [pdfUrl, setPdfUrl] = useState("");

useEffect(() => {
const fetchPdf = async () => {
try {
const objectKey = "biology/Photosynthesis 1.pdf";
const s3 = new S3Client({
region: "region-1",
credentials: new Credentials({
accessKeyId: "credential",
secretAccessKey: "credential",
}),
});
const getObjectParams = {
Bucket: "bucketname",
Key: objectKey,
};
const command = new GetObjectCommand(getObjectParams);
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
const response = await fetch(signedUrl);
const pdfObjectUrl = URL.createObjectURL(await response.blob());
setPdfUrl(pdfObjectUrl);
} catch (error) {
console.error("Error fetching PDF:", error);
}
};

fetchPdf();

return () => {
if (pdfUrl) {
URL.revokeObjectURL(pdfUrl);
}
};
}, []);
"use client";
import React, { useState, useEffect } from "react";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { Credentials } from "aws-sdk";
import { GetStaticProps } from "next/types";

const PDFViewer = (presignedUrl: RequestInfo | URL) => {
const [pdfUrl, setPdfUrl] = useState("");

useEffect(() => {
const fetchPdf = async () => {
try {
const objectKey = "biology/Photosynthesis 1.pdf";
const s3 = new S3Client({
region: "region-1",
credentials: new Credentials({
accessKeyId: "credential",
secretAccessKey: "credential",
}),
});
const getObjectParams = {
Bucket: "bucketname",
Key: objectKey,
};
const command = new GetObjectCommand(getObjectParams);
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
const response = await fetch(signedUrl);
const pdfObjectUrl = URL.createObjectURL(await response.blob());
setPdfUrl(pdfObjectUrl);
} catch (error) {
console.error("Error fetching PDF:", error);
}
};

fetchPdf();

return () => {
if (pdfUrl) {
URL.revokeObjectURL(pdfUrl);
}
};
}, []);
return (
<div
className="fixed inset-0 flex items-center justify-center"
>
<div className="absolute inset-0 bg-gray-900 opacity-75 pointer-events-none" />
<div className="relative w-full h-full flex items-center justify-center">
{pdfUrl ? (
<embed
src={`${pdfUrl}#toolbar=0`}
type="application/pdf"
className="w-full h-full"
/>
) : (
<p>Loading PDF...</p>
)}
</div>
</div>
);
};
return (
<div
className="fixed inset-0 flex items-center justify-center"
>
<div className="absolute inset-0 bg-gray-900 opacity-75 pointer-events-none" />
<div className="relative w-full h-full flex items-center justify-center">
{pdfUrl ? (
<embed
src={`${pdfUrl}#toolbar=0`}
type="application/pdf"
className="w-full h-full"
/>
) : (
<p>Loading PDF...</p>
)}
</div>
</div>
);
};
An important note is that although the user is going to visit /exams/biology/photosynthesis1 it needs to capitalise the P and then add a space before 1 as well as end it with .pdf so that the object key would be /biology/Photosynthesis 1.pdf
Maj
Maj16mo ago
u can get the params with router.query and dynamic routes. capitalizing and adding space wouldnt be that hard i guess
biology(folder) -> [..biologyId].tsx
biology(folder) -> [..biologyId].tsx
const router = useRouter() <------ This is from Next Router
const { biologyId } = router.query <------ U pull query params from it. if there are multiple params like /biology/aosdsa/asdsda then it will extract the params in a string array like ["aosdsa", "asdsda"]
const router = useRouter() <------ This is from Next Router
const { biologyId } = router.query <------ U pull query params from it. if there are multiple params like /biology/aosdsa/asdsda then it will extract the params in a string array like ["aosdsa", "asdsda"]
in ur case
/exams/biology/[biologyId].tsx <-- spread operator is optional in ur case probably
/exams/biology/[biologyId].tsx <-- spread operator is optional in ur case probably
wlvz
wlvz16mo ago
ahh that makes sense, but then if i were to visit /physics for example would it still work if the folder is named biology? or would i have to name it something else as well as the slug
Maj
Maj16mo ago
It wouldnt work are you using experimental nextjs? i think its possible with app directory tbh i would fetch the links on the biology page then when the user clicks on the link just open a blob or something like that i would make the "biology" <- subject name dynamic if you go to /physics fetch whatever you want and serve to client also you can add extra queries into the url of the client like /biology?pdfName=Photosynthesis1 and get them with the next router
Maj
Maj16mo ago
Josh tried coding
YouTube
NextJS App Router: Learn Modern Web Development in 1 Hour
The new NextJS app router is finally stable and has become the recommended way to write React. Let's learn the 4 core concepts that have changed or are completely new, and you how can build amazing web apps with them. My GitHub: https://github.com/joschan21 Discord: https://discord.gg/4vCRMyzgA5 -- Video links Server vs Client components: htt...
wlvz
wlvz16mo ago
i'm using the app directly but the stable version, next 13.4, yeah that makes a lot more sense now that i think about it. tysm
Want results from more Discord servers?
Add your server