This tutorial will guide you through creating a museum booking app using Vue.js, TypeScript, Timerise API, Apollo Client, and Tailwind CSS. We will focus on integrating the headless Timerise API, demonstrating the benefits of such a solution.
Prerequisites
- Basic knowledge of TypeScript and Vue.js
- Node.js installed
- Access to Timerise API
Step 1: Setting Up the Vue.js Project
The first step in building our museum booking app is to set up a new Vue.js project. Vue CLI provides a flexible and straightforward way to set up new projects. This approach allows us to configure essential features like Tailwind CSS, ESLint, and more, right from the start, simplifying the initial project setup.
Create a New Vue.js App
Run the command to initiate the creation of a new Vue.js app. You’ll be able to integrate Tailwind CSS and other configurations during the setup process.
npm create vue@latest vue-museum-booking-app
cd vue-museum-booking-app
npm install
npm run format
npm run dev
When prompted, choose your preferred configurations:
- Add TypeScript? … No / Yes
- Add JSX Support? … No / Yes
- Add Vue Router for Single Page Application development? … No / Yes
- Add Pinia for state management? … No / Yes
- Add Vitest for Unit Testing? … No / Yes
- Add an End-to-End Testing Solution? › No
- Add ESLint for code quality? … No / Yes
- Add Prettier for code formatting? … No / Yes
Step 2: Installing Dependencies
With our Vue.js project set up, we now need to install additional packages crucial for our application, particularly Apollo Client for handling GraphQL queries and mutations with the Timerise API.
Install Apollo Client
Apollo Client is a comprehensive state management library that enables you to manage both local and remote data with GraphQL. It will be our primary tool for interacting with the Timerise API.
npm install @apollo/client graphql
npm install @vue/apollo-option @vue/apollo-components --save
Additional Dependencies
Depending on your project requirements, you may need other dependencies. For example, if you plan to handle dates and times (common in a booking app), consider installing date-fns:
npm install date-fns
Step 3: Configuring Apollo Client
Now that our project is set up with the necessary dependencies, it’s time to configure Apollo Client. This setup enables our application to efficiently execute queries and mutations, manage loading states, cache data, and handle errors.
Set Up Apollo Client in lib/apolloClient.ts
Create an instance of Apollo Client pointing to the GraphQL API endpoint. This instance will be used throughout the application.
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
const httpLink = createHttpLink({
uri: 'https://sandbox-api.timerise.io/v1',
})
const cache = new InMemoryCache()
export const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
- The uri property is set to the endpoint of the Timerise API.
- InMemoryCache is used to cache query results after fetching them. This helps in optimizing the performance of our application by reducing the number of API calls needed.
This configuration is a crucial step in enabling our application to communicate effectively with the Timerise API. It sets the stage for the next steps where we will be creating and executing GraphQL queries and mutations to handle bookings.
Wrap Your Application with ApolloProvider
In your main.ts (or main.ts if using TypeScript), import ApolloProvider and the Apollo Client instance. Then, use Vue’s global properties feature to provide the Apollo Client instance globally.
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { apolloClient } from './lib/apolloClient'
import { createApolloProvider } from '@vue/apollo-option'
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
})
const app = createApp(App)
app.use(apolloProvider)
app.use(router)
app.mount('#app')
Step 4: Creating GraphQL Queries and Mutations
Define the GraphQL queries and mutations needed to interact with the Timerise API for fetching services and booking slots in src/graphql/queries.ts
and src/graphql/mutations.ts
.
Define GraphQL Operations in graphql/queries.ts
import { gql } from '@apollo/client/core';
export const SERVICE_QUERY = gql`
query Service($serviceId: ID!, $slotType: SlotType!) {
service(serviceId: $serviceId) {
serviceId
title
description
media {
title
url
}
slots(slotType: $slotType) {
slotId
dateTimeFrom
dateTimeTo
duration
quantity
}
}
}
`;
export const BOOKING_QUERY = gql`
query Booking($bookingId: ID!) {
booking(bookingId: $bookingId) {
bookingId
shortId
shortUrl
qrUrl
status
}
}
`;
Define Mutation in graphql/mutations.ts
import { gql } from '@apollo/client/core';
export const BOOKING_CREATE_MUTATION = gql`
mutation BookingCreate($serviceId: ID!, $slots: [ID]) {
bookingCreate(serviceId: $serviceId, slots: $slots) {
bookingId
qrUrl
shortUrl
status
}
}
`;
Step 5: Building the Application Components
With our API interactions defined, we now focus on building the React components. These components will provide the interface for selecting slots and confirming bookings, utilizing the queries and mutations we’ve set up.
Slot Selection Component (SlotSelection.vue)
Code and explanation for creating a component to select available slots.
<template>
<div>
<h2>Select a slot to book</h2>
<ul>
<li v-for="slot in service.slots" :key="slot.slotId">
<a
@click="handleSlotSelect(slot.slotId)"
>
{{ new Date(slot.dateTimeFrom).toLocaleString() }}
</a>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { provideApolloClient } from '@vue/apollo-composable';
import { apolloClient } from '../lib/apolloClient';
import { defineComponent, ref, watchEffect } from 'vue'
import { useQuery, useMutation } from '@vue/apollo-composable'
import { SERVICE_QUERY } from '@/graphql/queries'
import { BOOKING_CREATE_MUTATION } from '@/graphql/mutations'
import { useRouter } from 'vue-router'
export default defineComponent({
props: {
serviceId: String
},
setup(props) {
provideApolloClient(apolloClient);
const router = useRouter()
const selectedSlot = ref<string | null>(null)
const { result, loading, error } = useQuery(SERVICE_QUERY, () => ({
serviceId: props.serviceId,
slotType: 'AVAILABLE'
}))
const service = ref<any>({});
watchEffect(() => {
if (result.value && result.value.service) {
service.value = result.value.service;
}
});
const {
mutate: createBooking,
loading: bookingLoading,
error: bookingError
} = useMutation(BOOKING_CREATE_MUTATION)
const handleSlotSelect = async (slotId: string) => {
selectedSlot.value = slotId;
try {
const result = await createBooking({ serviceId: props.serviceId, slots: [slotId] });
if (result && 'data' in result) {
const { data } = result;
if (data.bookingCreate && data.bookingCreate.bookingId) {
router.push(`/confirmation/${data.bookingCreate.bookingId}`);
}
} else {
console.error('No data returned from booking creation');
}
} catch (error) {
console.error('Error creating booking:', error);
// Handle booking error here
}
};
return {
service,
loading,
error,
bookingLoading,
bookingError,
selectedSlot,
handleSlotSelect
}
}
})
</script>
Confirmation Component (BookingConfirmation.vue)
Code and explanation for creating a component that displays booking confirmation details, including the QR code.
<template>
<div v-if="!loading && !error" style="text-align: center;">
<h2>Booking status: {{ booking.status }}</h2>
<p style="margin-bottom: 8px;">Booking ID: {{ booking.shortId }}</p>
<div>
<img :src="booking.qrUrl" alt="QR Code" width="200" />
</div>
<a :href="booking.shortUrl" target="_blank">View booking page</a>
</div>
<p v-if="loading">Loading booking...</p>
<p v-if="error">Error loading booking.</p>
</template>
<script lang="ts">
import { provideApolloClient } from '@vue/apollo-composable';
import { apolloClient } from '../lib/apolloClient';
import { defineComponent, ref, watchEffect } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import { BOOKING_QUERY } from '@/graphql/queries'
export default defineComponent({
props: {
bookingId: {
type: String,
required: true,
},
},
setup(props) {
provideApolloClient(apolloClient);
const { result, loading, error } = useQuery(BOOKING_QUERY, () => ({
bookingId: props.bookingId,
}));
const booking = ref<any>({});
watchEffect(() => {
if (result.value && result.value.booking) {
booking.value = result.value.booking;
}
});
return { booking, loading, error };
},
});
</script>
Step 6: Deploying with Netlify or Vercel
After building your Vue.js app, you can choose to deploy it using Netlify or Vercel for Vue.js projects. Ensure your project is pushed to a GitHub repository, then follow the respective platform’s documentation for deploying Vue.js applications.
Push Your Code to GitHub
Before deploying, ensure your project is pushed to a GitHub repository:
git init
git add .
git commit -m "Init commit"
git branch -M main
git remote add origin https://github.com/your-username/your-repo-name.git
git push -u origin main
Set Up Deployment on Vercel
- Sign in to Vercel and connect your GitHub account.
- Choose the repository you just pushed.
- Vercel automatically detects it’s a Next.js app and suggests build settings. Accept them or modify as needed.
- Click ‘Deploy‘ to start the deployment process. Vercel handles the build and deployment, providing you with a live URL.
Vercel provides features like automatic HTTPS, custom domain support, and real-time analytics. You can also set environment variables and manage other settings directly from the Vercel dashboard.
Accessing the Complete Application Code
To complement this tutorial, the complete source code of the museum booking application will be made available on GitHub. This is an excellent opportunity for you to explore the codebase, experiment with modifications, and understand the application’s structure and functionality in greater detail.
Access the Repository
The entire source code for the museum booking application is hosted on GitHub. You can access it at the following URL: https://github.com/timerise-io/museum-booking-app
Clone the Repository
To work with the code on your local machine, clone the repository using Git:
git clone https://github.com/timerise-io/vue-museum-booking-app.git
cd vue-museum-booking-app
Explore and Modify
Once you have the code, feel free to explore and modify it. Whether you want to understand specific functionalities, add new features, or customize the UI, this codebase can serve as a practical starting point.
Visit the Demo
You can access the live demo of the Museum Booking App at the following URL: https://vue-museum-booking-app.vercel.app/
Conclusion
This tutorial provided a step-by-step guide on setting up a Vue.js application, integrating with a headless API like Timerise, and deploying it. The code snippets are crucial parts of the application, demonstrating how to integrate the Timerise API with a Vue.js app using Apollo Client for GraphQL.