Podcasts have become an increasingly popular form of media in recent years, with a wide range of topics and genres available for listeners to choose from. Suppose you are passionate about podcasting and want to create a platform to host and distribute your own shows. In that case, Nuxt.js and Appwrite can be powerful for building a feature-rich and scalable podcasting platform.
This article explores how to build a podcast platform using Nuxt.js and Appwrite, including how to set up the project, upload a podcast, and create a podcast player.
Repository
https://github.com/folucode/appwrite-podcast-app
Prerequisites
- Basic understanding of CSS, JavaScript, and Nuxt.js.
- Docker Desktop installed on your computer (run the
docker -v
command to verify the installation); if you don’t yet have it, install it from here. - An Appwrite instance running on your computer. Check out this post to create a local Appwrite instance. It can also be installed using digital ocean or gitpod.
Setting up the project
Create a new Nuxt.js project using the following command:
npx create-nuxt-app my-podcast-platform
This will guide you through several prompts to set up the project, including selecting a package manager, a UI framework, and additional features. Make sure to choose Axios as the HTTP client, as you'll use it to make API calls to Appwrite later.
Once you have set up the project, you can navigate to the project directory and start the development server using the following commands:
cd my-podcast-platform
npm run dev
Setting up the Appwrite database
You'll be using Appwrite's storage and database service in this app. The Appwrite storage bucket is where the Appwrite stores uploaded files.
To set up an Appwrite storage bucket, log in to the Appwrite console and create a new project. Next, click on Storage and then Create bucket. You can name the bucket Podcasts.
Next, click on the newly created bucket, go to the settings tab, and then scroll down to Update permissions
. Add a new role for Users
and give it role all permissions.
After adding the roles, the next thing to do is to create a database to store all the podcast data.
Click on the Databases
tab, and then Create database. You can give it the name of podcasts as well.
Click on the podcasts database and create a new collection in the database. Collections
serve as database tables in Appwrite.
You can call it podcast_details
, as it stores all the details of your podcast. Next, click on the collection and add new attributes. Attributes
serve as database columns in Appwrite.
The attributes you’ll add are:
-
fileID
of type string -
title
of type string -
name
of type string -
date
of type integer
Also, as you did for the bucket in storage, add a Users role with all permissions for the podcast_details
collection.
Setting up the UI
In the components folder, create a file called Podcast.vue
and paste the code below into it:
<template>
<div class="pod">
<h3>{{ title }}</h3>
<div class="pod-details">
<span>By {{ name }}</span> /
<span>{{ date }}</span>
<audio controls>
<source :src="source" type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</div>
</div>
</template>
<script>
export default {
name: "Podcast",
props: {
title: String,
name: String,
date: String,
source: URL,
},
};
</script>
<style>
h3 {
font-weight: bolder;
}
.pod {
background-color: #45a049;
border-radius: 5px;
padding: 10px;
margin: 5px;
}
.pod-details {
background-color: rgb(255, 255, 255);
display: block;
padding: 5px;
margin-bottom: 15px;
border-radius: 10px;
}
span {
margin-left: 5px;
margin-right: 5px;
color: rgb(0, 0, 0);
}
audio {
margin-top: 15px;
display: block;
}
</style>
This component is a simple card that contains all the details of a podcast. The audio file, name, title of the podcast, and date. There's also a props
object for each of these attributes.
Next, in the pages/index.vue
file, paste the code below:
<template>
<div class="container">
<div class="child">
<Podcast />
</div>
<div class="child">
<form>
<label for="title">Title</label>
<input
type="text"
name="title"
placeholder="Podcast title..."
v-model="title"
/>
<label for="country">File</label>
<input id="file" type="file" />
<button type="submit" value="Submit">
Upload
</button>
</form>
</div>
</div>
</template>
<script>
export default {
name: "IndexPage",
};
</script>
<style>
.container {
border: 3px solid #fff;
padding: 20px;
}
input[type="text"] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
input[type="file"] {
width: 93%;
background-color: #ffffff;
color: rgb(0, 0, 0);
padding: 14px 20px;
margin: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
}
button {
background-color: #45a049;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
.child {
width: 45%;
float: left;
padding: 20px;
border: 2px solid red;
margin: 5px;
}
</style>
In this file, you have the Podcast
component and a form to upload a podcast.
Building the app
Before you continue, install these two packages: the Appwrite official package and faker.js, a library to generate mock data.
npm i appwrite @faker-js/faker
Connecting to Appwrite
In the index.vue
file, import the Appwrite package and the faker.js package, and then create an Appwrite connection like so:
...
<script>
import { Client, Account, Databases, Storage, Query } from "appwrite";
import { faker } from "@faker-js/faker";
const client = new Client();
client
.setEndpoint("http://localhost/v1") // The Appwrite Endpoint
.setProject("[YOUR-PROJECT-ID]");
const account = new Account(client);
const database = new Databases(client);
const storage = new Storage(client);
account.createAnonymousSession().then(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
);
export default {
name: "IndexPage",
data() {
return {
podcasts: [],
title: "",
};
},
mounted() {},
};
</script>
...
In this file, you create an Appwrite instance with an account, database, and storage, which you'll use later in the app. Also, you set up an anonymous session - this saves us the time of making a whole authentication system.
There are two data variables, podcasts
, and title
.
In the mounted method, check whether the anonymous account is active and subscribe to the documents channel. Subscribing to a channel updates your app in real time once a change is made in the database.
...
mounted() {
if (account.get !== null) {
try {
client.subscribe("documents", (response) => {
});
} catch (error) {
console.log(error, "error");
}
}
}
...
Uploading a podcast
To upload a podcast, you'll follow the steps below:
- First, upload the audio file to the storage bucket
- Retrieve the ID and then store it in the
podcast_details
collection, along with the name, title, and date
...
methods: {
async uploadPodcast() {
const file = await storage.createFile(
"[YOUR-BUCKET-ID]",
"unique()",
document.getElementById("file").files[0]
);
await database.createDocument(
"[YOUR-DATABASE-ID]",
"[YOUR-COLLECTION-ID]",
"unique()",
{
fileID: file.$id,
name: faker.name.fullName(),
title: this.title,
date: Date.now(),
}
);
},
},
...
Make sure to add a click event to the Upload button and also prevent the page from reloading, like so:
<button type="submit" value="Submit" @click.prevent="uploadPodcast">
Upload
</button>
Fetching the podcasts
To create a function to fetch the podcasts, follow the steps below:
- Fetch all the files in the storage bucket
- For each audio file, get its details from the
podcast_details
collection using thefileID
- Put all the details in an object and push it to the podcasts array
...
async getPodcasts() {
const result = await storage.listFiles("63a4866686e5064b953d");
let podcasts = [];
result.files.map(async (file) => {
let link = await storage.getFileView("63a4866686e5064b953d", file.$id);
let podcastData = await database.listDocuments(
"63a486f99305afe71e48",
"63a487ea6bb5ba002a8c",
[Query.equal("fileID", file.$id)]
);
podcasts.push({
id: file.$id,
link,
title: podcastData.documents[0].title,
date: new Date(podcastData.documents[0].date).toDateString(),
name: podcastData.documents[0].name,
});
});
this.podcasts = podcasts;
},
...
Every time the page reloads or changes occur in the database, you want to fetch all the podcasts in real time. To do that, call the getPodcasts
function on the mounted method like so:
...
mounted() {
this.getPodcasts();
if (account.get !== null) {
try {
client.subscribe("documents", (response) => {
this.getPodcasts();
});
} catch (error) {
console.log(error, "error");
}
}
},
...
Displaying the podcasts
To display the podcasts, all you have to do is loop over the podcasts array and pass in all the data as props like so:
...
<Podcast
v-for="podcast in podcasts"
:key="podcast.id"
:title="podcast.title"
:date="podcast.date"
:source="podcast.link"
:name="podcast.name"
/>
...
You should see the podcasts show up like this:
You can get free audio files from this website: https://mixkit.co/free-stock-music/
Conclusion
In this article, you learned how to build a podcast platform using Nuxt.js and Appwrite. Combining these two technologies helped you to quickly and easily build a feature-rich podcast platform that meets the needs of both content creators and listeners.