Tutorials
Service Account HR & Payroll Tutorial
Build a secure, private enterprise HR management app using Service Account credentials.
Learn how to securely access unshared, private Google Sheets for enterprise HR and payroll records.
1. Setup Service Account Credentials
- In GCP Console, create a Service Account (e.g.
hr-bot@project.iam.gserviceaccount.com). - Create and download a JSON Key File.
- Open your private Google Sheet -> Share with
hr-bot@project.iam.gserviceaccount.comas Editor. - Configure
.env:
.env
GSHEET_SPREADSHEET_ID=your-private-spreadsheet-id
GSHEET_CLIENT_EMAIL=hr-bot@project.iam.gserviceaccount.com
GSHEET_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC...\n-----END PRIVATE KEY-----\n"
2. Vue Component Implementation (Read & Write)
pages/hr/employees.vue
<script setup lang="ts">
interface Employee {
empId: string
name: string
department: string
salary: string
}
// 1. Secure Server-Side Read
const { data: employees, pending, refresh } = await useGSheetAsObject<Employee[]>('A1:D50', {
sheet: 'payroll'
})
// 2. Private Write Operations
const { append } = useGSheetWrite({ sheet: 'payroll' })
const name = ref('')
const department = ref('Engineering')
const salary = ref('85000')
const addEmployee = async () => {
if (!name.value) return
await append('A1:D1', [
[`EMP-${Date.now()}`, name.value, department.value, salary.value]
])
name.value = ''
await refresh()
}
</script>
<template>
<div class="hr-portal">
<h1>Enterprise HR & Payroll Portal</h1>
<form @submit.prevent="addEmployee">
<input
v-model="name"
placeholder="Employee Name"
required
>
<select v-model="department">
<option value="Engineering">
Engineering
</option>
<option value="Marketing">
Marketing
</option>
</select>
<input
v-model="salary"
placeholder="Salary"
required
>
<button type="submit">
Add Employee
</button>
</form>
<div v-if="pending">
Authenticating JWT & loading private records...
</div>
<table v-else>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Dept</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr
v-for="emp in employees"
:key="emp.empId"
>
<td>{{ emp.empId }}</td>
<td>{{ emp.name }}</td>
<td>{{ emp.department }}</td>
<td>${{ emp.salary }}</td>
</tr>
</tbody>
</table>
</div>
</template>