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
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
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
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
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"]
}
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" }, ...
}
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}`);
});
After that we simply execute the following command to run our server:
$ npm run dev > [email protected] 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:
// 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:
// 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:
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/
:
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:
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:
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)
:
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
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;
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" }); } },
); ...
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" }); } },
); ...
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
:
And after having done the whole process correctly we should receive a success response if the video is successfully uploaded to vimeo:
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;