Tutorials
Apps Script Web App Tutorial
Build a live feedback and submission portal using Google Apps Script Web App URL.
Learn how to build a full-stack feedback submission portal using Google Apps Script without GCP credentials or API limits.
1. Google Sheets & Apps Script Setup
- Create a Google Sheet named User Feedback.
- Name the sheet tab
feedback. - Add column headers in Row 1:
id,author,rating,comment. - Open Extensions -> Apps Script and paste
Code.gs:
Code.gs
function doGet(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(e.parameter.sheet || 'feedback');
const values = sheet.getRange(e.parameter.range || 'A1:Z100').getValues();
return ContentService.createTextOutput(JSON.stringify(values)).setMimeType(ContentService.MimeType.JSON);
}
function doPost(e) {
const data = JSON.parse(e.postData.contents);
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(data.sheet || 'feedback');
if (data.values) {
data.values.forEach(r => sheet.appendRow(r));
}
return ContentService.createTextOutput(JSON.stringify({ success: true })).setMimeType(ContentService.MimeType.JSON);
}
- Click Deploy -> New Deployment -> Select Web App (Execute as: Me, Who has access: Anyone).
- Copy Web App URL into
.env:
.env
GSHEET_APPSCRIPT_URL=https://script.google.com/macros/s/AKfycb.../exec
2. Vue Component Implementation
pages/feedback.vue
<script setup lang="ts">
interface FeedbackItem {
id: string
author: string
rating: string
comment: string
}
// Read data
const { data: items, pending, refresh } = await useGSheetAsObject<FeedbackItem[]>('A1:D50', {
sheet: 'feedback'
})
// Write data
const { append } = useGSheetWrite({ sheet: 'feedback' })
const author = ref('')
const rating = ref('5')
const comment = ref('')
const isSaving = ref(false)
const submitComment = async () => {
if (!author.value || !comment.value) return
isSaving.value = true
try {
await append('A1:D1', [
[String(Date.now()), author.value, rating.value, comment.value]
])
author.value = ''
comment.value = ''
await refresh()
}
finally {
isSaving.value = false
}
}
</script>
<template>
<div class="feedback-page">
<h1>Submit User Feedback</h1>
<form @submit.prevent="submitComment">
<input
v-model="author"
placeholder="Your Name"
required
>
<select v-model="rating">
<option value="5">
5 Stars
</option>
<option value="4">
4 Stars
</option>
<option value="3">
3 Stars
</option>
</select>
<textarea
v-model="comment"
placeholder="Comment"
required
/>
<button
:disabled="isSaving"
type="submit"
>
{{ isSaving ? 'Sending...' : 'Submit Feedback' }}
</button>
</form>
<hr>
<div v-if="pending">
Loading comments...
</div>
<ul v-else>
<li
v-for="item in items"
:key="item.id"
>
<strong>{{ item.author }}</strong> ({{ item.rating }} Stars): {{ item.comment }}
</li>
</ul>
</div>
</template>