We use TypeScript and Node.js in all our back-end applications. Where possible, we run them in a serverless environment, this includes using Next.js API Routes on Vercel, AWS Lambda for background jobs, and AWS ECS Fargate for APIs.
To handle spikes in traffic and keep services separate, we use queues. This setup helps us scale parts of the system without affecting others. We limit the impact of failures by splitting responsibilities between a control plane and a data plane. The control plane handles requests, and the data plane sends emails.
We currently use Express for APIs. We are moving to Hono for its performance benefits and simplicity.
We use React with Next.js for our dashboard and public website.
For styling we build custom components using Tailwind CSS, Radix Primitives, and Radix Colors.
Since Next.js 15, we primarily use React Server Components to fetch data, while still using SWR for routes that haven't been migrated yet. We also use Zod to parse requests and responses into strongly-typed schemas.
We use Supabase for authentication, which gives us the ability to manage multiple sign-in methods including email/password, and OAuth providers.
We built our own organization model on top of Supabase to allow users to be part of multiple teams.
Our workloads are split between Vercel and AWS.
We choose the best provider for the job depending on the problem we're trying to solve. For example, using Vercel with Next.js allows us to ship features quickly using Preview Deployments.
We use Postgres as our main storage system, and the applications consume data using Drizzle as the ORM/query-builder.
For analytics purposes, we store data in Tinybird so we can get aggregate data for various use cases.
We also have Redis, used for caching data for fast access.
We use a combination of tools to monitor our applications:
We use AWS CDK and Terraform for infrastructure management.
Our CI/CD pipelines are powered by GitHub Actions along with Buildjet as a runner.
We have many background jobs that are triggered from different flows; the goal is to make requests faster by moving non-critical code out of the main path or to perform an action continuously based on some criteria.
Our main tool is Inngest, which provides a great developer experience and helps us visualize and debug background jobs.
Our documentation is written using Markdown and is powered by Mintlify, which lets us focus more on writing the best content we can.
We use Svix for processing webhooks to our users. It's a great tool that helps us monitor and scale webhooks with automatic retries.
We use Vitest as our testing framework for unit testing. We don't enforce a specific test coverage, and we try to be reasonable in balancing the amount of tests we write.
We don't have UI tests or E2E tests in our dashboard, but we know we need them sooner rather than later.