Upload videos to Vimeo using NodeJS, ExpressJS and TypeScript
Tags: node, express, typescript, api
Published: 2024-07-10 17:17:13

There are many services that offer you the opportunity to store your videos or images in them, some of these can be Cloudinary, Amazon S3 and more. But the protagonist of this tutorial is Vimeo.

## What is Vimeo?

Vimeo is a video hosting and distribution platform that allows users to upload, share and view high-definition videos. Vimeo is known for its focus on video quality and for offering advanced tools for content creators, such as customization options, detailed analytics and privacy controls.

## Requirements to develop the project

- NodeJS installed
- Vimeo account

## Project development

**1. External links**

- [Project repository - Github](https://github.com/Remy349/upload-videos-vimeo-node)

**2. Initial setup**

The first thing is to create a folder where all the necessary content and code will be stored, then create our package.json to install all the necessary dependencies:

```Shell
$ mkdir project-name
$ cd project-name/
$ npm init -y
```

Now that we have our package.json file we are going to define the necessary file and folder structure so we can focus on the code. We will create a folder src in the root of our project, inside src is where we will store our necessary files, the structure would be the following one:

```Shell
src
├── app.ts
└── routes
    └── videos.ts
```

**3. Installing necessary dependencies and typescript setup**

It is time to install the necessary packages for the development of the project, in the terminal of our project we will execute the following:

```Shell
$ npm i express axios dotenv multer
```

These will be the basic dependencies of our project, among the packages we have "multer" which is a node.js middleware for handling multipart/form-data, which is mainly used to upload files, "dotenv" to handle environment variables, "axios" for http requests and "express" that we will use to develop our api.

Now we will proceed to install the development dependencies and packages needed to configure typescript in our project:

```Shell
npm i typescript ts-node morgan nodemon @types/express @types/morgan @types/multer @types/node -D
```

Once everything is installed we will configure typescript and nodemon, nodemon is a tool that helps develop Node.js based applications by automatically restarting the node application when file changes in the directory are detected.

We will create a tsconfig.json file to configure typescript:

```Json
// tsconfig.json

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}
```

In our package.json we will add a new command for nodemon configuration in our project, also a command to compile our typescript and one more to run in production mode:

```Json
// package.json

{
  "name": "upload-videos-vimeo-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon src/app.ts",
    "build": "npx tsc",
    "start": "node src/app.js"
  },
  ...
}
```

**4. Basic server setup**

Let's go to our app.ts file to start the basic configuration of our server:

```Typescript
// app.ts

import "dotenv/config";

import express from "express";
import morgan from "morgan";

const app = express();

const PORT = 3000;

app.use(morgan("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.listen(PORT, () => {
  console.log(`Server is running at http://localhost:${PORT}`);
});
```

After that we simply execute the following command to run our server:

```Shell
$ npm run dev

> upload-videos-vimeo-node@1.0.0 dev
> nodemon src/app.ts

[nodemon] 3.1.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node src/app.ts`
Server is running at http://localhost:3000
```

**5. Create api path for uploading videos**

Let's go to our videos.ts file to create the path we will use to process the video upload to vimeo:

```Typescript
// videos.ts

import { Router, Request as ExRequest, Response as ExResponse } from "express";

const videoRouter = Router();

videoRouter.post("/upload", async (req: ExRequest, res: ExResponse) => {
    try {
      res.status(201).json({ message: "Video successfully uploaded" });
    } catch (error) {
      res.status(500).json({ message: "Internal server error" });
    }
  },
);

export default videoRouter;
```

In the code we have written we are declaring the method and the path we will use to process the video upload to vimeo, the logic will be inside a try/catch block to handle possible errors that may occur when processing upload.

At the end we are exporting our videoRouter variable to use it in our app.ts file to set the access of this route in our api. Let's go into our app.ts file to update the code in it and use the videoRouter variable:

```Typescript
// app.ts

import "dotenv/config";

import express from "express";
import morgan from "morgan";

// This is new
import videoRouter from "./routes/videos";

...

app.use(express.urlencoded({ extended: true }));

// This is new
app.use("/api/videos", videoRouter);

app.listen(PORT, () => {
  console.log(`Server is running at http://localhost:${PORT}`);
});
```

If you visit the path `http://localhost:3000/api/videos/upload` using the `POST` method you should see the following:

![Api response successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ygr25u84bry4yqii7p51.png)

**6. Upload videos to vimeo using its api**

It is time to start integrating vimeo in our api, in order to upload videos to vimeo we will make use of its api. We must go to the developers section of vimeo using the link `https://developer.vimeo.com/`:

![Vimeo developer website](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qd1vftcontpj5fwobbxv.png)

Something very important that we need to be able to upload videos to vimeo is that we need an access token. For that we will click on the `Create an app` button:

![Create an app button from vimeo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dd86jyxoopccmq9bki9z.png)

After clicking on the button to `create an app`, a form will appear with basic information that we will have to fill out in order to get our access token:

![Basic form to create an app on vimeo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8hfqq438cc4wz5ypvso.png)

Once the form is filled out, it will redirect us to the panel to obtain our access token. In the `Generate Access Token` section we will click on `Authenticated(you)`:

![Generate access token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9p167matj6pt5b4a7v71.png)

To be able to use the vimeo api without problems we must allow our token to edit and upload videos. Click on the `Private`, `Edit` and `Upload` checkboxes.

Once we have everything we need, we will go to the vimeo api documentation to start the process of uploading our videos to the platform `https://developer.vimeo.com/api/upload/videos`.

Before continuing with the development of our api we must create an `.env` file in the root of our project to save the access token we obtained:

```Shell
// .env

VIMEO_ACCESS_TOKEN=your_access_token_here
```

In our videos.ts file we will start configuring multer so that our api is able to receive and process files. In this case we are interested in videos:

```Typescript
// videos.ts

import { Router, Request as ExRequest, Response as ExResponse } from "express";
import multer from "multer";

const videoRouter = Router();

const storage = multer.memoryStorage();

const upload = multer({ storage });

videoRouter.post("/upload", upload.single("file"), async (req: ExRequest, res: ExResponse) => {
    try {
      res.status(201).json({ message: "Video successfully uploaded" });
    } catch (error) {
      res.status(500).json({ message: "Internal server error" });
    }
  },
);

export default videoRouter;
```

The new code we have written is so that our api can process the files it receives, using `multer.memoryStorage` we are telling multer that the file will be stored temporarily in memory and in the `post` function of our videoRouter we are telling it to process a single file through the name `file`, this is achieved by using `upload.single("file")`.

After having configured multer we will make a call to the vimeo api using the `post` method to create the process of uploading the video to our panel in our vimeo account:

```Typescript
// videos.ts

...

videoRouter.post("/upload", upload.single("file"), async (req: ExRequest, res: ExResponse) => {
    try {
      const VIMEO_ACCESS_TOKEN = process.env.VIMEO_ACCESS_TOKEN;

      const file = req.file;

      if (!file) {
        return res.status(400).json({ message: "File not uploaded" });
      }

      const response = await axios.post(
        "https://api.vimeo.com/me/videos",
        {
          upload: {
            approach: "tus",
            size: `${file.size}`,
          },
        },
        {
          headers: {
            Authorization: `Bearer ${VIMEO_ACCESS_TOKEN}`,
            "Content-Type": "application/json",
            Accept: "application/vnd.vimeo.*+json;version=3.4",
          },
        },
      );

      res.status(201).json({ message: "Video successfully uploaded" });
    } catch (error) {
      res.status(500).json({ message: "Internal server error" });
    }
  },
);

...
```

In the code we added we are creating a variable to store our access token and another one to handle the file information we are receiving through the request. After this we create a small validation in case an empty request is sent without a file to process.

After that we create the `post` request to the vimeo api `https://api.vimeo.com/me/videos`, we send as data the size of the file, we do it using `file.size` and in the headers we attach our access token. The data we receive after making the request will be stored in a variable, in this case call it `response`.

Now that we have created the request to upload our video to vimeo the last thing we need to do is to complete the request, to complete the request we must make a `patch` request to the vimeo api sending as data the content of the file that we receive in its binary format or buffer, we will achieve this using our variable `file` its property `file.buffer`.

```Typescript
// videos.ts

...

videoRouter.post("/upload", upload.single("file"), async (req: ExRequest, res: ExResponse) => {
    try {
      ...

      const uploadLink: string = response.data.upload.upload_link;

      await axios.patch(uploadLink, file.buffer, {
        headers: {
          "Content-Type": "application/offset+octet-stream",
          "Upload-Offset": "0",
          "Tus-Resumable": "1.0.0",
        },
      });

      res.status(201).json({ message: "Video successfully uploaded" });
    } catch (error) {
      res.status(500).json({ message: "Internal server error" });
    }
  },
);

...
```

The `uploadLink` variable will be used to complete the video upload, it is a link that we receive as a response from our `response` variable after completing the `post` request.

As a final point we make the patch request to the vimeo api sending as parameter the link `uploadLink` and as data in the body of the request the content of the file in its buffer format using `file.buffer`. With this our video will be successfully uploaded to vimeo.

We send the file to our server using the format `multipart/form-data`:

![Sending file to our server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9s914ow1m3inwujv3ku.png)

And after having done the whole process correctly we should receive a success response if the video is successfully uploaded to vimeo:

![Api response successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ygr25u84bry4yqii7p51.png)

The complete code should look like this:

```Typescript
// videos.ts

import axios from "axios";
import { Router, Request as ExRequest, Response as ExResponse } from "express";
import multer from "multer";

const videoRouter = Router();

const storage = multer.memoryStorage();

const upload = multer({ storage });

videoRouter.post("/upload", upload.single("file"), async (req: ExRequest, res: ExResponse) => {
    try {
      const VIMEO_ACCESS_TOKEN = process.env.VIMEO_ACCESS_TOKEN;

      const file = req.file;

      if (!file) {
        return res.status(400).json({ message: "File not uploaded" });
      }

      const response = await axios.post(
        "https://api.vimeo.com/me/videos",
        {
          upload: {
            approach: "tus",
            size: `${file.size}`,
          },
        },
        {
          headers: {
            Authorization: `Bearer ${VIMEO_ACCESS_TOKEN}`,
            "Content-Type": "application/json",
            Accept: "application/vnd.vimeo.*+json;version=3.4",
          },
        },
      );

      const uploadLink: string = response.data.upload.upload_link;

      await axios.patch(uploadLink, file.buffer, {
        headers: {
          "Content-Type": "application/offset+octet-stream",
          "Upload-Offset": "0",
          "Tus-Resumable": "1.0.0",
        },
      });

      res.status(201).json({ message: "Video successfully uploaded" });
    } catch (error) {
      res.status(500).json({ message: "Internal server error" });
    }
  },
);

export default videoRouter;
```