Deploy a WebAssembly-powered Next.js app on Vercel serverless functions
Tags: webdev, nextjs, webassembly, tutorial
Published: 2024-01-25 12:53:31

## What you will find in this article?

Javascript is great but sometimes you need to run some code that is not directly available in Javascript. In this article, we will see how to run WebAssembly in serverless Node.js functions using Next.js and Vercel.


![thanks JS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lmk95cor18hqe0rmzwnj.gif)



## Papermark - the open-source DocSend alternative.

Before we begin, let me introduce you to Papermark. It's an open-source alternative to DocSend that helps you manage secure document sharing, including real-time analytics, custom domains and more. It's all open-source!

I would be absolutely thrilled if you could give us a star! Don't forget to share your thoughts in the comments section ❤️
[https://github.com/mfts/papermark](https://github.com/mfts/papermark)

[![Papermark App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igzk8cdssbmla9uf1544.png)](https://github.com/mfts/papermark)

## Setup the project

Let's go ahead and set up our project environment for our page number counting application. We'll be creating a Next.js app, and set up to run a WebAssembly package in a serverless function.

### Setting up Next.js with TypeScript and Tailwindcss

We'll use `create-next-app` to generate a new Next.js project. We'll also be using TypeScript and Tailwind CSS, so make sure to 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
```

### Install vercel/blob

Vercel Storage released a new package called `@vercel/blob` that lets you upload files to Vercel's file storage. We'll use this package to store our PDF files.

```bash
npm install @vercel/blob
```

### Install mupdf

MuPDF is a lightweight PDF, XPS, and E-book viewer, but it comes with a **wasm** package that lets you manipulate and annotate PDFs. We'll use this package to count the number of pages in a PDF.

```bash
npm install mupdf
```

## Building the application

Now that we have our setup in place, we are ready to start building our application. The main features we'll cover are:

- Upload a file
- Count total number of pages with WASM package
- Make WASM run on Vercel's serverless functions

### #1 Upload a file

Let's create a serverless function that uploads a file to Vercel's file storage. Send a `POST` request to `/api/upload` with a `file` key in `FormData` object in the request body.

```ts
// pages/api/upload.ts
import { put } 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;

  if (!file) {
    return NextResponse.json(
      { error: "File name or file not submitted" },
      { status: 400 }
    );
  }

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

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

`blob` contains the data about our file including `url` which we'll use to access the file later.

_Caveat_: this serverless implementation only works with files smaller than 4MB. If you need to upload larger files, you'll want to use a **client-side upload** ([see more on GitHub](https://github.com/mfts/papermark/blob/main/components/documents/add-document-modal.tsx)).

### #2 Count total number of pages with WASM package

Now that we have our file uploaded, we can use the `mupdf` package to count the total number of pages in the PDF. We'll create a serverless function that takes the `url` from `blob` object of the file and returns the total number of pages.

Find the full documentation of the `mupdf` package on [their website](https://mupdf.readthedocs.io/en/latest/mupdf-wasm.html).

```ts
// pages/api/count-pages.ts
import { NextApiRequest, NextApiResponse } from "next";
// @ts-ignore
import mupdf from "mupdf";

export default async (req: NextApiRequest, res: NextApiResponse) => {
  // check if it's a POST method
  if (req.method !== "POST") {
    res.status(405).json({ error: "Method Not Allowed" });
    return;
  }

  try {
    const { url } = req.body as { url: string };
    // Fetch the PDF data
    const response = await fetch(url);
    // Convert the response to an ArrayBuffer
    const pdfData = await response.arrayBuffer();
    // Create a MuPDF instance
    var doc = mupdf.Document.openDocument(pdfData, "application/pdf");
    // Get the number of pages
    var n = doc.countPages();

    // Send the images as a response
    res.status(200).json({ numPages: n });
  } catch (error) {
    console.error("Error:", error);
    res.status(500).json({ error: "Internal Server Error" });
  }
};
```

This function will work fine locally, however, it will fail when deployed to Vercel. This is because the `wasm` file from `mupdf` node_modules package is not included in the deployment bundle. We'll fix this in the next step.

### #3 Make WASM run on Vercel's serverless functions

To make the `wasm` file available to Vercel's serverless functions, we'll need to include the `wasm` file from the package's node_modules folder in our deployment. We can do this by adding the following to our `next.config.js` file.

In this case we are using the `experimental.outputFileTracingIncludes` option to include the `wasm` file from `mupdf` package in our deployment to the API route `/api/count-pages`.

```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    outputFileTracingIncludes: {
      "/api/count-pages": ["./node_modules/mupdf/lib/*.wasm"],
    },
  },
};

module.exports = nextConfig;
```

That's it. Now we can deploy our application to Vercel and it will work as expected.

## Conclusion

And there you have it! We've built a simple app for counting numbers of pages in a PDF document using Next.js, WebAssembly and Vercel serverless functions. While the example here is simple, the same concepts can be applied to handle any other WebAssembly package that expands the capabilities of your app.

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

Keep experimenting and pushing the boundaries of what's possible with these technologies. Happy coding!

## Help me out!

If you found this article helpful and got to understand WebAssembly on Vercel serverless functions a bit better, I would be super grateful if you could give us a star! And don't forget to share your thoughts in the comments ❤️

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

![cat thanks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3etxn9ugxefj3jhwidgi.gif)