Building a FileStorage Application using Xata, Cloudinary, and NextJs.
Tags: filestorage, xata, nextjs, cloudinary
Published: 2022-11-23 07:26:53

Storing files became necessary due to the ease with which pictures, videos, and other files can be generated; the smartphone era played a significant role in this; people want to save their memories so that they can always refer back to them in the future.

This article explains how to build a file storage application in Next.js, using Xata to provide the database service and Cloudinary to store the files.


## Check out the Live demo of the application we will create.


![Demo Picture](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667211091635_clouddrop.png)


[Demo](https://clouddrop.netlify.app/) | [Code](https://github.com/Josh4324/CloudDrop)


## Prerequisite
- You should have a basic understanding of Javascript
- You should have a basic knowledge of React Js and Next Js.


## Tech Stack
- [NextJs](https://nextjs.org/) is a React framework that builds on the functionality of React Js, it simplifies the creation of pages and API endpoints.
- [Cloudinary](https://cloudinary.com/home-102622?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) is a cloud media platform that allows users to upload files easily.
- [Xata](https://xata.io/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) is a serverless data platform that allows users to store data, search for data and get insights into the data.


## Table of Contents
- Project Setup and Installation
    - Next Js pages setup
    - Creating database and tables on Xata
    - Cloudinary
- Connecting Cloudinary to the application
- Connecting Xata to the application
- Conclusion


## Project Setup and Installation

**NEXT JS PAGES SETUP**

To begin, we'll create a new Next.js app, launch our terminal, create a new empty directory and execute the following command:

```
    npx create-next-app@latest
```
Make sure you are in the correct directory and install the dependencies using a package manager like `npm`, `yarn`, or `pnpm`:

```
    npm i bcryptjs react-notifications 
```
**Create skeleton pages for the application**

We will create the necessary pages needed for the application with the code below, we will create the following pages.

- Index.js
- all.js
- files.js
- photos.js
- videos.js
- login.js
- signup.js

 

styles/global.css

{% gist
https://gist.github.com/Josh4324/0ec65233785eaff41cb27c22db1b8c99
%}

Global css for all the pages.

styles/Home.module.css

{% gist
https://gist.github.com/Josh4324/43118a2fb2d9420cec3bf8fb3b9fcf7c
%}

components/Sidebar.module.css

{% gist
https://gist.github.com/Josh4324/d06b92ebbb3c369184385894512b7710
%}

**CSS for the sidebar components.**

pages/_document.js

{% gist
https://gist.github.com/Josh4324/d7532ee4b147a3cee6f2564c1ea2ff6e
%}

We used the **_document.js** file to add font to our application.

components/Sidebar.js

{% gist
https://gist.github.com/Josh4324/c6d70124bd0e281f32fc83f08c908fd7
%}

The Sidebar component is used for switching between pages, and it is used on all the pages.

pages/index.js

{% gist
https://gist.github.com/Josh4324/496cc97cb84512f31d60a92bfca5e6a1
%}

The index page allows users to upload their files and view recently uploaded files.

pages/all.js

{% gist
https://gist.github.com/Josh4324/46626b0411a36ffe2c1f9f145c1b7c7a
%}

The **all.js** page is where users can view all their files.

pages/files.js

{% gist
https://gist.github.com/Josh4324/a585685b97d31f02dc0cac4c150b87e7
%}

The **files.js** page is where users can view their documents such as pdf, word document, excel documents and others.

pages/photos.js

{% gist
https://gist.github.com/Josh4324/b88133a34f41655202a5dfe6b03c8938
%}

The photos page is where users can view all their pictures.

pages/videos.js

{% gist
https://gist.github.com/Josh4324/c09608d5d8e313feae048fefc667636b
%}

The videos page is where users can view all their videos.

pages/login.js

{% gist
https://gist.github.com/Josh4324/bbd4f932c915d4ef8d6efa720e97f3ca
%}

The login page is the access page to the application, and only authenticated users can access the application.

pages/signup.js

{% gist
https://gist.github.com/Josh4324/0fa44a93d08abc5648866edf9b9992df
%}

The signup page is where the users register to access the application.


**CREATING DATABASE AND TABLES ON XATA**

We need to log into xata.io to be able to create a database and add tables to the database.


![Paper8](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667224838355_xata.png-mh.png)


Sign in with GitHub or Google Mail or  personal email.

![Paper9](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667225035066_xata2.png-mh.png)


Add a workspace; we can do this by entering the workspace name.

![Paper10](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667240007483_xata3.png-mh.png)


Add a Database, we can do this by entering the database name and selecting the preferred zone.

![Paper1](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667240182909_xata3.png-mh+1.png)


Next, let's add a table. We can do this by selecting the database and clicking **Add a table**. Select empty table and enter a table name. We can add users table first.

![Paper2](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667240958369_xata4.png-mh.png)


Let's also add fields to the table. The users table needs an **id, email,** **and** **password field**, while the files table needs an **id, name, type, date, url, and userid field**. The **id** field is created by default and autogenerated. Click on the highlighted **icon** and select the appropriate data type.


![Paper3](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667242643519_xata5.png-mh.png)


The datatypes for the Users fields are 

- Email - String
- Password - String 

The datatypes for the Files fields are 

- Name - String
- Type - String
- Date - date 
- Url - String 
- Userid -String.

Users table

![Paper4](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667244491462_xata6.png)


Files table

![Paper5](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667244785228_xata7.png)


Copy workspace base API URL.

![Paper6](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667245232381_xata8.png)


To generate the API key, go to account settings, enter the name of the API key and click o save.

![Paper7](https://paper-attachments.dropboxusercontent.com/s_53D394CA9EC8054C4D57206D6F81F4051344F365ABEA51B6BCB0963F87164624_1667245675884_xata9.png-mh.png)


[**CLOUDINARY**](https://cloudinary.com/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
Signup on cloudinary.com and login to your account.

Get the **cloudname** on the dashboard and the **presetname** on the settings page.


## Connecting Cloudinary to the application

We added the Cloudinary script to our index.js page during the page setup. Now, let's add the necessary code to upload files to Cloudinary and get the url link to our file.

{% gist
https://gist.github.com/Josh4324/20808688ce0bd8652c9927a4f399deaa
%}

## Connecting Xata to the application

This application will do 4 significant actions on Xata: signup, login, save and retrieve files. 

**Setup API Routes on NextJS.**
Before we set up our API routes, we need to add our .env file.

.env


    XATA=Bearer <Insert your API KEY>
    salt=<Insert anything>
    database=<workspace base API URL>
    

api/signup.js

{% gist
https://gist.github.com/Josh4324/720f4dd51d71da6fcb6f07a037345822
%}

The signup API route expects a request body from the client, it is a POST request, and the first thing it does is check if the email already exists; if it does, it returns an error message, but if it does not, it creates a new user in the database.

api/login.js

{% gist
https://gist.github.com/Josh4324/62908da7ffd90cc2e1e59a291d26a269
%}

The login API route expects a request body, a POST request, and it gets the email needed to check if the user exists; if the user exists, the user object is sent to the client.

api/upload.js

{% gist
https://gist.github.com/Josh4324/99b8fc9c00eed9dadbd450181598016b
%}

The upload route API expects the request object, which contains the file name, file url, type, and other data; the upload API stores the file in the database. It is a POST request.

api/files

{% gist
https://gist.github.com/Josh4324/453857dadde47f84c05f8c140108a394
%}

The files route API is a GET request. We need to retrieve the files we have stored in the database, which is what the files API route does. It uses the search query, which is the **userid**, to fetch all files associated with the user.

**Integrate API routes with the pages**

pages/signup.js

{% gist
https://gist.github.com/Josh4324/e3be93877a3685721035f14bcf18a96e
%}

We have imported **bcryptjs** and **react notifications** to help with password hashing and the user experience. We made use of the useRef hook to get our input value.
 
The sign function is where the action is taking place; it checks if the email or password is empty and returns an error message if it is empty, then we use bcryptjs to hash the password, we send the credentials to our signup API route, and if the request is successful, we store the userid and email in the client using localStorage API, and redirect the user to the index page, if the user already exists, we display an error message.

pages/login.js

{% gist
https://gist.github.com/Josh4324/857a3b4975098a7ce0e7253beba9a951
%}

The log function checks if the email or password is empty and returns an error message If it is empty, we send the request to the login API route; then we use bcryptjs to compare the password; if the password is wrong, we display an error message, if the password is correct, we store the userid and email in the client using the localStorage API, and redirect the user to the index page.

pages/index.js

{% gist
https://gist.github.com/Josh4324/4e6fe369f094f7af08ed752807e552e8
%}

We set up our useState for the different types of files, and we also set up our useEffect to check if the user has been authenticated. We are doing this by checking the userid in the localStorage. We also get all the files when the page is refreshed, and we do this by calling the files API route inside the useEffect. We have also updated our showWidget function to store the url link and associated information we get when we upload our files to Cloudinary. We are using the upload API route for this inside the showWidget function. We are also getting the type of file by checking the extension of the files, which will help us classify the files into different categories. Finally, we use the different array categories for the file type to display the files on the page.

pages/all.js

{% gist
https://gist.github.com/Josh4324/adad233a43b73102b9e50820feb21392
%}

The all page uses the api/files endpoint to get all the uploaded files and displays them on the page.

pages/files.js

{% gist
https://gist.github.com/Josh4324/fe1933c1ad1814d41b53319830584f7a
%}

The files page uses the api/files endpoint to get all the uploaded files, filters the result for documents like pdf and Microsoft word, and displays them on the page.

pages/photos.js

{% gist
https://gist.github.com/Josh4324/618875dd6d71e69d67c32c3a74555915
%}

The photos page uses the api/files endpoint to get all the uploaded files, filters the result for the images, and displays them on the page.

pages/videos.js

{% gist
https://gist.github.com/Josh4324/28df820b087531b559b9eac0cb8c4a9c
%}

The videos page uses the api/files endpoint to get all the uploaded files, and filters the results for videos, and displays them on the page.
 
For all the pages, we are checking if the user is authenticated inside the useEffect, we are also calling the files API route to get the files associated with the user. We use the useState to keep track of our files and display them on the page. 



## Conclusion

We built a fully functional file storage application using Xata, Cloudinary, and NextJs, where users can upload images, videos, documents, and other files.
 
Users can sign up and log in to the application to start using it; users' passwords are hashed for security, and only users can access the files they store in the application.
 
I hope you found this tutorial useful.
 
Thank you for reading.

Link to the demo - https://clouddrop.netlify.app/

Content created for the [Hackmamba](https://hackmamba.io/) Jamstack Content Hackathon with [Xata](https://xata.io/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) and [Cloudinary](https://cloudinary.com/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog).