visit
As someone who loves cutting-edge technology, I choose to build my first SaaS with a modern tech stack. With the rise of JAMStack and serverless architecture, I created with Next JS static generation for the frontend and the Node.js backend deployed to AWS.
Because I'm a solo full-stack developer, my time and resources are extremely limited. In this article, I'll share all the technologies I use to build my SaaS product: from programming language to development tools. You'll find how I overcome this challenge to build a SaaS as a solo developer.
For building my SaaS, I wrote every line of code in TypeScript. Yes, all the code: Frontend, Backend and also, Infrastructure as code in TypeScript.
Why did I choose TypeScript? It makes the development much more pleasant with strongly-typed and has better integration to IDE. So, if you are still a JavaScript developer, you should give it a try.
For the frontend, I use Next.js. It's a React framework to build a complex application. The good news, Next JS supports TypeScript out-of-the-box.
I use Tailwind CSS styling the React components. As a developer, you usually build an ugly interface. With Tailwind CSS, you can have now build a not so ugly interface even if you aren't a designer.
As a true believer of JAMStack, I have previously taken some time to try Jekyll, Hexo and 11ty for different projects. I choose to build my SaaS in static generated mode using Next JS. So, at build time, all the pages are generated and pre-rendered. Perfect for SEO, cheap hosting, fast, secure, and highly scalable.
I use Cloudflare Pages as a hosting service for the frontend, it's a brand new alternative to Netlify or Vercel. Cloudflare has announced it in December 2020 in beta and released it to the public in April 2021.
There is some small missing feature (nothing big) in Pages. Until the Cloudflare team solve it, I've found temporary workarounds. So, it isn't a big deal.
The good thing about Cloudflare Page is its generous free tier: unlimited bandwidth (Vercel and Netlify are limited to 100GB per month) and you can set up a password-protected website for free (not included for free in Vercel or Netlify).
On the backend side, I've built a REST API with Express.js and Serverless Framework. To support TypeScript in Serverless Framework, I use a serverless-bundle plugin. Express.js needs another plugin to work with Serverless Framework named serverless-http.
For better developer experience, I've also used two other plugins: serverless-dotenv-plugin and serverless-offline. The first plugin is to support dotenv files and the second one is to run Serverless Framework on your local computer.
As a solo developer, I choose serverless architecture for making my life easier with easy deployment, low maintenance, and scalable backend. No need to become a DevOps engineer: no need to SSH, make OS updates, configure proxy/webserver/load balancer/firewall, etc.
The REST API is protected by the IAM authentication. It's AWS built-in feature to secure any AWS resources, in our case, API gateway and AWS lambda. It denies the API invocation when the user isn't connected to the SaaS application. So, when it's protected, external actors won't be able to invoke your resource.
Because the API is deployed to AWS, I choose to use AWS Cognito for authentication. The good thing is that Cognito saves a lot of time by providing everything you need to implement authentication for your SaaS. You get access without any effort to Email authentication and Social sign-in (Facebook, Google, Apple and Amazon).
The connection between AWS Cognito and React frontend is done through AWS Amplify. Amplify provides React components and code for making your frontend integration to AWS easier and faster.
As a solo full-stack developer, I wanted something extremely easy to manage and 100% compatible with serverless. So, I choose DynamoDB as a primary database.
DynamoDB is a NoSQL database fully managed by AWS and I use it to store user states. They almost handle everything and I just need to focus on my code.
AWS gives developers access to AWS CDK where you can define your cloud resources in TypeScript. In one command, you can deploy to your AWS account and get everything provisioned.
Like many developers, I use Git and GitHub for the version control of my code. Many modern hosting services like Vercel and Netlify, Cloudflare pages automatically build and deploy your code at each commit. If you work with Git branches, you can also preview the results without pushing them to production.
For the backend and the infrastructure, I use a third-party service named Seed.run to deploy automatically at each commit. Like the frontend, it also builds and deploys the backend resources on AWS.
As you can doubt, I use Cloudflare for DNS and CDN without any surprise ;) Cloudflare Pages automatically deploy your code in the Cloudflare network, I only need to point my domain to Cloudflare DNS server and they handle the rest. Using Cloudflare, you get plenty of security features like a firewall and a DDoS protection for your SaaS products.
I use Sentry as the error tracking solution. It automatically reports when something goes wrong with useful information like stack trace, breadcrumbs (a trail of events that happened before an issue), browser information, OS information, etc. It makes debugging in production much easier with enriched data:
Sentry is only set up for the frontend and not for the REST API, I keep using the native solution. Indeed, Sentry with AWS lambda creates a lot of overhead and the setup wasn't straightforward. In the next section, you'll find the solution I use for error tracking in the backend.
AWS Lambda automatically sends logs to AWS CloudWatch, so no need to use Sentry. Here is an example of logs stored in CloudWatch:
I also use Lumigo to have additional information for my logging and monitoring. The interface is easier to use compared to Cloudwatch:
You can also enable tracing in Lumigo where you can visualize your AWS service and external API calls. It makes your debugging session easier by letting you know if there is an error in your code or it's from an external service.
Stripe can manage everything I mention in this section, it hides all these complexities and makes the integration to payment easier.