DEV Community

Ramu Narasinga
Ramu Narasinga

Posted on • Edited on

How Documenso, an open-source Docusign alternative, handles pdf file upload in Next.js?

In this article, we analyse how Documenso, an open-source Docusign alternative, handles pdf file upload in Next.js app router.

But first, where is the code that takes care of upload? To find that out, we first need to know where in the user interface on Documenso we upload pdf files.

Where is the file upload related code?

When you visit /documents, you see a widget shown below that lets you

upload pdf files in the Documenso dashboard.

Image description

Since Documenso uses Next.js app router and the url ends with /documents, we are looking for a folder named documents in the Documenso source code. You will find this in the (dashboard)/documents/page.tsx, but straight away you don’t see anything that says “upload”.

export default async function DocumentsPage({ searchParams = {} }: DocumentsPageProps) {
  await setupI18nSSR();

  const { user } = await getRequiredServerComponentSession();

  return (
    <>
      <UpcomingProfileClaimTeaser user={user} />
      <DocumentsPageView searchParams={searchParams} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

This means, we now have to search for “upload” in DocumentsPageView and UpcomingProfileClaimTeaser. Looking at the imports used in documents/upcoming-profile-claim-teaser.tsx, there’s nothing related to “upload”, that means, it has to be in DocumentsPageView.

In the documents-page-view.tsx, you will find this below shown import at line 27.

import { UploadDocument } from './upload-document';
Enter fullscreen mode Exit fullscreen mode

and this Component is the first in order in this documents-page-view.tsx, as shown below:

<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
      <UploadDocument team={currentTeam} />

      <div className="mt-12 flex flex-wrap items-center justify-between gap-x-4 gap-y-8">
        <div className="flex flex-row items-center">
          {team && (
Enter fullscreen mode Exit fullscreen mode

Hence the reason why you see the Upload widget followed by list of documents that you may have uploaded before.

Image description

DocumentDropzone

You will find this below code at line 133 in the upload-document.tsx,

that is imported from document-dropzone.tsx

<DocumentDropzone
 className="h-[min(400px,50vh)]"
 disabled={remaining.documents === 0 || !session?.user.emailVerified}
 disabledMessage={disabledMessage}
 onDrop={onFileDrop}
 onDropRejected={onFileDropRejected}
/>
Enter fullscreen mode Exit fullscreen mode

we are interested in onFileDrop function. This function has 50+ lines of code. Let’s focus only on core functionality that does the upload.

const { type, data } = await putPdfFile(file);

const { id: documentDataId } = await createDocumentData({
  type,
  data,
});

const { id } = await createDocument({
  title: file.name,
  documentDataId,
  teamId: team?.id,
});
Enter fullscreen mode Exit fullscreen mode

This code is picked from onFileDrop function. putPdfFile is a function imported from lib/universal/upload/put-file.ts

putPdfFile

/**
 * Uploads a document file to the appropriate storage location and creates
 * a document data record.
 */
export const putPdfFile = async (file: File) => {
  const isEncryptedDocumentsAllowed = await getFlag('app_allow_encrypted_documents').catch(
    () => false,
  );

  // This will prevent uploading encrypted PDFs or anything that can't be opened.
  if (!isEncryptedDocumentsAllowed) {
    await PDFDocument.load(await file.arrayBuffer()).catch((e) => {
      console.error(`PDF upload parse error: ${e.message}`);

      throw new AppError('INVALID_DOCUMENT_FILE');
    });
  }

  if (!file.name.endsWith('.pdf')) {
    file.name = `${file.name}.pdf`;
  }

  const { type, data } = await putFile(file);

  return await createDocumentData({ type, data });
};
Enter fullscreen mode Exit fullscreen mode

This function above has some checks against uploading encrypted document.

putFile

This below code is picked from upload/put-file.ts

/**
 * Uploads a file to the appropriate storage location.
 */
export const putFile = async (file: File) => {
  const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT');

  return await match(NEXT_PUBLIC_UPLOAD_TRANSPORT)
    .with('s3', async () => putFileInS3(file))
    .otherwise(async () => putFileInDatabase(file));
};
Enter fullscreen mode Exit fullscreen mode

so this function supports file uploads to either S3 or upload to database. Interesting.

About me:

Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.

I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com

My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects

References:

  1. https://github.com/documenso/documenso/tree/main

  2. https://github.com/documenso/documenso/blob/main/apps/web/src/app/(dashboard)/documents/page.tsx

  3. https://github.com/documenso/documenso/blob/main/apps/web/src/app/(dashboard)/documents/upcoming-profile-claim-teaser.tsx

  4. https://github.com/documenso/documenso/blob/main/apps/web/src/app/(dashboard)/documents/documents-page-view.tsx#L41

  5. https://github.com/documenso/documenso/blob/main/apps/web/src/app/(dashboard)/documents/upload-document.tsx#L65

  6. https://github.com/documenso/documenso/blob/main/packages/lib/universal/upload/put-file.ts#L22

Top comments (1)

Collapse
 
agria_haneefa profile image
Mohamed Haneefa haneefa

I have some doubts in documenso
can you please check this.

dev.to/agria_haneefa/unable-to-acc...