{"id":31315,"date":"2023-09-29T07:00:00","date_gmt":"2023-09-29T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=31315"},"modified":"2025-11-26T19:01:01","modified_gmt":"2025-11-27T03:01:01","slug":"build-full-stack-content-management-system-next-js-xata-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary","title":{"rendered":"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary"},"content":{"rendered":"\n<p>Managing a website&#8217;s content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded JSON file. But, when creating large-scale projects, managing files and databases becomes complex. In these cases, using a&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Content_management_system\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">content management system<\/a>&nbsp;(CMS) is one of the simple and efficient methods.<\/p>\n\n\n\n<p>A CMS is software used to create and manage content with a team. There are many third-party CMS providers, such as Sanity and Strapi. However, someone once said, &#8220;If you want something done right, do it yourself.&#8221; In this article, I&#8217;ll teach you how to create your own CMS using the latest web development technologies. You&#8217;ll learn to use a database and Cloudinary to manage your web app&#8217;s content and digital assets.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--x47IBUF0--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/7q2d0h6liftbr4tn313b.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211606\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-1.jpg\" alt=\"final demo of the application - ecommerce cms\"\/><\/a><\/figure><\/div>\n\n\n<p>Here\u2019s the&nbsp;<a href=\"https:\/\/github.com\/giridhar7632\/jamstackhack-22\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">source code<\/a>&nbsp;and the&nbsp;<a href=\"https:\/\/jamstackhack-22.netlify.app\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">final demo<\/a>&nbsp;if you want to dive directly into the code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#prerequisites\"><\/a>Prerequisites<\/h2>\n\n\n\n<p>Before diving into this tutorial, you should:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Have a solid understanding of HTML and CSS.<\/li>\n\n\n\n<li>Know JavaScript&#8217;s&nbsp;<a href=\"https:\/\/www.w3schools.com\/Js\/js_es6.asp\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">ES6 syntax<\/a>.<\/li>\n\n\n\n<li>Understand React and how it works.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#what-you-will-build\"><\/a>What You&#8217;ll Build<\/h2>\n\n\n\n<p>You&#8217;ll build a CMS for managing the content of an e-ommerce website. There are various&nbsp;<a href=\"https:\/\/www.oracle.com\/content-management\/what-is-cms\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">types of content management systems<\/a>. In this tutorial, we&#8217;ll create a headless CMS with a back-end system connecting to a database for managing content using a web interface. You&#8217;ll expose the content for the front-end via API endpoints and use the data however you like.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#technologies-you-will-use\"><\/a>Technologies You&#8217;ll Use<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/nextjs.org\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Next.js<\/a>: A JavaScript framework for building FullStack Jamstack applications. It extends the capabilities of React for writing serverless code.<\/li>\n\n\n\n<li><a href=\"https:\/\/tailwindcss.com\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Tailwind CSS<\/a>: A utility-first CSS framework that uses classes to style the markup.<\/li>\n\n\n\n<li><a href=\"https:\/\/xata.io\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Xata<\/a>: A serverless database that lets you create Jamstack applications without worrying about deployment or scaling issues.<\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Cloudinary<\/a>: A platform for managing media assets, such as images.<\/li>\n<\/ul>\n\n\n\n<p>You&#8217;ll need&nbsp;<a href=\"https:\/\/cloudinary.com\/users\/register_free\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Cloudinary<\/a>,&nbsp;<a href=\"https:\/\/app.xata.io\/signin\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Xata<\/a>, and&nbsp;<a href=\"https:\/\/app.netlify.com\/signup\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">Netlify<\/a>&nbsp;accounts to follow along. These services provide a generous free tier you can use for this project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#getting-started\"><\/a>Getting Started<\/h2>\n\n\n\n<p>To help you out, I created a&nbsp;<a href=\"https:\/\/codesandbox.io\/p\/github\/giridhar7632\/ecommerce-cms-starter\/main\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">starter codesandbox<\/a>; fork it and start coding.<\/p>\n\n\n\n<p>Run the following command to start the local development environment.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">yarn create next-app my-ecommerce-cms -e https:<span class=\"hljs-comment\">\/\/github.com\/giridhar7632\/ecommerce-cms-starter<\/span>\n<span class=\"hljs-comment\"># or<\/span>\nnpx create-next-app my-ecommerce-cms -e https:<span class=\"hljs-comment\">\/\/github.com\/giridhar7632\/ecommerce-cms-starter<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The starter code includes all essential dependencies, such as Tailwind CSS, Xata, and others. It also contains several ready-made front-end components styled using Tailwind CSS.<\/p>\n\n\n\n<p>Navigate to the project directory and start your development server after installing the dependencies:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-wrap-lines\">cd my-ecommerce-app\n\nyarn dev<\/code><\/span><\/pre>\n\n\n<p>Now you&#8217;ll be able to see the app running at&nbsp;<a href=\"http:\/\/localhost:3000\/\">http:\/\/localhost:3000<\/a>. You can also see the preview inside your codesandbox.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--Y6dRdM8C--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/8c49m6jijrbf3kslzfk3.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211608\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-2.png\" alt=\"preview of the starter project\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#starter-code\"><\/a>Starter Code<\/h2>\n\n\n\n<p>I already created the UI of the application in the starter code to make the process simpler. The main things inside the project are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>The<\/strong>&nbsp;<code>**\/****pages**<\/code>&nbsp;<strong>directory<\/strong>. Next.js allows&nbsp;<a href=\"https:\/\/nextjs.org\/docs\/routing\/introduction\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">file system-based routing<\/a>. It considers anything inside this directory as a route and the files inside&nbsp;<code>\/pages\/api<\/code>&nbsp;as API endpoints.<\/li>\n\n\n\n<li><strong>The<\/strong>&nbsp;<code>**\/components**<\/code>&nbsp;<strong>directory<\/strong>.&nbsp;If you&#8217;re familiar with React, you may already know that everything is a reusable component. This directory contains components such as layouts, forms, and everything required. Take a look inside the folders to understand the structure<\/li>\n\n\n\n<li><strong>The<\/strong>&nbsp;<code>**\/pages\/products.js**<\/code>&nbsp;<strong>file<\/strong>. The route for displaying the products. You can find the components used here inside the&nbsp;<code>\/components\/Products<\/code>&nbsp;folder.<\/li>\n\n\n\n<li><strong>The<\/strong>&nbsp;<code>**\/pages\/product**<\/code>&nbsp;<strong>directory<\/strong>.&nbsp;You can create dynamic routes for generating static pages using Next.js. You can define the route using the bracket syntax (<code>[slug]<\/code>) to make it dynamic. You can find the file&nbsp;<code>[id].js<\/code>&nbsp;inside this folder. So, you can get the&nbsp;<code>id<\/code>&nbsp;as a parameter inside that route.<\/li>\n<\/ol>\n\n\n\n<p>Let&#8217;s configure the database and add API endpoints to make the UI interactive.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#connecting-to-database\"><\/a>Connecting to Database<\/h2>\n\n\n\n<p><a href=\"https:\/\/xata.io\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Xata<\/a>&nbsp;is the serverless database that you&#8217;ll use to develop this CMS. Using Xata, you can store all the data inside a fully configured database without worrying about deployment and scaling.<\/p>\n\n\n\n<p>Xata provides a CLI tool,&nbsp;<code>@xata.io\/cli<\/code>, for creating and managing databases directly from the terminal.<\/p>\n\n\n\n<p>Run the command below to install the Xata CLI globally and use it in your project.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">yarn<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-tag\">-g<\/span> <span class=\"hljs-keyword\">@xata<\/span>.io\/cli\n# <span class=\"hljs-keyword\">or<\/span>\nnpm i -g @xata.io\/cli\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After the installation, use the below command to generate an API key for Xata.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-wrap-lines\">xata auth login\n<\/code><\/span><\/pre>\n\n\n<p>Xata will prompt you to select from the following options:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--7hHYIq13--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/va0m806wxan84vvpc19s.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764212023\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-13.jpg\" alt=\"Xata CLI asking to create API key\"\/><\/a><\/figure>\n\n\n\n<p>For this project, select&nbsp;<strong>Create a new API key<\/strong>&nbsp;<strong>in the browser<\/strong>; give the API key a name when the browser window pops up.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--CxczCZru--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/v8bb4mqwt4xx3lloqcug.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211610\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-3.png\" alt=\"Creating a new Xata API key inside browser\"\/><\/a><\/figure>\n\n\n\n<p>Workspaces symbolize your team, and they&#8217;re the starting point of Xata where databases are created. You can create one once you log in to your account. The data within the&nbsp;<strong>database<\/strong>&nbsp;is organized in the form of&nbsp;<strong>tables<\/strong>, which have&nbsp;<strong>columns<\/strong>&nbsp;that form a strict schema in the database.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--zOpLrpOO--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/pvzp1x3do3b4yhpuhzpv.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211612\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-4.png\" alt=\"Preview of workspace and database in Xata\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#creating-the-table\"><\/a>Creating the Table<\/h2>\n\n\n\n<p>To add products to the database, begin by constructing a table and its schema for data structuring. The&nbsp;<strong>products<\/strong>&nbsp;table will have a few columns, such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>id<\/code>: an automated column maintained by Xata.<\/li>\n\n\n\n<li><code>name<\/code>: a string containing the product&#8217;s name.<\/li>\n\n\n\n<li><code>description<\/code>: a string with a description of the product.<\/li>\n\n\n\n<li><code>price<\/code>: a number containing the product value.<\/li>\n\n\n\n<li><code>stock<\/code>: the number of items remaining.<\/li>\n\n\n\n<li><code>thumbnail<\/code>: the image URL of the product.<\/li>\n\n\n\n<li><code>media<\/code>: multiple URLs to the media of the product.<\/li>\n<\/ul>\n\n\n\n<p>In Xata, you can construct a table using either the browser or your CLI. Now, let&#8217;s build the table with the CLI. The database schema for this course is already inside the&nbsp;<code>schema.json<\/code>&nbsp;file of the starter code.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/schema.json\n\n{\n  <span class=\"hljs-string\">\"formatVersion\"<\/span>: <span class=\"hljs-string\">\"1\"<\/span>,\n  <span class=\"hljs-string\">\"tables\"<\/span>: &#91;\n    {\n      <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"products\"<\/span>,\n      <span class=\"hljs-string\">\"columns\"<\/span>: &#91;\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"name\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"string\"<\/span>\n        },\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"description\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"text\"<\/span>\n        },\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"price\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"float\"<\/span>\n        },\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"stock\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"int\"<\/span>\n        },\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"thumbnail\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"string\"<\/span>\n        },\n        {\n          <span class=\"hljs-string\">\"name\"<\/span>: <span class=\"hljs-string\">\"media\"<\/span>,\n          <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"multiple\"<\/span>\n        }\n      ]\n    }\n  ]\n}\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, execute the following command to populate the database with the given schema.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-wrap-lines\">xata init --schema=schema.json --codegen=utils\/xata.js\n<\/code><\/span><\/pre>\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--STvywF0k--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/h3rfjrxrcjhnevvredr9.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211858\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-12.jpg\" alt=\"Xata database creation process in CLI\"\/><\/a><\/figure>\n\n\n\n<p>To create a new database, you must first answer several questions in the terminal. Then the CLI reads the schema from the file and creates a database. It also generates a&nbsp;<code>\/utils\/xata.js<\/code>&nbsp;file for using the Xata client. You&#8217;ll use this client to communicate with Xata.<\/p>\n\n\n\n<p>Use the&nbsp;<code>xata random-data<\/code>&nbsp;command to generate some random data inside your database. Open your&nbsp;<a href=\"https:\/\/app.xata.io\/workspaces\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Xata workspace<\/a>&nbsp;inside the browser to see your database in action.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--FsxD2RXn--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/naku3vrl7zct3d09eeod.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211614\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-5.png\" alt=\"Xata database with randomly generated data\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#creating-product-data\"><\/a>Creating Product Data<\/h2>\n\n\n\n<p>Now, let&#8217;s develop API endpoints for interacting with product data. Inside the&nbsp;<code>\/pages\/api<\/code>&nbsp;directory, add a folder called&nbsp;<code>products<\/code>&nbsp;and file called,&nbsp;<code>createProduct.js<\/code>. You&#8217;ll use this file to handle the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Methods\/POST\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">POST<\/a>&nbsp;request for creating a product within the database.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/pages\/api\/products\/createProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { getXataClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/..\/utils\/xata\"<\/span>;\n<span class=\"hljs-keyword\">const<\/span> xata = getXataClient();\n<span class=\"hljs-keyword\">const<\/span> handler = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-comment\">\/\/ create method is used to create records in database<\/span>\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">await<\/span> xata.db.products.create({ ...req.body });\n    res.json({ <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">\"Success \ud83d\ude01\"<\/span> });\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message });\n  }\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> handler;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The&nbsp;<code>Xata Client<\/code>&nbsp;provides methods for accessing the databases; you can use the names of the table to perform operations like&nbsp;<code>xata.db.&lt;table-name&gt;<\/code>&nbsp;for example. You can also create records inside a database using the&nbsp;<code>create()<\/code>&nbsp;method of the table.<\/p>\n\n\n\n<p>You can see the form inside&nbsp;<code>\/components\/ProductForm<\/code>&nbsp;for adding the required product data. I used&nbsp;<code>react-hook-form<\/code>&nbsp;to handle the form data in multiple steps.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--nSp-TGvH--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/9pqji541ux82wzc6mp39.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211617\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-6.png\" alt=\"Form for creating a new product inside the database\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#uploading-images-to-cloudinary\"><\/a>Uploading Images to Cloudinary<\/h2>\n\n\n\n<p>Before adding a new product to the database, you&#8217;ll first upload the image to Cloudinary and store the image link with product data. Once you have a Cloudinary account, follow the below steps to enable image upload.<br>There are two ways of uploading media to Cloudinary:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Signed presets.<\/li>\n\n\n\n<li>Unsigned presets. Create an unsigned preset to allow users to upload images to your cloud. Go to your Cloudinary Dashboard and<strong> <\/strong>navigate to&nbsp;<strong>Settings<\/strong> &gt; <strong>Upload<\/strong> &gt; <strong>Add upload preset<\/strong>.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--oFNld3xw--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/1taqsraq32i4e6xsw5zp.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211619\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-7.png\" alt=\"Adding a new upload preset inside Cloudinary\"\/><\/a><\/figure>\n\n\n\n<p>Configure the&nbsp;<strong>Name<\/strong>,&nbsp;<strong>Signing Mode<\/strong>, and&nbsp;<strong>Folder<\/strong>&nbsp;in the upload preset. Adding a folder is optional, but I recommend you put all your uploaded images in one place. You can learn more about Cloudinary upload presets in&nbsp;<a href=\"https:\/\/cloudinary.com\/documentation\" target=\"_blank\" rel=\"noreferrer noopener\">their documentation<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--F9q0l7Bn--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/o6kr2wug9octq3ad7ok1.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211622\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-8.png\" alt=\"Configuring the new upload preset inside cloudinary\"\/><\/a><\/figure>\n\n\n\n<p>Once saved, go to the previous page, where you can find the new unsigned preset.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--9URxG6of--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/knpn33oqzi1jsnrf4r4u.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211624\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-9.png\" alt=\"The new preset added\"\/><\/a><\/figure>\n\n\n\n<p>With your new preset, you can now upload the images to Cloudinary from the frontend. The&nbsp;<code>ThumbnailUpload.js<\/code>&nbsp;file inside&nbsp;<code>\/components\/ProductForm<\/code>&nbsp;directory will handle the image upload and add the image URL to the product data. Add the following code inside the file.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/components\/ProductForm\/ThumbnailUpload.js\n\n<span class=\"hljs-keyword\">import<\/span> React, { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Button <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/common\/Button\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> ThumbnailUpload = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ defaultValue, setValue }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;imageSrc, setImageSrc] = useState(defaultValue);\n  <span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;uploadData, setUploadData] = useState();\n  <span class=\"hljs-keyword\">const<\/span> handleOnChange = <span class=\"hljs-function\">(<span class=\"hljs-params\">changeEvent<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> handleUpload = <span class=\"hljs-keyword\">async<\/span> (uploadEvent) =&gt; {\n    uploadEvent.preventDefault();\n    setLoading(<span class=\"hljs-literal\">true<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> form = uploadEvent.currentTarget;\n    <span class=\"hljs-keyword\">const<\/span> fileInput = <span class=\"hljs-built_in\">Array<\/span>.from(form.elements).find(\n      <span class=\"hljs-function\">(<span class=\"hljs-params\">{ name }<\/span>) =&gt;<\/span> name === <span class=\"hljs-string\">\"file\"<\/span>\n    );\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n      <span class=\"hljs-comment\">\/\/ specifying cloudinary upload preset<\/span>\n      formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, <span class=\"hljs-string\">\"vnqoc9iz\"<\/span>);\n      <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> file <span class=\"hljs-keyword\">of<\/span> fileInput.files) {\n        formData.append(<span class=\"hljs-string\">\"file\"<\/span>, file);\n      }\n      <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(\n        <span class=\"hljs-string\">\"https:\/\/api.cloudinary.com\/v1_1\/scrapbook\/image\/upload\"<\/span>,\n        {\n          <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n          <span class=\"hljs-attr\">body<\/span>: formData,\n        }\n      );\n      <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> res.json();\n      setImageSrc(data.secure_url);\n      <span class=\"hljs-comment\">\/\/ adding the thumbnail URL to te main form data<\/span>\n      setValue(<span class=\"hljs-string\">\"thumbnail\"<\/span>, data.secure_url);\n      setUploadData(data);\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-built_in\">console<\/span>.log(error);\n    }\n    setLoading(<span class=\"hljs-literal\">false<\/span>);\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{handleSubmit}<\/span>&gt;<\/span>\n      \/\/ ...\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><\/span>\n  );\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ThumbnailUpload;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The code above does the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Extracts the file from the form input, sends a POST request to the Cloudinary API along with&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/FormData\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Form data<\/a>, and returns the public URL of the file<\/li>\n\n\n\n<li>Uses the&nbsp;<code>setValue<\/code>&nbsp;method of&nbsp;<code>react-hook-form<\/code>&nbsp;to add the image URL to the product form data<\/li>\n\n\n\n<li>And also updates the components&#8217; state to make UI interactive<\/li>\n<\/ul>\n\n\n\n<p>Similarly, you can add multiple image uploads using&nbsp;<code>Promise.all<\/code>&nbsp;method to handle the product media.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/components\/ProductForm\/MediaUpload.js\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<span class=\"hljs-keyword\">const<\/span> MediaUpload = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ defaultValues = &#91;], setValue }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;imageSrc, setImageSrc] = useState(&#91;...defaultValues]);\n  <span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;uploadedData, setUploadedData] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> handleOnChange = <span class=\"hljs-function\">(<span class=\"hljs-params\">changeEvent<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> handleUpload = <span class=\"hljs-keyword\">async<\/span> (uploadEvent) =&gt; {\n    uploadEvent.preventDefault();\n    setLoading(<span class=\"hljs-literal\">true<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> form = uploadEvent.currentTarget;\n    <span class=\"hljs-keyword\">const<\/span> fileInput = <span class=\"hljs-built_in\">Array<\/span>.from(form.elements).find(\n      <span class=\"hljs-function\">(<span class=\"hljs-params\">{ name }<\/span>) =&gt;<\/span> name === <span class=\"hljs-string\">\"file\"<\/span>\n    );\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-comment\">\/\/ adding upload preset<\/span>\n      <span class=\"hljs-keyword\">const<\/span> files = &#91;];\n      <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> file <span class=\"hljs-keyword\">of<\/span> fileInput.files) {\n        files.push(file);\n      }\n      <span class=\"hljs-keyword\">const<\/span> urls = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">Promise<\/span>.all(\n        files.map(<span class=\"hljs-keyword\">async<\/span> (file) =&gt; {\n          <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n          formData.append(<span class=\"hljs-string\">\"file\"<\/span>, file);\n          formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, <span class=\"hljs-string\">\"vnqoc9iz\"<\/span>);\n          <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(\n            <span class=\"hljs-string\">\"https:\/\/api.cloudinary.com\/v1_1\/scrapbook\/image\/upload\"<\/span>,\n            {\n              <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n              <span class=\"hljs-attr\">body<\/span>: formData,\n            }\n          );\n          <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> res.json();\n          <span class=\"hljs-keyword\">return<\/span> data.secure_url;\n        })\n      );\n      setImageSrc(urls);\n      setValue(<span class=\"hljs-string\">\"media\"<\/span>, urls);\n      setUploadedData(<span class=\"hljs-literal\">true<\/span>);\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-built_in\">console<\/span>.log(error);\n    }\n    setLoading(<span class=\"hljs-literal\">false<\/span>);\n  };\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{handleUpload}<\/span>&gt;<\/span>...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><\/span>;\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> MediaUpload;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, you should add the product to the database by sending a POST request to the endpoint you created.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/components\/Product\/AddProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { Close } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/common\/icons\/Close\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> ProductForm <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/ProductForm\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> AddProduct = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ props }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;isOpen, setIsOpen] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> handleClose = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> setIsOpen(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> handleOpen = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> setIsOpen(<span class=\"hljs-literal\">true<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> onFormSubmit = <span class=\"hljs-keyword\">async<\/span> (data) =&gt; {\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${baseUrl}<\/span>\/api\/products\/createProduct`<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n        <span class=\"hljs-attr\">headers<\/span>: {\n          <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n        },\n        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify(data),\n      }).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        handleClose();\n        <span class=\"hljs-built_in\">window<\/span>.location.reload();\n      });\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-built_in\">console<\/span>.log(error);\n    }\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  );\n};\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, try to add new products using the form. You can see the files uploaded to your Cloudinary and records created in Xata.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--niV-NLT2--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/ams8cd7pskeln7tozcis.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764212291\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-14.png\" alt=\"Xata database records created using the UI\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#querying-the-product-data\"><\/a>Querying the Product Data<\/h2>\n\n\n\n<p>For displaying all the products, you will use client-side rendering. To show the details of each product, let&#8217;s use <a href=\"https:\/\/cloudinary.com\/glossary\/server-side-rendering\">server-side rendering<\/a> with dynamic routing. Create an API endpoint&nbsp;<code>getProducts.js<\/code>&nbsp;for getting all the product data.<\/p>\n\n\n<div class='c-callout  c-callout--warning'><strong class='c-callout__title'>Warning:<\/strong> <p>It\u2019s advised not to use Xata in client-side programming. It may reveal your API key while retrieving data from the browser. So, we\u2019ll get all the data from the Xata inside API routes and deliver it to the client.<\/p>\n<\/div>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/pages\/api\/products\/getProducts.js\n\n<span class=\"hljs-keyword\">import<\/span> { getXataClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/..\/utils\/xata\"<\/span>;\n<span class=\"hljs-keyword\">const<\/span> xata = getXataClient();\n<span class=\"hljs-keyword\">const<\/span> handler = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-comment\">\/\/ getMany or getAll method can be used to create records in database<\/span>\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> xata.db.products.getAll();\n    res.json({ <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">\"Success \ud83d\ude01\"<\/span>, data });\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message, <span class=\"hljs-attr\">data<\/span>: &#91;] });\n  }\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> handler;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here, you used the&nbsp;<code>getAll<\/code>&nbsp;method of the table to fetch all the records at one time. Refer to the&nbsp;<a href=\"https:\/\/xata.io\/docs\/quickstart\/building-a-web-app-with-xata-and-nextjs#methods-to-query-the-database\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Xata docs<\/a>&nbsp;for more ways of fetching data from a table.<\/p>\n\n\n\n<p>Inside the&nbsp;<code>\/pages\/Products.js<\/code>&nbsp;file, add the following code for fetching the data from the API.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"> \/pages\/Products.js\n\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Products<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;products, setProducts] = useState(&#91;]);\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> fetchData = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n      setLoading(<span class=\"hljs-literal\">true<\/span>);\n      <span class=\"hljs-keyword\">try<\/span> {\n        <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`\/api\/products\/getProducts`<\/span>);\n        <span class=\"hljs-keyword\">const<\/span> { data } = <span class=\"hljs-keyword\">await<\/span> res.json();\n        setProducts(data);\n      } <span class=\"hljs-keyword\">catch<\/span> (error) {\n        <span class=\"hljs-built_in\">console<\/span>.log(error);\n      }\n      setLoading(<span class=\"hljs-literal\">false<\/span>);\n    };\n    fetchData();\n  }, &#91;]);\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  );\n}\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the above code, you&#8217;re fetching the data inside the&nbsp;<code>useEffect<\/code>&nbsp;hook from the API you created before. Save your code and head over to the browser. You can see the product data displayed in the form of a table.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--OmSKbJop--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/3ncvatptpjplssc5m98e.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211627\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-10.png\" alt=\"Displaying all the product data in the UI, fetching from database\"\/><\/a><\/figure>\n\n\n\n<p>You&#8217;ll be redirected to the&nbsp;<code>\/product\/&lt;some-product-id&gt;<\/code>&nbsp;when you click on the details. As of now, you&#8217;ll see an empty page. Let&#8217;s move on to create the add the data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#generating-static-pages-for-product-details\"><\/a>Generating Static Pages for Product Details<\/h2>\n\n\n\n<p>You can find the&nbsp;<code>\/product<\/code>&nbsp;folder inside the&nbsp;<code>\/pages<\/code>&nbsp;directory. As mentioned, I added the&nbsp;<code>[id].js<\/code>&nbsp;file to generate dynamic routes. You&#8217;ll access the product&#8217;s id using the parameters of the route. Refer to Next.js&nbsp;<a href=\"https:\/\/nextjs.org\/docs\/routing\/dynamic-routes\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>&nbsp;for more information on dynamic.<\/p>\n\n\n\n<p>In the following code, first, you&#8217;re fetching all the items to add paths for&nbsp;<code>getStaticPaths<\/code>. Then, in&nbsp;<code>getStaticProps<\/code>, you get the&nbsp;<code>id<\/code>&nbsp;from the route parameters (<code>params<\/code>) and bring the single product data for passing the props to the page.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/pages\/product\/&#91;id].js\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { getXataClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/utils\/xata\"<\/span>;\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> xata = getXataClient();\n<span class=\"hljs-comment\">\/\/ props passed in the server while generating the page<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Product<\/span>(<span class=\"hljs-params\">{ product }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  );\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Product;\n\n<span class=\"hljs-comment\">\/\/ fetching data from xata in server for generating static pages<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getStaticProps<\/span>(<span class=\"hljs-params\">{ params }<\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ getting filtered data from Xat<\/span>\n  <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> xata.db.products\n    .filter({\n      <span class=\"hljs-attr\">id<\/span>: params.id,\n    })\n    .getFirst();\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">props<\/span>: { <span class=\"hljs-attr\">product<\/span>: data },\n  };\n}\n<span class=\"hljs-comment\">\/\/ pre-rendering all the static paths<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getStaticPaths<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> products = <span class=\"hljs-keyword\">await<\/span> xata.db.products.getAll();\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">paths<\/span>: products.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">item<\/span>) =&gt;<\/span> ({\n      <span class=\"hljs-attr\">params<\/span>: { <span class=\"hljs-attr\">id<\/span>: item.id },\n    })),\n    <span class=\"hljs-comment\">\/\/ whether to run fallback incase if user requested a page other than what is passed inside the paths<\/span>\n    <span class=\"hljs-attr\">fallback<\/span>: <span class=\"hljs-literal\">true<\/span>,\n  };\n}\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Kill the terminal and again run the&nbsp;<code>yarn dev<\/code>&nbsp;command. Open your browser and try to see the details of any product. Now you can see the page ready with the data. Here&#8217;s mine:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--46DYOxpx--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/7y5zwg1cb2xxvc7qi99x.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764211628\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-11.jpg\" alt=\"Statically generated product details page\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#updating-the-product\"><\/a>Updating the Product<\/h2>\n\n\n\n<p>Let&#8217;s add the functionality of updating the product. Create an API route for updating data from serverless functions. Create a new file,&nbsp;<code>updateProduct.js<\/code>, for updating the data inside your products API directory. Just like creating, you can edit the records in Xata using the&nbsp;<code>update<\/code>&nbsp;method of the table. The&nbsp;<code>update<\/code>&nbsp;method will identify the record using it\u2019s&nbsp;<code>id<\/code>&nbsp;and update the columns with the&nbsp;<code>data<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/pages\/api\/products\/updateProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { getXataClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/..\/utils\/xata\"<\/span>;\n<span class=\"hljs-keyword\">const<\/span> xata = getXataClient();\n<span class=\"hljs-keyword\">const<\/span> handler = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-comment\">\/\/ using update method to update records in database<\/span>\n  <span class=\"hljs-keyword\">const<\/span> { id, ...data } = req.body;\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">await<\/span> xata.db.products.update(id, { ...data });\n    res.json({ <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">\"Success \ud83d\ude01\"<\/span> });\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-built_in\">console<\/span>.log(error);\n    res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message });\n  }\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> handler;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Navigate to&nbsp;<code>UpdateProduct.js<\/code>&nbsp;component inside&nbsp;<code>\/components\/Product<\/code>&nbsp;to add the function to update the product data. You&#8217;ll send a&nbsp;<strong>PUT<\/strong>&nbsp;request to the API endpoint along with the product id and updated data.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/components\/Product\/UpdateProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { baseUrl } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/utils\/config\"<\/span>;\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> UpdateProduct = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ product, ...props }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-keyword\">const<\/span> onFormSubmit = <span class=\"hljs-keyword\">async<\/span> (data) =&gt; {\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${baseUrl}<\/span>api\/products\/updateProduct`<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"PUT\"<\/span>,\n        <span class=\"hljs-attr\">headers<\/span>: {\n          <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n        },\n        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({ <span class=\"hljs-attr\">id<\/span>: product.id, ...data }),\n      }).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        handleClose();\n        <span class=\"hljs-built_in\">window<\/span>.location.reload();\n      });\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-built_in\">console<\/span>.log(error);\n    }\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  );\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> UpdateProduct;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Try updating the product data using the form. You&#8217;ll see the data updated on the product details page and Xata table.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#deleting-a-product\"><\/a>Deleting a Product<\/h2>\n\n\n\n<p>To delete a specific record from a database, you can use the&nbsp;<code>delete<\/code>&nbsp;method with the&nbsp;<code>id<\/code>&nbsp;of the product. Create a new file,&nbsp;<code>deleteProduct.js<\/code>, inside the&nbsp;<code>\/page\/api\/products<\/code>&nbsp;folder.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/pages\/api\/products\/deleteProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { getXataClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/..\/utils\/xata\"<\/span>;\n<span class=\"hljs-keyword\">const<\/span> xata = getXataClient();\n<span class=\"hljs-keyword\">const<\/span> handler = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-comment\">\/\/ use delete method for deleting the records in database<\/span>\n  <span class=\"hljs-keyword\">const<\/span> { id } = req.body;\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">await<\/span> xata.db.products.delete(id);\n    res.json({ <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">\"Success \ud83d\ude01\"<\/span> });\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message });\n  }\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> handler;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, add the&nbsp;<code>handleDelete<\/code>&nbsp;function inside the&nbsp;<code>DeleteProduct.js<\/code>&nbsp;component.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\/components\/Product\/DeleteProduct.js\n\n<span class=\"hljs-keyword\">import<\/span> { baseUrl } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/..\/utils\/config\"<\/span>;\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> DeleteProduct = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ productId }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-keyword\">const<\/span> handleDelete = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${baseUrl}<\/span>\/api\/products\/deleteProduct`<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"DELETE\"<\/span>,\n        <span class=\"hljs-attr\">headers<\/span>: {\n          <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n        },\n        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({ <span class=\"hljs-attr\">id<\/span>: productId }),\n      }).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        handleClose();\n        <span class=\"hljs-built_in\">window<\/span>.location.replace(<span class=\"hljs-string\">\"\/products\"<\/span>);\n      });\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      <span class=\"hljs-built_in\">console<\/span>.log(error);\n    }\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  );\n};\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> DeleteProduct;\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can try deleting the products from the product details page. If it works, you&#8217;re good to go.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#deploying-to-netlify\"><\/a>Deploying to Netlify<\/h2>\n\n\n\n<p>To make this app available throughout the internet, deploy it to a cloud service. Netlify is an ideal service provider for this project as it supports Xata and Next.js out of the box. Install the Netlify CLI globally using&nbsp;<code>npm<\/code>&nbsp;to deploy the app from your terminal. Log in to CLI using your Netlify account.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\"># installing the Netlify CLI globally<\/span>\nnpm i -g netlify-cli\n\n<span class=\"hljs-comment\"># logging into your Netlify account<\/span>\nntl login\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After successful login, try running&nbsp;<code>ntl init<\/code>&nbsp;inside the project folder to configure Netlify. Answer the prompted questions, and you will have the URL for your deployed site. Here\u2019s&nbsp;<a href=\"https:\/\/jamstackhack-22.netlify.app\/\">mine<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s---3Nio-pa--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/articles\/9y1ei0u707bmxn42qi90.png\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764212443\/blog-How_to_Build_a_Full_Stack_Content_Management_System_Using_Next.js_Xata_and_Cloudinary-15.jpg\" alt=\"Netlify configuration using netlify-cli\"\/><\/a><\/figure>\n\n\n\n<p>If you face any issues, try to fix them following&nbsp;<a href=\"https:\/\/www.netlify.com\/blog\/2020\/11\/30\/how-to-deploy-next.js-sites-to-netlify\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">this guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#conclusion\"><\/a>Conclusion<\/h2>\n\n\n\n<p>In this tutorial, you have successfully created a CRUD app using Next.js, Xata, and Cloudinary. As Jamstack combines several technologies for markup and APIs, you can use other services besides what you used in this tutorial. You can take this tutorial further by creating the frontend of an e-commerce site using your preferred framework.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/dev.to\/hackmamba\/how-to-build-a-full-stack-content-management-system-using-nextjs-xata-and-cloudinary-iaa#resources\"><\/a>Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/nextjs.org\/docs\/getting-started\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Next.js documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/xata.io\/docs\/overview\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Xata documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary<\/a>&nbsp;<a href=\"https:\/\/cloudinary.com\/documentation\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\">documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.oracle.com\/content-management\/what-is-cms\/?utm_source=hackmamba&amp;utm_campaign=hackmamba-hackathon&amp;utm_medium=hackmamba-blog\" target=\"_blank\" rel=\"noreferrer noopener\">More about CMS<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Managing a website&#8217;s content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded JSON file. But, when creating large-scale projects, managing files and databases becomes complex. In these cases, using a&nbsp;content management system&nbsp;(CMS) is one of the simple and efficient methods. A CMS is [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":31316,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[25,212],"class_list":["post-31315","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-asset-management","tag-next-js"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Build a Full Stack CMS Using Next.js, Xata, and Cloudinary<\/title>\n<meta name=\"description\" content=\"Managing a website&#039;s content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Managing a website&#039;s content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-09-29T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-27T03:01:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary\",\"datePublished\":\"2023-09-29T14:00:00+00:00\",\"dateModified\":\"2025-11-27T03:01:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\"},\"wordCount\":2101,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA\",\"keywords\":[\"Asset Management\",\"Next.js\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2023\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\",\"name\":\"Build a Full Stack CMS Using Next.js, Xata, and Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA\",\"datePublished\":\"2023-09-29T14:00:00+00:00\",\"dateModified\":\"2025-11-27T03:01:01+00:00\",\"description\":\"Managing a website's content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build a Full Stack CMS Using Next.js, Xata, and Cloudinary","description":"Managing a website's content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary","og_locale":"en_US","og_type":"article","og_title":"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary","og_description":"Managing a website's content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded","og_url":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2023-09-29T14:00:00+00:00","article_modified_time":"2025-11-27T03:01:01+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary","datePublished":"2023-09-29T14:00:00+00:00","dateModified":"2025-11-27T03:01:01+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary"},"wordCount":2101,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","keywords":["Asset Management","Next.js"],"inLanguage":"en-US","copyrightYear":"2023","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary","url":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary","name":"Build a Full Stack CMS Using Next.js, Xata, and Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","datePublished":"2023-09-29T14:00:00+00:00","dateModified":"2025-11-27T03:01:01+00:00","description":"Managing a website's content is always challenging when developing a modern web application. You can manage the content via a database or a hard-coded","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/build-full-stack-content-management-system-next-js-xata-cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Build a Full Stack Content Management System Using Next.js, Xata, and Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1695764695\/Blog-Hackmamba_Build_Full_Stack_CMS\/Blog-Hackmamba_Build_Full_Stack_CMS.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31315","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=31315"}],"version-history":[{"count":6,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31315\/revisions"}],"predecessor-version":[{"id":39450,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31315\/revisions\/39450"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/31316"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=31315"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=31315"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=31315"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}