Can AI Assistants Help Visual Developers Build Web Apps Faster?
With the help of AI assistants, visual developers can build web apps and prototypes faster than ever before. Here's how it works.
Over the last week, my Twitter timeline has overflowed with users sharing their ChatGPT results. Last night the FOMO kicked in, and I had to take it for a spin.
What is ChatGPT?
ChatGPT is a variant of the GPT-3 (Generative Pretentious Transformer 3) language model that is designed to be used in a conversational context. This means that it is trained to generate relevant and coherent responses to a given conversation. Like other large language models, ChatGPT can generate human-like text on a wide range of topics, but its ability to respond in a conversational manner sets it apart from other models.
The challenge
At Alphi, our most common support enquiry is users asking how they can display data from an Alphi endpoint on their website. Typically we would point them to a nocode/lowcode frontend framework like WeWeb, Wized or AlphiJS.
Sometimes users aren't looking for another tool or framework; instead, they want a Javascript snippet to add to their website.
Could ChatGPT help solve this issue? 🤔
The result
The AI assistant was able to generate the Javascript code to fetch a list of blog posts and display them on the page.
Additionally, it was able to fix an XSS vulnerability when prompted, convert the function to async await and generate a HTML template using TailwindCSS.
Transcript
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// display each item in the response on the page
response.forEach(item => {
let div = document.createElement("div");
div.innerHTML = item.title;
document.body.appendChild(div);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "template"
let template = document.getElementById("template");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = template.cloneNode(true);
clone.querySelector(".title").innerHTML = item.title;
clone.querySelector(".body").innerHTML = item.body;
document.body.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "template"
let template = document.getElementById("template");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = template.cloneNode(true);
clone.querySelector("[data-bind='title']").innerHTML = item.title;
clone.querySelector("[data-bind='body']").innerHTML = item.body;
document.body.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "template"
let template = document.getElementById("template");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = template.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
document.body.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "template" and the element with the id "blogList"
let template = document.getElementById("template");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = template.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "blogTemplate" and the element with the id "blogList"
let template = document.getElementById("blogTemplate");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = template.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "blogTemplate" and the element with the id "blogList"
let blogTemplate = document.getElementById("blogTemplate");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = blogTemplate.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display it on the page
let div = document.createElement("div");
div.innerHTML = error.message;
document.body.appendChild(div);
});
}
// make the request on page load
makeRequest();
function makeRequest() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "blogTemplate" and the element with the id "blogList"
let blogTemplate = document.getElementById("blogTemplate");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = blogTemplate.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display the error modal and set the error message
let errorModal = document.getElementById("errorModal");
errorModal.querySelector("[data-bind='error']").textContent = error.message;
errorModal.style.display = "block";
});
}
// make the request on page load
makeRequest();
function getBlogPosts() {
// make a GET request to https://jsonplaceholder.typicode.com/posts
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// if the request was successful, process the response
if (response.ok) {
return response.json();
}
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
})
.then(response => {
// get the element with the id "blogTemplate" and the element with the id "blogList"
let blogTemplate = document.getElementById("blogTemplate");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
response.forEach(item => {
let clone = blogTemplate.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
})
.catch(error => {
// if there was an error, display the error modal and set the error message
let errorModal = document.getElementById("errorModal");
errorModal.querySelector("[data-bind='error']").textContent = error.message;
errorModal.style.display = "block";
});
}
// make the request on page load
getBlogPosts();
async function getBlogPosts() {
try {
// make a GET request to https://jsonplaceholder.typicode.com/posts
let response = await fetch("https://jsonplaceholder.typicode.com/posts");
// if the request was successful, process the response
if (response.ok) {
let responseJson = await response.json();
// get the element with the id "blogTemplate" and the element with the id "blogList"
let blogTemplate = document.getElementById("blogTemplate");
let blogList = document.getElementById("blogList");
// for each item in the response, clone the template and bind the data
responseJson.forEach(item => {
let clone = blogTemplate.cloneNode(true);
clone.querySelector("[data-bind='title']").textContent = item.title;
clone.querySelector("[data-bind='body']").textContent = item.body;
blogList.appendChild(clone);
});
} else {
// if the request was not successful, throw an error
throw new Error("Request failed: " + response.statusText);
}
} catch (error) {
// if there was an error, display the error modal and set the error message
let errorModal = document.getElementById("errorModal");
errorModal.querySelector("[data-bind='error']").textContent = error.message;
errorModal.style.display = "block";
}
}
// make the request on page load
getBlogPosts();
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
<style>
/* add styles for the page here */
</style>
</head>
<body>
<h1>Blog Posts</h1>
<div id="blogList">
<!-- this is the template for each item in the response -->
<div id="blogTemplate">
<h2 data-bind="title"></h2>
<p data-bind="body"></p>
</div>
</div>
<!-- this is the error modal -->
<div id="errorModal" style="display: none;">
<h2>An error occurred</h2>
<p data-bind="error"></p>
</div>
<script>
// include the JavaScript function here
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" />
</head>
<body class="bg-gray-100">
<h1 class="text-2xl font-bold m-4">Blog Posts</h1>
<div id="blogList" class="grid grid-cols-2 gap-4 m-4">
<!-- this is the template for each item in the response -->
<div id="blogTemplate" class="p-4 bg-white rounded-lg shadow-md">
<h2 class="font-bold" data-bind="title"></h2>
<p data-bind="body"></p>
</div>
</div>
<!-- this is the error modal -->
<div id="errorModal" class="fixed bottom-0 left-0 right-0 p-4 bg-white rounded-lg shadow-md" style="display: none;">
<h2 class="font-bold">An error occurred</h2>
<p data-bind="error"></p>
</div>
<script>
// include the JavaScript function here
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" />
</head>
<body class="bg-gray-100">
<h1 class="text-2xl font-bold m-4">Blog Posts</h1>
<div id="blogList" class="grid grid-cols-2 gap-4 m-4">
<!-- this is the hidden container for the template -->
<div style="display: none;">
<!-- this is the template for each item in the response -->
<div id="blogTemplate" class="p-4 bg-white rounded-lg shadow-md">
<h2 class="font-bold" data-bind="title"></h2>
<p data-bind="body"></p>
</div>
</div>
</div>
<!-- this is the error modal -->
<div id="errorModal" class="fixed bottom-0 left-0 right-0 p-4 bg-white rounded-lg shadow-md" style="display: none;">
<h2 class="font-bold">An error occurred</h2>
<p data-bind="error"></p>
</div>
<script>
// include the JavaScript function here
</script>
</body>
</html>
Summary
Whilst this was a great experiment, I would only recommend generating code snippets for production if you are well-versed in writing Javascript. Nevertheless, I am excited to see how AI assistants impact the no-code landscape over the next year and whether we will see page builders adopt the technology. The future is bright. 🌅