Building an Open Source DocSend alternative with Next.js, Vercel Blob and Postgres 🚀
Tags: react, opensource, nextjs, typescript
Published: 2023-06-07 14:32:35



## What you will find in this article?
You probably come across platforms for secure document sharing, tracking, and storage like DocSend, Dropbox, Google Drive, and the list goes on. The underlying functionality is you upload a document, share the link with a person or a group, and they can view the document via a unique link.


![fun gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkhhdyshc3yscbqm1x71.gif)


## Papermark - the first dynamic open-source alternative to DocSend. 

Just a quick background about us. Papermark is the dynamic open-source alternative to DocSend. We basically help to manage secure document sharing, including real-time analytics. All of it is open-source.

I would be super happy if you could give us a star! And let me know in the comments ❤️
[https://github.com/mfts/papermark](https://github.com/mfts/papermark)


![Papermark Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3k2p9q8t7mifk7c41dj.png)




## Setup the project

Here, I'll guide you through creating the project environment for the document sharing application. We'll set up the Next.js app.

### Set up tea

Before you get started, I recommend you to set up a package manager, like `tea` to handle your development environment. 

```bash
sh <(curl https://tea.xyz)

# --- OR ---
# using brew
brew install tea
```

`tea` let's you focus on developing, it will take care of installing `node`, `npm`, `vercel` and any other packages you may need for development. 
Just type the command and the package becomes available - even if you didn't install it before. 
The best part, `tea` installs all packages in a relocatable directory (_default: `~/.tea`_), so you don't have to worry about polluting your system files.

### Setting up Next.js with TypeScript and Tailwindcss
We are using `create-next-app` to generate a new Next.js project. We'll also be using TypeScript and Tailwind CSS, so we'll select those options when prompted.

```bash
npx create-next-app

# ---
# you'll be asked the following prompts
What is your project named?  my-app
Would you like to add TypeScript with this project?  Y/N
# select `Y` for typescript
Would you like to use ESLint with this project?  Y/N
# select `Y` for ESLint
Would you like to use Tailwind CSS with this project? Y/N
# select `Y` for Tailwind CSS
Would you like to use the `src/ directory` with this project? Y/N
# select `N` for `src/` directory
What import alias would you like configured? `@/*`
# enter `@/*` for import alias
```

### Setup Vercel Blob 

0. Sign up to vercel.com
1. Go to [https://vercel.com/dashboard/stores](https://vercel.com/dashboard/stores)
2. Create a new database; choose "Blob"
3. Don't forget to add @vercel/blob to your package.json
   ```bash
   npm install @vercel/blob
   ```
4. Done 🎉

![Vercel Blob Quickstart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kq6uqrrv1ulaill055me.png)


### Set up Vercel Postgres
 
0. Sign up to vercel.com
1. Go to [https://vercel.com/dashboard/stores](https://vercel.com/dashboard/stores)
2. Create a new database; choose "Postgres"
3. Done 🎉

![Vercel Postgres Quickstart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3vh3544p1fso0a2ejlp.png)

### Set up Prisma

Prisma is our database ORM layer of choice. It's a great tool for interacting with Postgres and other databases. We'll use it to create a schema for our database and generate TypeScript types for our database models.

```bash
npm install @prisma/client
npm install prisma --save-dev
```

## Building application 

1. Uploading the Document
2. Generating the Link for the Document
3. Viewing the Document

### #1 Uploading the document to Vercel Blob

First stop is the Document Upload. Here, we will lay the groundwork for our users to upload their documents to our platform. This process involves creating an interface to upload files, utilizing Next.js and Vercel Blob to store these documents, and implementing functionality to handle these files.

```tsx
// pages/upload.tsx
// this is a simplified example of a file upload form in Next.js
import { useState } from "react";

export default function Form() {
  const [currentFile, setCurrentFile] = useState<File | null>(null);

  const handleSubmit = async (event: any) => {
    event.preventDefault();

    // code to send the file to a serverless API function `/api/upload` 
    // to upload to Vercel Blob 
  };

  return (
    <form onSubmit={handleSubmit}>
      <h2>Upload a document</h2>

      <label>Upload a document</label>
      <input type="file" onChange={(e) => setCurrentFile(e.target.files?.[0] || null)} />
      
      <label>Name</label>
      <input type="text" placeholder="Acme Presentation" />
      
      <button type="submit">Upload document</button>
    </form>
  );
}

```

```ts
// pages/api/upload.ts
import * as vercelBlob from "@vercel/blob";
import { NextResponse, NextRequest } from "next/server";

export const config = {
  runtime: "edge",
};

export default async function upload(request: NextRequest) {
  const form = await request.formData();
  const file = form.get("file") as File;

  const blob = await vercelBlob.put(file.name, file, { access: "public" });

  return NextResponse.json(blob);
}
```

![Upload file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9k83s1c2k4bef9lewe4.png)

### #2 Generating Link to Document

Having tackled the uploading, let's now set our sights on the next milestone: generating a unique link for the uploaded document. This is how we allow users to share their documents with others. Essentially, we need to create a system where every document corresponds to a unique URL on our app.

```ts
// pages/api/create-link.ts
import { NextApiRequest, NextApiResponse } from "next";
import prisma from "@/lib/prisma";

export default async function handle(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === "POST") {
    const { name, url } = req.body;

    const document = await prisma.document.create({
      data: {
        name,
        url,
        link: {
          create: {},
        },
      },
      include: {
        link: true,
      }
    });

    return res.status(201).json({ document });
  }
}

```

When a document is uploaded to Vercel Blob, we'll store the blob URL, a reference to the document, in our Postgres database along with a unique identifier (like a UUID) for the document. When someone navigates to a URL on our platform that includes this identifier, we fetch the corresponding blob URL from Postgres and use it to load the document.



### #3 Viewing the document

With the document uploaded and the unique link generated, we're at the last stage of our quest: viewing the document. We need to create a way for users to view the documents they access via the unique link.

We can do this by creating a new Next.js page that matches the URL pattern of our document links. This page will fetch the blob URL from our Postgres database using the document identifier in the URL and load the document from Vercel Blob.

```tsx
// pages/view/[linkId].tsx
// this is a simple example of a Next.js page that loads a document
import React, {useEffect, useState} from 'react'
import {useRouter} from 'next/router'

const ViewDocumentPage = () => {
    const router = useRouter()
    const { linkId } = router.query
    const [documentURL, setDocumentURL] = useState(null)

    useEffect(() => {
        // code to fetch the document URL from Postgres using linkUUID
        // and then set it as documentURL
    }, [linkUUID])

    if (!documentURL) {
        return <div>Loading...</div>
    }

    return <iframe src={documentURL} width="100%" height="100%" />
}

export default ViewDocumentPage

```


## Conclusion

And just like that, our coding adventure concludes. Together, we've constructed a simple, open-source alternative to DocSend, tapping into the power of Next.js and Vercel Blob. It allows users to upload, share, and view documents, similar to many commercial platforms out there, but with our custom touch.

Thank you for reading. I am Marc, an open-source enthusiast. I am building [papermark.io](https://www.papermark.io) - the dynamic open-source alternative to DocSend.

For me coding is about continuous exploration and learning. 
Happy coding, friends!

## Help me out!
If you feel like this article helped you understand Vercel Blob better! I would be super happy if you could give us a star! And let me also know in the comments ❤️

[https://github.com/mfts/papermark](https://github.com/mfts/papermark)

![cat please](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65a2ac01ylmc6f7b9h2g.gif)