Hey!Sound: Upload and Vote for the Best Audios on the Internet
Fazza Razaq Amiarso
Posted on May 13, 2024
This is a submission for the Netlify Dynamic Site Challenge: Build with Blobs.
What I Built
An audio sharing app where users can upload their audio with maximum time limit of 30 seconds. Users can tag their audios into 3 categories: Podcast, Music, and Bites. Users can also vote or downvote an uploaded audio (without authentication).
Huge Credits to 50hacks.co for the app's inspiration.
Initially, I got delayed by one whole day because of production build that keep failing when using blobs. However, I'm really grateful that I didn't give up since I ended up learning a lot of interesting things while searching and hacking for the solutions 😉.
Useful Links
Demo: https://heysound.netlify.app/
Source code:https://github.com/fazzaamiarso/heysound
Platform Primitives
Filtering Blobs Metadata
The main feature of this app is sharing audio, so I utilize Netlify blobs to store audio with it's metadata to display in UI.
Since netlify blobs have filter by prefix to get blobs list, I used it to categorize the audios by prefixing it with <category>:<key>
.
Here's the upload implementation
const store = () => getStore("sounds");
export async function uploadAudio(formData) {
const audio = formData.get("sound");
const category = formData.get("category");
const description = formData.get("description");
const key = `${category}:${nanoid()}`;
if (!audio) return { error: "Audio not found" };
await store().set(key, audio, {
metadata: {
category,
description,
type: audio.type,
name: audio.name,
createdAt: new Date().toISOString(),
},
});
}
Since I display the audios based on categories, I can conveniently filter them when calling the API with prefix
const store = () => getStore("sounds");
export async function getSoundsMetadata(prefixFilter) {
const blobList = await store().list({ prefix: prefixFilter });
const soundsData = await Promise.all(
blobList.blobs.map(async (blob) => {
const metadata = await store().getMetadata(blob.key);
return { key: blob.key, metadata: metadata?.metadata };
}),
);
return soundsData;
}
Another convenient thing is that, I have the options to only retrieve the metadata so I don't need to pass around blobs in the components (which isn't possible, right?).
Retrieving Audios Blob
To retrieve a blob is pretty simple and straight-forward.Although, I know there are better ways to send the blob back to client. Due to time constraints, I just use the simplest method
const store = () => getStore("sounds");
export async function GET(request) {
const { searchParams } = new URL(request.url);
const key = searchParams.get("key");
if (!key) throw new Error("Key not found!");
const audioBlob = await store().get(key, {
type: "stream",
consistency: "strong",
});
return new NextResponse(audioBlob, {
headers: {
"Netlify-CDN-Cache-Control": "public, max-age=604800, immutable",
"Netlify-Vary": "query",
},
});
}
Store Voting Data
The final way I use the blob storage is to store voting data. What I like the most is that the data is unstructured, that means I have a lot flexibility on how I want to store my data without strict constraints.
Here's the implementation
const store = () => getStore("votes");
const createVoteKeys = (key) => `${key}:votes`;
export async function GET(request) {
const { searchParams } = new URL(request.url);
const key = searchParams.get("key");
if (!key) return NextResponse.json({ count: 0 });
const count = await store().get(createVoteKeys(key), { type: "json" });
return NextResponse.json({ count: count ?? 0 });
}
export async function POST(request) {
const data = await request.json();
const { key, action } = data;
const count = await store().get(createVoteKeys(key), { type: "json" });
const newCount = count + votesAction
await store().setJSON(createVoteKeys(key), newCount);
return NextResponse.json({ message: `Successfully updated: ${key} count!` });
}
If you come until this far, I thank you for your time! I hope by reading this article, someone can find it useful.
Let's chat and connect on Linkedin 😀. I love talking and learning from new people.
Posted on May 13, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
May 8, 2024
May 12, 2024
May 8, 2024