Build a seat booking app with SQLite in Durable Objects
In this tutorial, you will learn how to build a seat reservation app using Durable Objects. This app will allow users to book a seat for a flight. The app will be written in TypeScript and will use the new SQLite storage backend in Durable Object to store the data.
Using Durable Objects, you can write reusable code that can handle coordination and state management for multiple clients. Moreover, writing data to SQLite in Durable Objects is synchronous and uses local disks, therefore all queries are executed with great performance. You can learn more about SQLite storage in Durable Objects in the SQLite in Durable Objects blog post โ.
The application will function as follows:
- A user navigates to the application with a flight number passed as a query parameter.
- The application will create a new Durable Object for the flight number, if it does not already exist.
- If the Durable Object already exists, the application will retrieve the seats information from the SQLite database.
- If the Durable Object does not exist, the application will create a new Durable Object and initialize the SQLite database with the seats information. For the purpose of this tutorial, the seats information is hard-coded in the application.
- When a user selects a seat, the application asks for their name. The application will then reserve the seat and store the name in the SQLite database.
- The application also broadcasts any changes to the seats to all clients.
Let's get started!
- Sign up for a Cloudflare account โ.
- Install
Node.js
โ.
Node.js version manager
Use a Node version manager like Volta โ or
nvm โ to avoid permission issues and change
Node.js versions. Wrangler, discussed
later in this guide, requires a Node version of 16.17.0
or later.
Create a new Worker project to create and deploy your app.
-
Create a Worker named
seat-booking
by running:Terminal window npm create cloudflare@latest -- seat-bookingTerminal window yarn create cloudflare seat-bookingTerminal window pnpm create cloudflare@latest seat-bookingFor setup, select the following options:
- For What would you like to start with?, choose
Hello World Starter
. - For Which template would you like to use?, choose
Worker + Durable Objects
. - For Which language do you want to use?, choose
TypeScript
. - For Do you want to use git for version control?, choose
Yes
. - For Do you want to deploy your application?, choose
No
(we will be making some changes before deploying).
- For What would you like to start with?, choose
-
Change into your new project directory to start developing:
cd seat-booking
The frontend of the application is a simple HTML page that allows users to select a seat and enter their name. The application uses Workers Static Assets to serve the frontend.
-
Create a new directory named
public
in the project root. -
Create a new file named
index.html
in thepublic
directory. -
Add the following HTML code to the
index.html
file:
public/index.html
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Flight Seat Booking</title> <style> body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } .booking-container { background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } .seat-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; margin-top: 20px; } .aisle { grid-column: 4; } .seat { width: 40px; height: 40px; display: flex; justify-content: center; align-items: center; border: 1px solid #ccc; cursor: pointer; } .seat.available { background-color: #5dbf61ba; color: white; } .seat.unavailable { background-color: #f4433673; color: white; cursor: not-allowed; } .airplane { display: flex; flex-direction: column; align-items: center; background-color: #f0f0f0; padding: 20px; border-radius: 20px; } </style> </head> <body> <div class="booking-container"> <h2 id="title"></h2> <div class="airplane"> <div id="seatGrid" class="seat-grid"></div> </div> </div>
<script> const seatGrid = document.getElementById("seatGrid"); const title = document.getElementById("title");
const flightId = window.location.search.split("=")[1];
const hostname = window.location.hostname;
if (flightId === undefined) { title.textContent = "No Flight ID provided"; seatGrid.innerHTML = "<p>Add `flightId` to the query string</p>"; } else { handleBooking(); }
function handleBooking() { let ws; if (hostname === 'localhost') { const port = window.location.port; ws = new WebSocket(`ws://${hostname}:${port}/ws?flightId=${flightId}`); } else { ws = new WebSocket(`wss://${hostname}/ws?flightId=${flightId}`); }
title.textContent = `Book seat for flight ${flightId}`;
ws.onopen = () => { console.log("Connected to WebSocket server"); };
function createSeatGrid(seats) { seatGrid.innerHTML = ""; for (let row = 1; row <= 10; row++) { for (let col = 0; col < 6; col++) { if (col === 3) { const aisle = document.createElement("div"); aisle.className = "aisle"; seatGrid.appendChild(aisle); }
const seatNumber = `${row}${String.fromCharCode(65 + col)}`; const seat = seats.find((s) => s.seatNumber === seatNumber); const seatElement = document.createElement("div"); seatElement.className = `seat ${seat && seat.occupant ? "unavailable" : "available"}`; seatElement.textContent = seatNumber; seatElement.onclick = () => bookSeat(seatNumber); seatGrid.appendChild(seatElement); } } }
async function fetchSeats() { const response = await fetch(`/seats?flightId=${flightId}`); const seats = await response.json(); createSeatGrid(seats); }
async function bookSeat(seatNumber) { const name = prompt("Please enter your name:"); if (!name) { return; // User canceled the prompt }
const response = await fetch(`book-seat?flightId=${flightId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ seatNumber, name }), }); const result = await response.text(); fetchSeats(); }
ws.onmessage = (event) => { try { const seats = JSON.parse(event.data); createSeatGrid(seats); } catch (error) { console.error("Error parsing WebSocket message:", error); } };
ws.onerror = (error) => { console.error("WebSocket error:", error); };
ws.onclose = (event) => { console.log("WebSocket connection closed:", event); };
fetchSeats(); } </script> </body>
</html>