How to build a CMS using JavaScript with Contentful
![Illustrated image showing how rich text is rendered with JS](https://images.ctfassets.net/fo9twyrwpveg/3NVGSh9bruaUncfcSLbQ6P/6b8fac886b9dfb66aed646ee0cfcc767/cBdtvZGg.png?fm=jpg&q=90&w=1536)
A content management system (CMS) enables a website’s editor to manage content without technical expertise. A CMS also provides a quick glimpse into the status of your content — whether it’s live, unpublished, or in the review process.Â
Although their original scope was limited to web page content, CMSes are now used for all sorts of business operations.Â
The Web 2.0 era saw the establishment of the monolithic, linked (all-in-one) CMS that included everything needed for organizing and publishing web content. However, this solution was designed to deliver web content to desktops, and the subsequent proliferation of mobile devices and IoT devices challenged its efficacy.
Then, the decoupled and headless CMS arose. This new model allowed developers to work with the frontend of the website without affecting the site’s content.
Now that CMS tools are integral to website management, some CMS providers offer API capabilities, enabling developers to create custom CMS platforms. One such example is the Contentful Content Management API, which you’ll explore in this tutorial.Â
The following tutorial demonstrates how to create a CMS with a backend that enables you to generate, manage, save, and publish information using the Content Management API. This API empowers developers to rapidly design high-quality user experiences without worrying about how changes might affect the application’s backend business logic.
Can you build a CMS with JavaScript?
Most certainly! In this tutorial, you'll learn how to create a JavaScript CMS app that connects with Contentful’s Content Management API. Prerequisites for following along:
A fundamental understanding of apps built with Node.js and Express.js.Â
A Created Contentful space and an API key to store and retrieve content from your space.
A personal authentication token.
Access to Postman for testing.
You can find the full project code on GitHub.
Getting startedÂ
To begin, follow these steps to install and set up a new project.
First, create a new directory for your project and navigate to it.
Now, open a terminal in that directory and run the command below.
This will walk you through the steps to adding a package.json
file to your directory.
Now, you’ll add some additional packages for this project.Â
Run the following commands in your terminal to install Express.js, and Nodemon, which monitors file changes.
This tutorial will also use other npm packages: dotenv
for storing the token, axios
for making HTTP requests to the API, and content-management
to use Contentful’s built-in functions. Later on, you’ll use Postman to test the API endpoints.Â
To install the modules, use the following command:
After the packages are installed, create a new file, index.js
, in the root directory. This will act as the main file in your Node application.
Before you write any code inside your index.js
file, you’ll have to set up Nodemon to point to the index.js
file.Â
Open the package.json
file and add the following line under the scripts section:
Save the file.
Next, to store environment variables, create a new file called .env in the root folder to store the CMA and CDA tokens. In that file, create two variables and replace the placeholder text with your tokens, which you can retrieve by following this guide:
Now, you can create an express server in the index.js
file with the necessary routes, using the code below:
Once you’ve added the boilerplate code, save the index.js
file.
Now, create a new folder, /routes
in your project directory, and create a new file called cmsroutes.js
in the routes folder.
Then, add the following code in the cmsroutes.js
file:
Now, the basic application is ready for the next steps. Open a terminal in your project directory and run the following command to start the Node server:
(Note: Because you have yet to add routes, it’s not advisable to test the server response in-browser, as it will return an error.)
Building a JavaScript app that authenticates into your Contentful account
In this and subsequent sections, you’ll need your Space ID. You can locate your Space ID while creating the token or you by following this guide.Â
Now, open the routes directory. Then, inside the cmsroutes.js
file, add the following code to make a new route:
In this code, you created a route to authenticate a user who is logging in to a specific space in their Contentful account. The authentication is performed using the CDA token, which you passed in the headers, along with your request to the Contentful server. Based on the response from Contentful, the user is notified if they’re authorized to log in.
Now that you’ve created the route and method, it’s time to test the API in Postman, a popular API testing tool. Once you’ve downloaded Postman, you can add URLs, types of requests, and headers to carry out API testing.Â
Below is an example of Postman in action.Â
![Postman](https://images.ctfassets.net/fo9twyrwpveg/134SU8H9LWSd3FHCAfGgMu/2a19413929a644aa1b9e580334ec747c/unnamed__13_.png)
Note that the string after authUser is the space_id
. To create this request in Postman, you can add the following in the URL bar:Â
The successful API response returns the string: You are authorized to log in!!
Adding content types to your space
Before continuing, it is worth noting that when you call the Contentful API to create or update the content type, you receive the version of the particular type in the response. So, to validate subsequent requests, you need to pass that version through the request body.
Now, you can add a content type to your space. Start by creating a new route in routes/cmsroutes.js
:
In the above code, :space_id
passes the ID of the newly created space, :environment_id
passes the ID of your environment (master), and :content_type_id
is the user-defined name of your content type. Â
Note that you’re passing two parameters in your header:
Authorization: `Bearer ${process.env.CDA_ACCESS_TOKEN}`Â
"content-type": "application/vnd.contentful.management.v1+json"
Now that the route is created, test it in Postman using the URL:
Ensure that the type of request is set to PUT and the request body type is JSON.
![Content Type](https://images.ctfassets.net/fo9twyrwpveg/5z2a5CrMCsMS5knE2x2q0e/e23b9d9a5943d83d969de12fb5335eae/unnamed__14_.png)
The request body is:
In response, you received:Â
![Body](https://images.ctfassets.net/fo9twyrwpveg/v9Rch6OSo38tuBvQemwBM/b2fa9361592ac01dfc26840b8a4724d1/unnamed__15_.png)
The Contentful portal will create the content type like this:
![Space home](https://images.ctfassets.net/fo9twyrwpveg/T0dPydCeCMLgNyyYbpuOv/6448c9cfbcb2ed48212a9a96b814814e/unnamed__16_.png)
Adding several fields to your content type
After creating your content type, you can add more fields to it, like a title and body.
To do this, create a new route in routes/cmsroutes.js
by adding the following code:
Here, you’re passing :space_id
, :environment_id
, and :content_type_id. You’re also passing three parameters in your header:
Authorization: `Bearer ${process.env.CMA_ACCESS_TOKEN}`
"content-type": "application/vnd.contentful.management.v1+json"
"X-Contentful-Version":content_version
which will come from request body
The third parameter, X-Contentful-Version
, tracks the version of the created content type. Each time it updates, the version number increases.
![PublishedCounter](https://images.ctfassets.net/fo9twyrwpveg/2X6GQu0hBMbvBFTKDMOOaP/485d41e4e95a1ba47cf8f53e628889a5/unnamed__17_.png)
Now, to test the route in Postman, add the following URL:
Again, ensure the type of request is set to PUT.
The request body should be as follows:
![Food Blog](https://images.ctfassets.net/fo9twyrwpveg/7GKvqgXh81Cdrm6o29SH5s/e70cbfb7604d51fd1f2ed909df1d0fa9/unnamed__18_.png)
Then, Postman provides this response:
![Postman](https://images.ctfassets.net/fo9twyrwpveg/5GKIKMdBG8qvKGFEl44oor/31739f6954cd5489ce8c5c06a75caed9/unnamed__19_.png)
![PublishedCounter](https://images.ctfassets.net/fo9twyrwpveg/7pHK3H5zGI8VsGYVM8bPBx/aabf0289827374fade5c74f582dc4add/unnamed__20_.png)
Your content model should look similar to this:
![Food Blog](https://images.ctfassets.net/fo9twyrwpveg/2Of4ph2wQWW0IBKYZjJ1yf/3ee1df669b9e32fe2a2e708eec48325f/unnamed__21_.png)
Validations
When making or changing a content type, defining the validations of a field enables you to prevent errors.
You can use the same route that was used to add fields to the content type. However, when passing the JSON object in the body of your request, you’ll also add validations in addition to the fields using the following code:
![FoodBlogContent](https://images.ctfassets.net/fo9twyrwpveg/6x7e4YeVXcK3hMPOVrrofZ/7995246903930cf3d9cc87d05d8bd0ce/unnamed__22_.png)
A successful response looks like this:
![Body](https://images.ctfassets.net/fo9twyrwpveg/2RSHoU1mKCjirfH21UuZAp/bfb5c4509661975b1afdf841f9e05e68/unnamed__23_.png)
![False](https://images.ctfassets.net/fo9twyrwpveg/7BbFaxDAzrToYuKDhSJvzK/bc234749f7956cb33516a449f9f6a0d9/unnamed__24_.png)
![False](https://images.ctfassets.net/fo9twyrwpveg/24xbYqyPX6uU2fs9EyiFHe/e85dc9eb4364cf0972f2a50c529e72f1/unnamed__25_.png)
Then, the Contentful UI shows the validation.
![Post Title](https://images.ctfassets.net/fo9twyrwpveg/4CAB2ishOjh7jZMSRzgPyR/9a25b8702135951e8578aee1000be74e/unnamed__26_.png)
Activating a content type
You can change the status of the content type from Draft to Active. To activate a content type, first, add a new route in your routes/cmsroutes.js
file. Then, add the following code:
In the code above, you used a built-in library from Contentful and initialized it by adding contentful.createClient
to your cmsroutes.js
file.Â
Parameters like :space_id
, :environment_id
, and :content_type_id
are also passed, similar to other routes. Contentful’s library takes care of everything else.
Now, you can test the route to activate the content type in Postman using the following URL:
![PUT](https://images.ctfassets.net/fo9twyrwpveg/43ARDQ5Pq49PmCIo3CVOGg/13148398c18ad900e22917e371c8ba3c/unnamed__30_.png)
In the Postman response body, you’ll see that it’s been activated! The Contentful CMS portal will set the content type as ACTIVE.
![Content model](https://images.ctfassets.net/fo9twyrwpveg/2ZC7FoGRfyhJ9fEbxEzo3K/180dfc899d0589850872a2d0b624c606/unnamed__27_.png)
Retrieving the editor interface for the content type
An editing interface represents a content type’s appearance and behavior in the Contentful web app. Each category of content type can only have a single editing interface. The API will return a 404 error if a content type hasn’t been activated yet.
To retrieve the interface, first set up a new route in routes/cmsroutes.js
file by adding the following:
Similar to other routes, you’ve passed two arguments in your route: :space_id
and :environment_id
.Â
Additionally, the header contains your CMA (Contentful Management API token):
By testing the route in Postman using the URL 127.0.0.1:8000/editorInterfaces/{space_id}/{environment_id}
and setting the request type as GET
, you receive the following response:
![GET](https://images.ctfassets.net/fo9twyrwpveg/6U4slRkcBlc0hKAqGdJoGd/05fa67257f24838a045d3aec9d6624ee/unnamed__28_.png)
![GET](https://images.ctfassets.net/fo9twyrwpveg/eBqtAvcpfnTjA8hCOVHPN/fdcf0ff552a46efd98dfd3b22414008b/unnamed__29_.png)
Updating the editor interface for the content type
You can add new properties to a content type field, amend existing ones, or remove them entirely by using an editor interface.Â
To begin making changes, add the following code in the routes/cmsroutes.js
file:
You’ve passed three parameters in your route:space_id
, environment_id
, and content_type_id
.Â
Now, just like adding fields in content_type
, you’ll pass three parameters in your request header:
Authorization: `Bearer ${process.env.CMA_ACCESS_TOKEN}`
"content-type": "application/vnd.contentful.management.v1+json"
"X-Contentful-Version":`${req.body.content_version}`
This is a PUT
request:
![PUT](https://images.ctfassets.net/fo9twyrwpveg/7R6juk94MAm5jRIQYommM/f208be66e7871fc0073bf37c824d7d00/unnamed__30_.png)
Then, add the following code snippet for the body:
By default, the title field is set as: Markdown.
![Food Blog Post Title](https://images.ctfassets.net/fo9twyrwpveg/EzHOKaOJHoHiiZ9HeVtEZ/673f6a9393f54c67a0db7ff78871bcc8/unnamed__31_.png)
After executing the route, the response in Postman looks as follows:
![Multiple Line](https://images.ctfassets.net/fo9twyrwpveg/4ybikIidusjaqsc5zXInx/80408e80ca0f3c6576678695ecd7b65a/unnamed__32_.png)
![Multiple Line](https://images.ctfassets.net/fo9twyrwpveg/4eJLkSOD6UiQpSMNe4kmUB/8288fef43e58a308160c0f0e31a8b92a/unnamed__33_.png)
Now, the title field appearance is set to: Multiple line.
![Food Blog Post Title](https://images.ctfassets.net/fo9twyrwpveg/2T2r8qEs4HpIDW0BNDNmvr/d7266b4aaa1be04e72e92b41e8657831/unnamed__34_.png)
Content Delivery
Now, you can retrieve all the spaces created in your organization. You’ll do this using the Content Delivery API (CDA), which is mainly used to retrieve your content. It works just like the CMA.Â
To do so, first create a new route in routes/cmsroutes.js
using the code below:
The parameters you passed are similar to previous routes, but this time, you passed the CDA token instead of the CMA token in your route’s URL.Â
By executing the route in Postman with this URL:
you should receive a response as shown in the images below:
![Food](https://images.ctfassets.net/fo9twyrwpveg/3X0aYcrpE3uFURh6GK50Dh/d175227cd4017fbe129b42422100e4fe/unnamed__35_.png)
![Food](https://images.ctfassets.net/fo9twyrwpveg/TqHM4RvCms7k52BiMQuhQ/eec12bcbb99e20b5aa56d8bebe0b3310/unnamed__36_.png)
As you can see, the Postman response is identical to using the CDA token.Â
Wrapping up
You’ve seen how easy it is to create a JavaScript CMS app that connects with Contentful’s Content Management API. But you’ve barely scratched the surface. Contentful offers a wide range of APIs and SDKs.
Are you interested in building a related project using Contentful? Create a free Contentful account and get started in moments, no credit card required.