Building QRQuick: Laying the Foundation and Setting the Course
I recently reached out on my social networks to gauge interest in developing an application for generating and managing QR codes. Surprisingly, the response was quite positive, with more engagement than I initially anticipated. Despite being on vacation, I'm enthusiastic about diving into this project and laying down its foundations.
As with my previous projects, I intend to document the entire process, but with a twist this time: breaking down the blog posts according to the project's progress. In this initial post, I aim to discuss the application's structural planning, the frameworks and libraries it will utilize, and establish some fundamental goals and features.
First things first, let's decide on a name for the application. After considering options provided by ChatGPT, I narrowed it down to QRify and QRQuick. While QRify seemed popular, I opted for QRQuick due to its uniqueness.
While graphic design and vector illustration aren't my forte, I recognize the importance of having high-quality assets for the project, especially the app icon. Therefore, I'll be reaching out to my skilled friends @mn519 and @nishfn for assistance with graphics. To give them a starting point and convey my vision for the logo, I used the AI tool at hotpot.ai to generate a minimalist logo. The result, shared below, looks promising and will serve as a helpful reference for them.
With the name and graphics assets sorted, let's delve into the functionality of the application. At this early stage of development, simplicity is our guiding principle. Our primary goal is to provide users with the capability to generate QR codes that link to web addresses. As the project advances, we'll have the opportunity to incorporate upgrades and introduce new features.
I'll be using TypeScript for the entire project. For the frontend, I'll stick with my familiar tools: Vue.js for the framework and Tailwind CSS for styling. I might also incorporate icons from icones.js and Google Fonts. To compile the project into native apps, I'll continue relying on CapacitorJS.
I've opted for Hono as the web framework for the backend. Given Hono's native support for TypeScript, it offers lightweight and speedy performance. Additionally, I've selected MongoDB as the database, with Mongoose serving as the Object Data Modeling (ODM). This combination is well-equipped to provide the necessary flexibility and performance for the application.
In contrast to many of my other projects, we won't be integrating an external authentication library or service; instead, we'll utilize JWT (JSON Web Tokens). This implementation shouldn't pose much difficulty, as Hono offers built-in middleware and helpers for JWT, making the process quite straightforward.
As for QR code generation, we can leverage the Node library qrcode. While it's fully capable of operating on the server side, I prefer generating QR codes directly within the browser and storing them as base64 images in the database.
Additionally, I'll need to establish a mechanism for generating custom URLs on my domain to direct users to their intended destinations. This could entail utilising either path or query parameters. Such a setup will facilitate tracking analytics by recording user interactions with the links decoded from scanned QR codes.
Now, shifting our focus to the database structure and its constituent collections, we'll begin with the collection dedicated to storing authentication data for users. Each user document will predominantly include the fields username and password hash. It's essential that usernames are unique and represent valid email addresses, as my intention is to preserve minimal user information connecting individuals to the data stored within our system.
Next, we'll have a collection dedicated to QR codes. This collection will comprise documents with fields including an alphanumeric ID for the QR code, base64 data for the image, and the destination URL.
Lastly, we'll implement a collection for analytics purposes. This collection will store documents containing fields such as the QR code ID, along with a date and time field represented by a UNIX timestamp indicating when the link was accessed.
That's the extent of my current database planning. Plans are always subject to refinement, so there may be minor adjustments, but this forms the foundation. Now, let's discuss the hosting environment. I'm still contemplating whether to include a web application alongside the native apps. Perhaps you could help me decide. If I do decide to include a web application, I'll likely host it on CloudFlare Pages.
For the backend, one option is to host it as a CloudFlare worker. However, this would preclude the use of Mongoose as the ODM, necessitating the use of MongoDB's REST API and hosting the database elsewhere, possibly on Mongo Atlas. Given cost considerations and the desire to minimize complexity, I'm leaning towards hosting both the NodeJS application and MongoDB on a VPS, possibly on Digital Ocean or Hetzner, as this would be more cost-effective.
To implement an HTTPS layer between the frontend and backend, we'll utilize CloudFlare's reverse proxy. Despite its seeming simplicity, directing the node application with an A record requires a reverse proxy on the VPS itself. This is because an A record cannot be directed to a destination with a defined port. Given my extensive familiarity with Apache over Nginx since my Linux journey began at the age of 11 (feeling a bit old at 33), I'll opt for Apache for this task.
As for the potential drawbacks of employing two reverse proxies and whether utilizing CertBot for SSL implementation would be preferable, I'm uncertain. However, based on my past experiences with similar setups, I haven't encountered any performance issues. Hence, I'm inclined to proceed with this approach. Nevertheless, I'm open to discussing and receiving feedback. For now, let's consider this the plan as we conclude this blog post.
As mentioned, this post outlines the initial plan, subject to improvement. Challenges are inevitable, and our approaches may evolve accordingly. With the project's foundation laid, I'll sign off for now. The next post, which I'll commence once back in the office after the 20th, will focus more on completing the backend.