Building a new app is always fun.
The most exciting part is choosing the right tech stack.
I think the best way to think about it is what my goals are. Such as:
-
Who am I building this for?
- A specific demographic
- Myself
-
Why am I building this?
- For profit
- As a learning experience
-
Do I need it to scale?
-
Do I need to be able to build fast?
And it's also a good idea to consider what tech stacks and programming languages I already know.
After giving it some thought, I decided this building this app was mostly for myself. I already had some experience with using the Django framework in Python, but it feels a bit dated to it these days.
This is why I figured it would be ideal to learn Typescript and use modern frameworks such as Nextjs and React. This shouldn't be too hard to learn since I already know how to use Javascript and C++. Having knowledge in a static typing language like C++ should make it easier to understand.
After all I like to think that once you have used a few programming languages, it becomes more about understanding the syntax. The programming logic is still the same fundamentally, just you now are playing by the rules of how the language was created. At the end of the day, most programming languages have things like variables, functions, classes and data structures. I just need to understand how the language handles these concepts.
I also already know how to use PostgreSQL. I did give some thought about using a NoSQL-like database, but it seems like most modern solutions are using a relational database like PostgreSQL so it is best to stick with it.
Auth and database
I did some research and found a few ways to handle authentication and authorisation. Many people use the library NextAuth AKA Auth.js in conjunction with their own database.
However, I found Supabase which handles auth and has uses a PostgreSQL database. This seemed perfect for what I needed.
There's also other solutions like Clerk, but I think I prefer Supabase since I don't have to set up the PostgreSQL database. Besides, I could also export the schema and use a standalone PostgreSQL if I find any issues with Supabase.
Nextjs and handling backend logic
Using Nextjs has been interesting. After doing some research it seems like a lot of people are using Nextjs as a full stack framework. This can be done because Nextjs can directly make request to a backend by either using Server Actions or Route Handlers (Formerly known as API Routes).
With Server Actions, I could set them up to directly interact with Supabase by using their library. This seemed like a good idea, but I slowly realised that when I needed to do more complicated logic, it would start to get a little messy to handle. There's also technical limitations using them. For instance, they always use a POST request; so it is not ideal for fetching data.
Using Route Handlers might be a better since they allow me to configure the type of request; and I can set up the routes to handle more complicated logic. However, I found this not very intuitive since routing in Nextjs is file directory based. This means I have to keep creating new folders and files to set the path of the route.
Furthermore, having all of my server side logic on Nextjs doesn't seem like a good idea since every single action is going to increase the server load.
Enter Nestjs
This is when I decided that it makes a lot more sense to separate the frontend logic and backend logic to keep things more organised and secure.
With this method, I could always swap out the frontend and backend if I wanted to make significant changes.
I thought of a few options. I could use Django REST framework, but I didn't want to go down this route since it felt a little dated.
Using either Spring Boot framework in Java, or .Net Core framework in C# was also on my mind. I have used both of these languages briefly and they are quite similar. I did like how these frameworks organised everything neatly, which makes it easier to understand. However after looking at the documentation for both of these frameworks, it seems like there is a quite a bit to learn on how to use them properly.
After doing more research, I found Nestjs (The naming is a bit confusing...); which seemed like a great middle ground. I could still use Typescript, so I didn't have to keep switching between languages when using Nextjs and Nestjs. Also the design of the framework was apparently modelled after Spring Boot, so it is designed to make developers keep their code tidy.
With Nestjs, I could easily make API endpoints (what Nestjs calls controllers) and have server side logic cleanly defined (what Nestjs calls services).
I could also handle HTTP Requests and Exceptions with ease, and can setup Auth guards to make sure only the user can modify their own account in the database.
Handling Databases
Although Supabase does have a library to modify the database, I found some limitations. In particular, it was not ideal when I had to create new tables using the Supabase Dashboard GUI.
This is when I decided that I need to use Object Relational Mapping (ORM) to make this easier. A lot of people seem to be using Prisma, but I did some research I decided to use Drizzle ORM instead as it seemed like it had better features and was easier to understand on how to use.
Being able to create the database schema with code was a lot better since I could easily see any flaws in my database logic design with code.
With that said, I think it may have been more efficient to define SELECT, INSERT, UPDATE and DELETE functions using the Supabase library instead of Drizzle ORM's library. Since I believe it could be done with less code.
Using an ORM is still my preferred way to handle databases though. Particularly when I need to handle migrations. It just makes things easier.
Summary
I'm pretty happy with my choices in my tech stack. Typescript these days seems to be able to handle most things.
Having said that, in the future I may experiment with building a backend with Spring Boot or .Net Core.
This may be more beneficial since they can perform faster than Node.js. However it may be slower to literally build, depending on how much more extra lines of code is needed.
Perhaps this is a requires weighing the pros and cons of development time vs performance time?