Creating stunning blogs with Appwrite’s database relationships and Nuxt
Tags: appwrite, javascript, nuxt, tutorial
Published: 2023-05-19 09:26:34

Databases are essential to modern web app development, providing a centralized location for data storage, management, and retrieval. They are crucial, as without databases, web apps would have to store data in files or memory, which may become unmanageable as data increases.

In this article, we’ll build a blog using [Appwrite’s database relationship](https://appwrite.io/docs/databases-relationships?utm_source=hackmamba&utm_medium=hackmamba-blog) to store and render data, as well as Pink Design, an open-source design system to style a Nuxt project.

The complete source code is located on [GitHub](https://github.com/ugwutotheeshoes/appwrite-blogs). Clone and fork it to get started.

##Prerequisites

To follow along, the following are required:

- Docker Desktop installed on the computer; run the `docker -v` command to verify that we have Docker installed. If not, install it from the [Get Docker documentation](https://docs.docker.com/get-docker/).
- A basic understanding of JavaScript, Vue.js, and [Nuxt](https://nuxtjs.org/).
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance. Appwrite also supports one-click installation on [DigitalOcean](https://marketplace.digitalocean.com/apps/appwrite) or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod).
- Node and its package manager, `npm`. Run the command `node -v && npm -v` to verify that we have them installed or install them from [here](https://nodejs.org/en/).

#Getting started
##Project setup and installation

Run the following command in the terminal to create a new Nuxt application:

```js
npx nuxi init <project-name>
```

Navigate to the project directory and install the required dependencies.

```js
cd <project-name> 
npm install
```

Run `npm run dev` to start a development server at https://localhost:3000/ in our browser.


> _**NOTE**: `<project-name>` above stands for the name of our app; we can call it any name we deem fit._

##What is Pink Design?

[Pink Design](https://pink.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is Appwrite's open-source design system for creating consistent and reusable user interfaces. Pink Design aims to emphasize collaboration, developer experience, and accessibility.

##Scaffolding an Appwrite Project

###Installing Appwrite
[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is a development platform that provides a powerful API and management console for building backend servers for web and mobile applications.

To use Appwrite in our Nuxt application, install Appwrite’s Pink Design and client-side SDK (Software Development Kit) for web applications.

```js
npm install appwrite
npm install @appwrite.io/pink
```

Then, we’ll set up a new Appwrite project by starting an Appwrite instance and navigating to the specified hostname and port:

```js
    localhost:80
```

> _**NOTE**: To set up an Appwrite instance, follow the steps in the article located in **Prerequisites**_.

###Creating a new project
Next, we must log into our account or create an account by initializing an Appwrite instance if we don’t have one.

![Log in or sign up](https://paper-attachments.dropboxusercontent.com/s_52ABD050904341011DEBD07E3B7A89DB41271D9A224427484F2A5E59DF19874A_1671311391345_Sign-up-Appwrite.png)


On Appwrite’s console, click the **Create Project** button, input **blog** as the project name, and click **Create**.

![Creating a new project](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1681616776582_Organizations-Appwrite+2.png)


The project dashboard will appear on the console. Next, click the **Settings** tab at the bottom of the page and copy the **Project ID** and **API Endpoint**; we’ll use this to set up our Nuxt application.

![Settings tab](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1681638321757_Console-Appwrite+5.png)
![Project ID and API Endpoint](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1681638325177_Settings-Appwrite.png)


###Creating a Collection and Attribute
On the left side of the Appwrite Console dashboard, click on the **Database tab**. Click on the **Create Database** button to create a new database. Creating a new database will lead us to the **Collection** page.


![Create a database](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1681638547780_Settings-Appwrite+1.png)
![Database name](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1682944641112_Databases-Appwrite+2.png)


Next, we’ll create two Collections — **Blog** and **Posts**. 

![Blog Collection](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683181959767_comments-Appwrite+5.png)
![Post Collection](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683181948950_Blog-Appwrite+1.png)


After creating the Collections, go to the **Update Permissions** section on the **Settings** page. We want to assign a CRUD access (create, read, update, delete) with a **role: any** value for the Collections we created earlier. We can customize these roles later to specify who has access to read or write to our database.

![Update Permissions](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1682988922275_comments-Appwrite.png)


Next, go to the **Attributes** tab of the **Post** collection to create the properties we want the document to have. Let’s generate string attributes — **title** and **content** with a size of 1000 and 20000 bits.

![Title attribute](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683181700760_Post-Appwrite+4.png)
![Content attribute](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683181704477_Post-Appwrite+5.png)


For the **Blog** collection, generate a string attribute — **blog-name** with a size of 256 bits.

![Blog attribute](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683181372982_Blog-Appwrite.png)


Next, create a **Relationship** attribute for the **Blog** collection. Select **Relationship** as the attribute type. Select the **Two-way relationship type** in the Relationship modal and pick **post** as the **related collection** and **post** as an **attribute key**. Select an **attribute key** called **blog** to represent the related collection and **one-to-many** as the relation. Select **Cascade** as the desired on-delete behavior. Click the **Create** button to create the relationship.

![Create a relationship](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683182390722_Blog-Appwrite+2.png)


Let’s head to the **Documents** section and click on **Create Document**. We want to create a post title for the blog. Fill in the attributes with the dummy title we made for the blog.

![Creating a title document](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683182450810_Blog-Appwrite+3.png)


Head to the **Post** collection and create a **Document** with a title and content value. Select the **Blog** document ID we created earlier as the blog relationship value.

![Creating a comment document](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683182861986_Post-Appwrite+6.png)
![Created Document](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683183714078_Post-Appwrite+8.png)


The documents created above are for posts for the blog.

##Building the UI

Create a `pages/index.vue` file at the root folder of the project with the following syntax:

```js
<template>  
  <main>
    <nav class="u-flex u-cross-center u-main-space-between u-padding-32">
     <h2 class="logo u-padding-32 eyebrow-heading-1 u-color-text-pink">
       <NuxtLink to="/">Pink Blog</NuxtLink>
      </h2>
      <NuxtLink to="/">
        <span class="button">  WRITE AN ARTICLE </span>
      </NuxtLink>
    </nav>
  <div class="container">
 </div>
 </main>
</template>
```

The Pink Design CSS styles above do the following:

- **container**: It holds any content and is used to group content and create scannable interfaces
- **eyebrow-heading-1**: Used to support the main heading of a page or to provide additional context or orientation
- **u-flex**, **u-grid**: Controls how an element is displayed
- **u-cross-center u-main-space-between**: Aligns and justifies an element’s content
- **u-text-center**: Centers a text
- **u-padding-32**: Applies a padding of 2rem to the element
- **u-color-text-pink**: Changes a text color to pink

##Fetch and render data from Appwrite

Here, we’ll fetch the posts created earlier on from Appwrite. To do this, create a new file, `utils.js`, in the root directory of the project, then copy and paste the following code:

```js
<script setup>
import { Client, Databases } from "appwrite";
const client = new Client();
const databases = new Databases(client);
  client
   .setEndpoint("OUR_API_ENDPOINT") // Your API Endpoint
   .setProject("OUR_PROJECT_ID"); // Your project ID
export const getPost = databases.listDocuments( "OUR_DATABASE_ID", "OUR_COLLECTION_ID", "OUR_DOCUMENT_ID");
```
Let’s break down the code snippet above:

- Imports the required module, **Client** and **Databases**
- Creates a new instance for the modules


> _**NOTE**: Get your `API Endpoint`, `Project ID`,  `[DATABASE_ID]`, and `[COLLECTION_ID]` from Appwrite’s console._

Import the instance in the `index.vue` file, which interacts with Appwrite services whenever the component is mounted.

```js
import { getPost } from "../utils";
const docs = ref(null);
    
onMounted(() => {
  getPost.then(
    function (response) {
      docs.value = response;
    },
    function (error) {
      console.log(error);
    }
   );
});
</script>
```
Next, we’ll display the posts from the Appwrite console using the **v-for** directive. We’ll also render the latest post at the top of the list.

```js
<template>
  <main>
    <nav>
      ...   // nav bar
    </nav>
<div class="container">
   <h2 class="eyebrow-heading-1 u-text-center u-padding-64">posts</h2>
     <ul class="list">
       <li class="box" v-for="doc in docs.slice().reverse()" :key="doc.$id">
         <NuxtLink :to="`/post/${doc.$id}`">
           <div class="u-flex u-cross-center u-main-space-between">
             <div class="u-grid">
               <span class="text eyebrow-heading-1"> {{ doc.title }} </span>
                 <span class="text">{{ doc.content.slice(1, 200) }}</span>
              </div>
              <span class="button icon-cheveron-right" aria-hidden="true"></span>
              </div>
           </NuxtLink>
         </li>
       </ul>
   </div>
  </main>
</template>
```

The Pink Design CSS styles within the `<template>` do the following:

- **heading-level-3**: Determines the size of the text
- **u-line-height-2**: Adjusts the line height of a text
- **button**: A class representing a button
- **icon-cheveron-right**: Creates a chevron right button

The application will look like this after applying the configurations above:


![Posts section](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683187728273_localhost-3000+11.png)

##Posting new content to Appwrite

Here, we'll build a form to post content to Appwrite. To do this, create a `pages/article.vue` file and paste the following code:

```js
<template>
 <div>
  <nav> ... // nav </nav>
  <form @submit.prevent="submitForm">
     <h2 class="eyebrow-heading-1 u-text-center u-padding-64">
       write an article
      </h2>
   <div class="u-grid u-cross-center u-main-center u-padding-16">
 <label for="title">Article title:</label>
 <input placeholder="Title" type="text" id="title" v-model="title" required />
 <label for="content">Content:</label>
 <textarea class="input-text" placeholder="Content" type="content" id="content"
    v-model="content" required />
 <button class="button" type="submit">
  <span class="text">Submit</span>
 </button>
 </div>
 </form>
 </div>
 </template>
```

This is what our application will look like after applying the configurations mentioned above:


![Article section](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683186851755_localhost-3000-article.png)


Next, we’ll assign the form data values to two separate variables and push the values to Appwrite. Let’s also clear the form once the data is sent.

```js
<script>
import { Client, Databases, ID } from "appwrite";
export default {
 data() {
   return {
     title: "",
     content: "",
    };
   },
  methods: {
    async submitForm() {
     const client = new Client();
     const databases = new Databases(client);
     client
      .setEndpoint("OUR_API_ENDPOINT") // Your API Endpoint
      .setProject("OUR_PROJECT_ID"); // Your project ID
     try {
      const response = await databases.createDocument(
         "OUR_DATABASE_ID", 
         "OUR_COLLECTION_ID",
         ID.unique(),
            {
             title: this.title,
             content: this.content,
            }
          );
           console.log(response);
         this.name = "";
         this.comment = "";
        } catch (error) {
           console.error(error);
         }
       },
     },
   };
</script>
```   

Head back to the **Post** section after pushing the new post to Appwrite. This ection will contain an updated version of the posts.

##Applying dynamic routing

We want to click on each post in the blog and view its content. Create a `pages/post/[id].vue` file to render a post’s content when clicked. Copy and paste the code snippet below:

```js
<template>
 <main>
  <nav>
   <h2 class="logo u-padding-32 eyebrow-heading-1 u-color-text-pink">
  <NuxtLink to="/">Pink Blog</NuxtLink>
   </h2>
  </nav>
<div v-if= "isIdMatch">
  <h2 class="heading-level-3 u-text-left u-padding-64">{{ post.title }}</h2>
   <p class="heading-level-7 u-padding-64" style="line-height: 2.5rem">
     {{ post.content }}
   </p>
</div>
</main>
</template>
<script>
import { useRouter, useRoute } from "vue-router";
import { Client, Databases } from "appwrite";
const router = useRouter();
const route = useRoute();
const id = route.params.id;
const isIdMatch = router.currentRoute.value.params.id === id;
const post = ref(null)
const client = new Client();
const databases = new Databases(client);
  client
    .setEndpoint("OUR_API_ENDPOINT") // Your API Endpoint
    .setProject("OUR_PROJECT_ID"); // Your project ID
const singlePost = databases.getDocument(
  "OUR_DATABASE_ID",
  "OUR_COLLECTION_ID",
  "`${id}`"
 );
onMounted(() => {
  singlePost.then(
  function (response) {
     post.value = response;
     console.log(post.value); // Success
    },
   function (error) {
     console.log(error); // Failure
    }
   );
 });
</script>
```

Here, the code snippet will do the following:

- Gets the route ID of the clicked post.
- Creates an instance to access the post via its route ID.
- Renders the post only if the fetched post’s ID matches the route id.

The application will look like this after applying the configurations above:

![Final Demo](https://paper-attachments.dropboxusercontent.com/s_3E99E643F934FCD5B4F4BE363C3B8373B361448419DD03EF63C88F9369A7F247_1683185170584_ezgif.com-video-to-gif+3.gif)

#Conclusion

Appwrite’s Relationships feature provides a database management system that helps reduce redundant information while creating data sets. Pink Design's flexible framework promotes development and responsiveness while providing a broad range of customization. This combination allows us to build applications with exciting UIs swiftly, without hassle.

#Resources
- [Creating an Appwrite Instance in 3 steps](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba)
- [Pink Design Documentation](https://pink.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite Documentation](https://appwrite.io/docs/getting-started-for-web?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Nuxt Documentation](https://nuxt.com/)
- [Appwrite Relationship Databases](https://appwrite.io/docs/databases-relationships?utm_source=hackmamba&utm_medium=hackmamba-blog)