Working with the CMS
What We'll Build
In this guide, you'll create a Data Client App that interacts with Webflow's CMS APIs. The frontend, built with React, will display data fetched from the Webflow CMS. The backend, powered by Express, will handle authentication and API calls.
Prerequisites
- A Webflow site. If you’re not sure where to start, clone our Astral Fund site with defined CMS collections.
- A Webflow App or a Site Token with the following scopes:
sites:read
,cms:read
, andcms:write
- An Ngrok account and an authentication token
- Node.js and an IDE of your choice.
- Additionally, you should have basic knowledge of Node.js and Express.
- Clone the starter code.
Run the following commands in your IDE to clone the example repository and install dependencies:# Clone the example repository git clone https://github.com/Webflow-Examples/cms-examples.git cd webflow-example # Install dependencies npm install
- Add Environment Variables
Add your credentials to the.env
file. If you’re using an App, input your App’sCLIENT_ID
andCLIENT_SECRET
. If using a Site Token, input the token asSITE_TOKEN
. - Add Ngrok Auth Token
Ngrok is required because Webflow Apps must run onhttps://
URLs, and Ngrok provides a secure tunnel for your local server.
Get your Ngrok auth token from the Ngrok dashboard. Then, add your token to your environment variables.export NGROK_AUTH_TOKEN=your-ngrok-auth-token
- Review Project
In this example, our focus will be on thebackend
folder. Within this folder, you'll find:server.js
: The main server file that sets up and runs our Express server.webflowClientMiddleware.js
: Contains the middleware for initializing the Webflow client.- routes: Includes various routes for interacting with the Webflow CMS.
- controllers: Defines the logic for API calls.
Quickstart 🚀
Want to see the demo in action? Run the following command to start your server and follow the instructions in the terminal.
npm start
👇 Keep going to dive deeper into the specifics of how to interact with the Webflow CMS API, set up middleware, and define routes and controllers.
To authenticate all of our API calls to Webflow, we'll need to initialize and authenticate the WebflowClient
provided by the Webflow JavaScript SDK. To efficiently manage this for multiple requests, we’ll create middleware for our Express server, which will handle initialization of the WebflowClient
and authentication in each request.
Authentication
We’ve already taken care of the OAuth Handshake in
authRoutes.js
. To learn more about authorization and authentication for your app, read the guide.
- Define Imports
First, we'll import the necessary modules. TheWebflowClient
will be imported from the Webflow SDK, and thegetToken
function will be imported from ourtokens.js
utility. ThegetToken
function handles retrieving an authentication token from our database.Add the following import statements at the top of yourwebflowClientMiddleware.js
file: - Build Middleware Function
Next, we'll create the middleware function. This function will be an asynchronous function that retrieves the access token, initializes the Webflow client, and attaches it to the request object. It will also handle errors and send appropriate responses if something goes wrong.- Retrieve Access Token
Within the middleware function, the first step is to retrieve the access token for the user. This is done using thegetToken
function. If the token is not found, an error is logged, and a401
Unauthorized response is sent. - Initialize Webflow Client
Once we have the access token, we'll initialize the Webflow client using theWebflowClient
constructor and attach it to thereq
object. This ensures that the client is available for subsequent middleware and route handlers. - Export the function
Finally, we'll export the middleware function so it can be used in other parts of the application.
- Retrieve Access Token
import WebflowClient from 'webflow-api';
import getToken from './tokens.js';
import WebflowClient from 'webflow-api';
import getToken from './tokens.js';
/ Middleware function to initialize and authenticate WebflowClient
const webflowClientMiddleware = async (req, res), next => {
try {
// Retrieve Access Token
const accessToken = await getToken('user');
if (!accessToken) {
console.log('Access token not found for user');
return res.status(401).send('Access token not found');
}
// Initialize Webflow Client
req.webflow = new WebflowClient({ accessToken });
// console.log('Webflow client initialized');
next();
} catch (error) {
console.error('Error initializing Webflow client:', error);
res.status(500).send('Failed to initialize Webflow client');
}
};
export default webflowClientMiddleware;
Let’s review how controllers and routes are structured in the project. Routes define the endpoints for your application, while controllers contain the logic for handling requests and responses.
Controllers
Controllers contain the business logic for handling requests and responses. Each controller method is designed to handle a specific type of request, such as listing sites, creating a new collection, or deleting an item.
Example Controller:
controllers/sitesController.js
Here’s the listSites
controller method which is used to
fetch and return the list of sites:
export const listSites = async (req, res) => {
// Fetch sites
const sites = await req.webflow.sites.list();
// Respond with the list of sites
res.json({ sites });
} catch (error) {
console.error('Error fetching sites:', error);
res.status(500).send('Failed to fetch sites');
}
};
Routes
Routes are responsible for defining the API endpoints and linking them to the appropriate controller methods. Each route file is dedicated to a specific resource or group of related functionalities. In this example, there are routes for handling authentication, sites, collections, and items data.
Example Route: routes/sitesRoutes.js
Here’s a quick look at how routes are set up in the project:
import express from 'express';
import { listSites } from '../controllers/sitesController.js'; // Import the listSites controller
const router = express.Router();
router.get('/', listSites); // Define the route for listing sites and link it to the listSites controller
export default router;
In this section, we'll set up routes and controllers for managing Collections in the Webflow CMS. This includes listing Collections, getting Collection details, creating Collections with fields, and deleting Collections.
Collections and Fields
When creating a collection, you’ll need to define required details such as displayName
, singularName
, and slug
. Additionally, all collections have the following default, required fields: name
and slug
.
Collection Object
{
"id": "6487b589d27f8112c8623bcb",
"displayName": "test2",
"singularName": "testing123",
"slug": "test2",
"createdOn": "2023-06-13T00:17:13.320Z",
"lastUpdated": "2023-06-13T00:17:13.748Z",
"fields": [
{
"id": "b24f574b7ab550df74090de0f850c81d",
"isEditable": true,
"isRequired": true,
"type": "PlainText",
"slug": "name",
"displayName": "Name",
"helpText": null
},
{
"id": "08abacfa7f860006c7785fb6b725b9f5",
"isEditable": true,
"isRequired": true,
"type": "PlainText",
"slug": "slug",
"displayName": "Slug",
"helpText": null
}
]
}
In addition to a Field’s required type
, displayName
, and slug
properties, you can also add helpText
and determine whether a field isRequired
for an item.
Field Object
{
"id": "b24f574b7ab550df74090de0f850c81d",
"isEditable": true,
"isRequired": true,
"type": "PlainText",
"slug": "name",
"displayName": "Name",
"helpText": null
}
Creating Custom Fields
Once a collection is created, you can create additional fields at any time. Learn more about field types and accepted item values in the Webflow Field Types Documentation.
Unsupported Field Types
Note: At this time, Webflow does not support the creation of the following field types through the API:
These field types can be added to a collection at any time via the Webflow designer.
- Setup Collection Controllers
Let's define the logic for handling the collection routes in the
collectionController.js
file. In this example, we’re setting up ourcreateCollection
logic to accept a preset collection with accompanying fields defined in./utils/CollectionPresets.js
. We’ll provide an example request later on in the guide.Create the file in your
controllers
folder and add the following code. We’ll export each function so they can be used in our routes. - Setup Collection Routes
Now that we have the controllers, let's define the routes that will use these controllers. Create a file named
collectionRoutes.js
in your routes folder and add the following code:
export const listCollections = async (req, res) => {
// Fetch collections
const collections = await req.webflow.collections.list(req.params.siteId);
// Respond with the list of collections
res.json({ collections });
} catch (error) {
console.error('Error fetching collections:', error);
res.status(500).send('Failed to fetch collections');
}
};
export const getCollection = async (req, res) => {
try {
// Fetch collection details
const collection = await req.webflow.collections.get(req.params.collectionId);
// Respond with the collection details
res.json(collection);
} catch (error) {
console.error('Error fetching collection:', error);
res.status(500).send('Failed to fetch collection');
}
};
import express from 'express';
import {
listCollections,
getCollection,
createCollectionWithFields,
deleteCollection
} from '../controllers/collectionController.js'; // Import the controllers we just setup
const router = express.Router();
// Route to list all collections for a specific site. See listCollections controller for logic.
router.get("/:siteId", listCollections);
// Route to get details of a specific collection. See getCollection controller for logic.
router.get("/:collectionId/details", getCollection);
// Route to create a new collection with fields. See createCollectionsWithFields controller for logic.
router.post("/:siteId", createCollectionWithFields);
// Route to delete a specific collection. See deleteCollection controller for logic.
router.delete("/:collectionId", deleteCollection);
export default router;
In this section, we'll set up endpoints for managing items within a collection in the Webflow CMS. This includes fetching items, adding and updating items, and managing an item’s publish state.
Understanding Items
Items are individual entries within a collection. This section provides detailed information on how to manage these items, including creating, updating, retrieving, and deleting items. You'll also learn about the different states items can be in and how to work with various field types.
Required Properties
- Archive: Pulls collection items from the site but keeps them accessible from the CMS. Default value is
false
. - Draft: Saves the collection item as a draft. The item will not go live until you change its status, even if the entire site is published. Default value is
false
.
Live vs. Staging
Items can be added either as a staged item or directly to the live site. The endpoints for these actions differ, and understanding when to use each is crucial for effective content management. For more detailed information on site publishing, visit Webflow University.
- Staging Endpoints: Use staging endpoints to manage items within a collection without immediately publishing them. Items added or modified through staging endpoints will be published to the live site the next time the entire site is published. Staging read endpoints return both staged and live items, while write actions are saved as staged changes.
- Live Endpoint: Use endpoints ending in
/live
to manage items directly on the live site.
Query Parameters
When retrieving items from a collection, you can use various query parameters to filter, sort, and paginate the results. Here are the key query parameters you can use:
Pagination:
offset
: Use to paginate through a longer list of results.limit
: Use to limit the number of items returned.
cmsLocaleId: Retrieve localized collection items by providing a comma-separated string of multiple locales. Refer to the localization guide for more information.
FilteringBeta
name
: Filter by the exact name of the item.slug
: Filter by the exact slug of the item.lastPublished
: Uselte
(less than or equal) andgte
(greater than or equal) to filter by publication date.
SortingBeta
sortBy
- name
- slug
- lastPublished
sortOrder
:asc
: Ascending orderdesc
: Descending order
- Setup Item Controllers
First, let's define the logic for handling item operations in the
itemsController.js
file. Create the file in yourcontrollers
folder and add the following code. We’ll export each function so they can be used in our routes. - Setup Item Routes
Next, define the routes that will use these controllers. Create a file named
itemsRoutes.js
in yourroutes
folder and add the following code:
// List collection items
export const listItems = async (req, res) => {
try {
const data = await req.webflow.collections.items.listItems(req.params.collectionId);
res.json(data.items); // Respond with collection items
} catch (error) {
console.error("Error fetching collection items:", error);
res.status(500).send("Failed to fetch collection items");
}
};
// Create collection item
export const createItem = async (req, res) => {
try {
const data = await req.webflow.collections.items.createItem(req.params.collectionId, req.body);
res.json(data); // Respond with created item
} catch (error) {
console.error("Error creating collection item:", error);
res.status(500).send("Failed to create collection item");
}
};
// Delete collection item
export const deleteItem = async (req, res) => {
try {
const data = await req.webflow.collections.items.deleteItem(req.params.collectionId, req.params.itemId);
res.json(data); // Respond with deletion result
} catch (error) {
console.error("Error deleting item:", error);
res.status(500).send("Failed to delete item");
}
};
import express from 'express';
import {
listItems,
createItem,
deleteItem
} from '../controllers/itemsController.js'; // Import the controllers we just setup
const router = express.Router();
// Route to list all items for a specific collection. See listItems controller for logic.
router.get("/:collectionId/items", listItems);
// Route to create a new item in a specific collection. See createItem controller for logic.
router.post("/:collectionId/items", createItem);
// Route to delete a specific item from a collection. See deleteItem controller for logic.
router.delete("/:collectionId/items/:itemId", deleteItem);
export default router;
Let's set up the server to handle requests, configure middleware, define routes for interacting with the Webflow API, and tunnel through Ngrok for secure external access.
-
Import Required Modules
Start by importing the necessary modules and middleware:
import express from "express"; import cors from "cors"; import chalk from "chalk"; // Library for styling terminal output import Table from "cli-table3"; // Library for styling terminal output import { startNgrok } from "./utils/ngrokManager.js"; // Logic for handling Ngrok // Import Webflow Client Middleware import webflowClientMiddleware from "./webflowClientMiddleware.js"; // Import Routes import authRoutes from "./routes/authRoutes.js"; import sitesRoutes from "./routes/sitesRoutes.js"; import collectionsRoutes from "./routes/collectionRoutes.js"; import itemRoutes from "./routes/itemRoutes.js"; -
Initialize Express Application
Create an instance of the Express application and define the port:
const app = express(); const PORT = process.env.PORT || 8000; -
Configure Middleware
Setup middleware for CORS, which will allow our backend to accept requests from our frontend running on a different port. Additionally, add middleware for handling JSON data sent in a request body, making it available as
req.body
in your request handlers.app.use( cors({ origin: "http://localhost:3000", // Allow only this origin to access the resources optionsSuccessStatus: 200, // For legacy browser support }) ); app.use(express.json()); -
Define Routes
Configure the routes for authentication and accessing sites, collections, and items. Use the Webflow Client middleware for routes that need API interaction:
app.use("/", authRoutes); app.use("/api/sites", webflowClientMiddleware, sitesRoutes); app.use("/api/collections", webflowClientMiddleware, collectionsRoutes); app.use("/api/collections", webflowClientMiddleware, itemRoutes); -
Start the Server with Ngrok
Create a function to start the server and tunnel through Ngrok for external access:
// Start server with NGROK const startServer = async () => { try { // Start Ngrok const ngrokUrl = await startNgrok(PORT); // Create a table to output in the CLI const table = Table({ head: ["Location", "URL"], // Define column headers colWidths: [30, 60], // Define column widths }); // Add URL information to the table table.push( ["Development URL (Frontend)", "http://localhost:3000"], ["Development URL (Backend)", `http://localhost:${PORT}`] ); // If using an App, also add the Redirect URI to the table if (!process.env.SITE_TOKEN) { table.push(["Auth Callback URL", `${ngrokUrl}/auth/callback`]); } // Console log the table console.log(table.toString()); // If using an App, send a note to adjust the app's Redirect URI if (!process.env.SITE_TOKEN) { console.log( chalk.blue.inverse("\n\nNOTE:"), chalk.blue("Update your Redirect URI in your App Settings\n\n") ); } // Start the server app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); }); } catch (error) { console.error("Failed to start the server with ngrok:", error); process.exit(1); } }; // Start the server startServer();
Now that we’ve set up the server, let’s start making requests through our frontend.
-
Start the server
First, ensure your server is running. Use the following command to start both the frontend and backend servers, with the backend being tunneled through Ngrok:npm start
-
Authenticate the App
- Get the Ngrok redirect URI from the terminal.
- Update your app’s redirect URI in the Webflow app settings with this Ngrok redirect URI.
- Navigate to http://localhost:8000.
- You’ll be taken to a Webflow authentication screen. Add the workspaces/sites you want to authorize.
- After authorizing, you’ll be redirected to the frontend.
If you’re using a Site Token, you’ll be redirected directly to the frontend.
-
Explore the CMS
- Choose a Site
From the dropdown list, select one of the authorized sites you want to explore. - Choose a Collection
- Select a collection from the dropdown list to populate the item list.
- (Optional) You can delete a collection if you wish.
- Create an Item
- Fill out the form and submit it to create a new item.
- The new item will appear in the item list.
- Delete an Item
- Scroll through the item list to find the delete button.
- Delete an item and observe the changes.
- Create a Collection
- In the modal, choose from a list of collection presets and create a collection.
- Return to the collection picker to see the new collection appear.
- (Optional) Add new items to the newly created collection.
- Choose a Site
Conclusion
Congratulations! You've successfully navigated through the process of setting up and using the Webflow API with a fully functional backend and frontend application. Here's a quick recap of what you've accomplished:
- Webflow Client and Backend Configuration: You configured the WebflowClient, set up middleware, and created routes and controllers for managing collections and items.
- Working with Collections: You learned how to create, retrieve, and delete collections, including handling different field types.
- Working with Items: You explored how to create, retrieve, update, and delete items within a collection, managing various item states and field types.
Next Steps
- Extend Functionality: Enhance your application by adding new endpoints - try updating an item - or incorporating additional data processing logic.
- Explore Localization: Managing content localization is a crucial part of working with the Webflow CMS. Check out our localization guide for more details on how to localize your content effectively.
Updated 4 months ago