In order to add data to the Webflow CMS, we need to first retrieve it from somewhere. In this example, we're getting data from the movies database api.
We also need to consider the Webflow API rate limit (requests per minute). The CMS plan has a limit of 60 and the Business site plan has a limit 120 requests per minute. We're using the Bottleneck npm package to accommodate this limit.
We are also using the Webflow CMS api client to interact with the Webflow CMS.
The movies api includes many genres for each movie. This means we'll need to use a multi-reference field in Webflow to include the different genres. However, we can't just add the genre name as a value for the multi-reference field, it has to be the id of the referenced collection item.
To resolve this, we ran a script to initially add all of the genres to Webflow, then added that array of genres in our script while including a new property for the Webflow collection item id for that genre (the function called fetchGenres()
at the end of the code). This way, we can find movies in the api according to the genre id of the api and then add the relevant genre in Webflow based on the collection id.
When retrieving data from the movies collection, we get 1) 20 results at a time and 2) the page count. In this example, we set the maximum page count to 401 which means our code will make api calls repeatedly until we reach 400 pages — 400 * 20 = 8000 items
added to Webflow.
While retrieving 20 movies at a time, we loop over each movie and call another function (using our Bottleneck method) to create the collection item with the Webflow CMS api client. As a response for each successfully created item, we print out the name of the item/movie.
// import neccessary packages import axios from "axios"; import Webflow from "webflow-api"; import Bottleneck from "bottleneck"; import getYear from "date-fns/getYear/index.js"; import { parseISO } from "date-fns"; import * as dotenv from "dotenv"; dotenv.config(); // set up api task scheduler to respect Webflow api rate limit of 120 const limiter = new Bottleneck({ maxConcurrent: 2, minTime: 1000, }); // variable containing the id of the genre and collection id from Webflow which we'll use to add our referenced collection items const allMovieGenres = [ { id: "28", name: "Action", itemId: "6350c466177fc03ab366dbde" }, { id: 12, name: "Adventure", itemId: "6350c466fc61527feb98ed8d" }, { id: 16, name: "Animation", itemId: "6350c4669fddb42553273d22" }, { id: 35, name: "Comedy", itemId: "6350c46611c67034a8dd3950" }, { id: 80, name: "Crime", itemId: "6350c46672526eaf9a608e0f" }, { id: 99, name: "Documentary", itemId: "6350c46646bb1dba9bac4506" }, { id: 18, name: "Drama", itemId: "6350c4663aebc3d9ab68b239" }, { id: 10751, name: "Family", itemId: "6350c466b8b704cc5e00bba4" }, { id: 14, name: "Fantasy", itemId: "6350c467716c1349cd979ea5" }, { id: 36, name: "History", itemId: "6350c466e48b907aae3dc343" }, { id: 27, name: "Horror", itemId: "6350c4661f96af44035b5d8b" }, { id: 10402, name: "Music", itemId: "6350c466cc865494f30b2562" }, { id: 9648, name: "Mystery", itemId: "6350c4662be0acf3c39fcfe1" }, { id: 10749, name: "Romance", itemId: "6350c466e3858126836a733c" }, { id: 878, name: "Science Fiction", itemId: "6350c466227c9381e733fc5f" }, { id: 10770, name: "TV Movie", itemId: "6350c466c1cf132f292a862e" }, { id: 53, name: "Thriller", itemId: "6350c466968264447a173718" }, { id: 10752, name: "War", itemId: "6350c466227c9320d433fc60" }, { id: 37, name: "Western", itemId: "6350c46672526e68ea608e10" }, ]; // max page count of the movies api const MAX_PAGE_COUNT = 401; // create connection to the Webflow api using our Webflow api token const webflowApi = new Webflow({ token: process.env.WF_API_KEY }); // set up axios to make requets to the movies api const movieApi = axios.create({ baseURL: "https://api.themoviedb.org/3/", params: { api_key: process.env.MOVIE_API_KEY, }, }); // some constants for images related to the movies api const MOVIE_IMAGE_PATH = "https://image.tmdb.org/t/p/w300"; const MOVIE_BACKDROP_PATH = "https://image.tmdb.org/t/p/original"; // instantiate the function to retireve movies fetchMovies(); // function definition for getting movies async function fetchMovies() { let activePage = 1; // iterate activePage until we reach the max page count set — 401 // using the Bottleneck task scheduler, make api call to Webflow to create a collection item for each movie retrieved while (activePage < MAX_PAGE_COUNT) { // some options we're passing to the movies endpoint const options = { params: { page: activePage } }; // the movies endpoint we're hitting const { data } = await movieApi.get("discover/movie", options); // data.results contains an array of objects where each object is a movie data.results.forEach( async (movie) => await limiter.schedule(() => createMovie(movie)) ); activePage++; } } // function to call the Webflow api to create each movie async function createMovie(movie) { if (!movie.poster_path || !movie.backdrop_path) return; // find the trailer for the movie, if no trailer, don't add movie to Webflow const trailer = await findTrailer(movie); if (!trailer || !trailer.key) return; movie.trailerKey = trailer.key; let genres = []; // add the relevant Webflow genre collection id for our multi reference field movie.genre_ids.forEach((genreId) => { const matchedGenre = allMovieGenres.find((genre) => genre.id === genreId); if (matchedGenre) genres.push(matchedGenre.itemId); }); // make the call to Webflow to add the movie return webflowApi .createItem({ collectionId: "6353176f2cf2501b7755dae3", fields: { name: movie.title, "movie-id": movie.id, genres, "movie-backdrop-poster": MOVIE_BACKDROP_PATH + movie.backdrop_path, "movie-poster": MOVIE_IMAGE_PATH + movie.poster_path, "release-date": movie.release_date, "release-year": getYear(parseISO(movie.release_date), 1), overview: movie.overview, "vote-average": movie.vote_average, "vote-count": movie.vote_count, popularity: movie.popularity, trailer: "https://www.youtube.com/watch?v=" + movie.trailerKey, _archived: false, _draft: false, }, }) .then((res) => console.log(res.name)) .catch((err) => console.log(err)); } // function to check if a movie has a trailer async function findTrailer(movie) { const trailerOptions = { params: { append_to_response: "videos" } }; const { data } = await movieApi.get(`movie/${movie.id}`, trailerOptions); return await data.videos.results.find((vid) => vid.type === "Trailer"); } // a function we ran separately to initially add the movie genres to Webflow async function fetchGenres() { const { data } = await movieApi.get("/genre/movie/list"); data.genres.forEach((genre) => { webflowApi.createItem({ collectionId: "6348398efba7fae203374c15", fields: { name: genre.name, _archived: false, _draft: true, }, }); }); }