Create Blog Post Bookmarks in Next.js

Odewole Babatunde Samson - Jun 6 '22 - - Dev Community

Blog bookmark is a web browser feature used to save a website's URL address for future reference with the advantage of being able to be viewed from mobile devices and PCs anytime globally. Bookmarks save user and browser time, which is especially useful for webpages with long URLs or accessing a specific part of the site that might not be the site’s homepage.

What we will be building

In this post, we will create a blog using markdown where a user can bookmark blog posts and see bookmarked posts on another page. The user will be able to understand how to use Appwrite's database feature to add a bookmark function to blog posts.

GitHub



https://github.com/Tundesamson26/blog-bookmark.git


Enter fullscreen mode Exit fullscreen mode

Prerequisites

To get the most out of this article, we require the following:

  • A basic understanding of CSS, JavaScript, and React.js
  • Docker Desktop is installed on the computer, run the docker -v command to verify if we have Docker Desktop installed, if not, install it from here.
  • An Appwrite instance running on our computer; check this documentation to quickly learn how to create a local Appwrite instance, we will use Appwrite’s powerful database service and experience to manage our board.

Setting up the Next.js app

Next.js is an open-source React framework that enables us to build server-side rendered static web applications.

To create our Next.js app, we navigate to our preferred directory and run the terminal command below:



 npx create-next-app@latest
 # or
 yarn create next-app


Enter fullscreen mode Exit fullscreen mode

After creating our app, we change the directory to our project and start a local development server with:



cd <name of our project>
npm run dev


Enter fullscreen mode Exit fullscreen mode

To see our app, we then go to http://localhost:3000.

Installing marked and gray-matter

Now that we have our markdown posts in place, we want to retrieve them and show them on the homepage. We want to show the image, title, and link to the actual post.
Since the image and title are defined in the markdown file's frontmatter section, we need to find a way to extract them.

Luckily for us, there is a fantastic NPM package that can help us with this. Let’s install it.



  npm i marked gray-matter


Enter fullscreen mode Exit fullscreen mode

These packages allow us to parse the frontmatter section and the content section from a content string.

Installing Appwrite

Appwrite is an open-source, end-to-end, backend server solution that allows developers to build applications faster.

To use Appwrite in our Next.js application, we install the Appwrite client-side SDK by running this terminal command.



 npm install appwrite --save


Enter fullscreen mode Exit fullscreen mode

Creating a new Appwrite project

Running a local Appwrite instance gives us access to the console. To create an account, we go to the local Appwrite instance on whatever port it is started on. Typically, this is on localhost:80 or as specified during Appwrite's installation.

On the console, there is a Create Project button. Click on it to start a new project.

Appwrite create-project-tab

Our project dashboard appears once we have created the project. At the top of the page, there is a Settings bar. Click it to access our Project ID and API Endpoint.

Appwrite settings-tab

We copy our Project ID and API Endpoint, which we need to initialize our Web SDK code.

In the root directory of our project, we create a utils folder, which will hold our web-init.js file. This file configures Appwrite in our application.

In the utils/web-init.js file, we initialize our Web SDK with:



    // Init your Web SDK
    import { Appwrite } from "appwrite";
    export const sdk = new Appwrite();

    sdk
        .setEndpoint('http://localhost/v1') // Your Appwrite Endpoint
        .setProject('455x34dfkj') // Your project ID
    ;


Enter fullscreen mode Exit fullscreen mode

Creating a collection and attributes

On the left side of our dashboard, we select the Database menu. We create a collection in our database tab by clicking on the Add Collection button. This action redirects us to a Permissions page.

At the Collection Level, we want our Read Access and Write Access to have a value of role:all.

Appwrite collection

On the right of our "Permissions" page, we copy our collection ID, which we need to perform operations on documents in this collection.

Next, we go to our attributes tab to create the fields we want a document to have. These properties are title, date, excerpt, and cover_image.

Appwrite attribute-tab

Creating the Blog Application

We create this blog application with the GitHub gist below.

From the gist below, we have the index.js and we created a components folder in the root directory and a Post.js file inside.

In the index.js, we have the following:

  • Import required dependencies and components
  • Passed posts as props from the Post.js
  • Create state variables to get a post from the posts directory, get slug and frontmatter from posts, and get frontmatter

In the components/Post.js file, we rendered:

  • The cover_image that display the cover image from our markdown file for each post
  • The title of each post from the markdown file for the blog post
  • The date a post was posted and the excerpt of the blog post

Our application should look like this by now:

Blog-app

Making our Blog interact with our Database.

Creating an anonymous user session
Appwrite requires a user to sign in before reading or writing to a database to enable safety in our application. However, they allow us to create an anonymous session that we'll use in this project.

In the components/Post.jsfile, we import sdk from the web-init.js file.



import sdk from '../utils/web-init';


Enter fullscreen mode Exit fullscreen mode

Next, we create an anonymous user session once our app is mounted in the components/Post.js file.



async function createAnonymousSession(){
    try{
        await sdk.account.createAnonymousSession();
    }catch(err){
        console.log(err)
    }

}
useEffect(()=> {
    createAnonymousSession()
}, [])


Enter fullscreen mode Exit fullscreen mode

Creating database documents.

In the components/Post.js file, we write a handleBookmarks function to create documents in our collection.



const handleBookmarks = () => {
      let promise = sdk.database.createDocument(
        [COLLECTION ID],
        "unique()",
        {
          title: post.frontmatter.title,
          excerpt: post.frontmatter.excerpt,
          coverImage: post.frontmatter.cover_image,
          date: post.frontmatter.date,
        }
      );
      promise.then(
        function (response) {
          console.log(response); // Success
          alert('bookmark has been successfully saved');
        },
        function (error) {
          console.log(error); // Failure
        }
      );
}


Enter fullscreen mode Exit fullscreen mode

This handleBookmarks() function above does the following :

  • Uses the Appwrite createDocument() method, which creates a document using the collection ID and data fields to be stored. This collection ID is the same ID we copied from our Permissions Page earlier.
  • Alerts us when we have successfully saved our document, then clears the information in our local state variables.

Next, we pass our handleBookmarks() function into an onClick event listener on our Link element.



<Link href={`/blog/${post.Bookmark}`}>
   <a className="btn" onClick={handleBookmarks}>Bookmark</a>
</Link>


Enter fullscreen mode Exit fullscreen mode

Click on the Bookmark button and go to the Documents tab on Appwrite's project dashboard to see the saved documents.

Appwrite Document-tab

Creating our Bookmark Page

Listing documents

In the pages folder, we create a bookmark.js file. The bookmark.js file is responsible for listing our bookmarks.

In the pages/bookmark.js, we write this code for listing out documents in our collection.



 const listBookmarks = () => {
    let promise = sdk.database.listDocuments([COLLECTION_ID]);
    promise.then(
      function (response) {
        console.log(response); // Success
        alert("bookmarks list");
      },
      function (error) {
        console.log(error); // Failure
      }
    );
  };
  useEffect(() => {
    listBookmarks();
 }, []);


Enter fullscreen mode Exit fullscreen mode

In the code snippet above, We create a listBookmarks() function to display our bookmarks. We use Appwrite's listDocuments() method to do this.
We pass a collection ID parameter to the listBookmarks() method to specify what collection we want to access.

Our useEffect() hook runs the listBookmarks() function.

Deleting documents

In the pages/bookmark.js, we create a handleDelete() function to delete documents we do not want any more in our collection.



const handleDelete =  () => {
   let promise = sdk.database.deleteDocument(
    [COLLECTION_ID], 
    [DOCUMENT_ID]);
   promise.then(
     function (response) {
       console.log(response); // Success
       alert("item have been deleted successfully");
     },
     function (error) {
       console.log(error); // Failure
     }
   );
};


Enter fullscreen mode Exit fullscreen mode

The handleDelete() function above does the following:

  • Finds a document using its collection ID and the document ID gets passed into the function.
  • Deletes that document using Appwrite’s deleteDocument() method
  • Alerts us if we delete an item

Our blog page is incomplete without the styling. We add these styles in our global.css file.

Our Blog bookmark should look like this:

Bookmark Blog-app

Conclusion

This article discussed using Appwrite to quickly create, retrieve, and delete data on our database. With this, we created a bookmarked post in a Next.js application.

Resources

Here are some resources that might be helpful:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .