DEV Community

Cover image for Upload videos to Vimeo using NodeJS, ExpressJS and TypeScript
Santiago Moraga Caldera
Santiago Moraga Caldera

Posted on

Upload videos to Vimeo using NodeJS, ExpressJS and TypeScript

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

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:

$ mkdir project-name
$ cd project-name/
$ npm init -y
Enter fullscreen mode Exit fullscreen mode

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:

src
├── app.ts
└── routes
    └── videos.ts
Enter fullscreen mode Exit fullscreen mode

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:

$ npm i express axios dotenv multer
Enter fullscreen mode Exit fullscreen mode

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:

npm i typescript ts-node morgan nodemon @types/express @types/morgan @types/multer @types/node -D
Enter fullscreen mode Exit fullscreen mode

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:

// 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"]
}
Enter fullscreen mode Exit fullscreen mode

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:

// 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"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

4. Basic server setup

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

// 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}`);
});
Enter fullscreen mode Exit fullscreen mode

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

$ 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
Enter fullscreen mode Exit fullscreen mode

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:

// 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;
Enter fullscreen mode Exit fullscreen mode

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:

// 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}`);
});
Enter fullscreen mode Exit fullscreen mode

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

Api response successfully

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

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

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

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

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:

// .env

VIMEO_ACCESS_TOKEN=your_access_token_here
Enter fullscreen mode Exit fullscreen mode

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:

// 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;
Enter fullscreen mode Exit fullscreen mode

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:

// 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" });
    }
  },
);

...
Enter fullscreen mode Exit fullscreen mode

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.

// 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" });
    }
  },
);

...
Enter fullscreen mode Exit fullscreen mode

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

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

The complete code should look like this:

// 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;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)