https://github.com/nktnet1/rt-stack
Turborepo for modular components, shared configs and full type-safety.
https://github.com/nktnet1/rt-stack
better-auth drizzle-orm hono react shadcn-ui tailwind tanstack template trpc
Last synced: 3 months ago
JSON representation
Turborepo for modular components, shared configs and full type-safety.
- Host: GitHub
- URL: https://github.com/nktnet1/rt-stack
- Owner: nktnet1
- License: mit
- Created: 2025-02-13T09:44:38.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-02-19T14:04:11.000Z (about 1 year ago)
- Last Synced: 2025-02-19T14:22:22.637Z (about 1 year ago)
- Topics: better-auth, drizzle-orm, hono, react, shadcn-ui, tailwind, tanstack, template, trpc
- Language: TypeScript
- Homepage:
- Size: 649 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
RT Stack
A modern & lightweight [turborepo](https://turbo.build/repo/docs) template for
fullstack projects with modular components, shared configs, containerised
deployments and 100% type-safety.
- [About](#about)
- [Stack overview](#stack-overview)
- [Base Functionalities](#base-functionalities)
- [Inspirations & Goals](#inspirations--goals)
- [Quick Start](#quick-start)
- [Prerequisites](#prerequisites)
- [Setup](#setup)
- [Using an External Database](#using-an-external-database)
- [Developing](#developing)
- [Working with a single package](#working-with-a-single-package)
- [Adding new shadcn components](#adding-new-shadcn-components)
- [Adding new better-auth plugins](#adding-new-better-auth-plugins)
- [Tooling Scripts](#tooling-scripts)
- [Containerisation (Docker/Podman)](#containerisation-dockerpodman)
- [Deployment](#deployment)
- [Using Containers](#using-containers)
- [Using Major Platforms](#using-major-platforms)
- [Domain Caveat](#domain-caveat)
- [Other Notes](#other-notes)
- [Tanstack Router](#tanstack-router)
- [Server API Artificial Delays](#server-api-artificial-delays)
- [Environment Variables](#environment-variables)
- [Extensions to existing template](#extensions-to-existing-template)
## About
### Stack overview
Below is an overview of all the components in the stack:
```
apps
├─ web
| ├─ react (vite)
| ├─ tanstack (router, query, form)
| └─ tailwindcss
├─ server
| └─ hono (wrapper for api & auth)
packages
├─ api
| └─ orpc with valibot
├─ auth
| └─ better-auth
├─ db
| └─ drizzle-orm (postgres database)
├─ ui
| ├─ tailwindcss
| └─ shadcn & radix ui
tools
├─ eslint
├─ prettier
├─ tailwind
└─ typescript
```
View all catalog dependencies in [pnpm-workspace.yaml](pnpm-workspace.yaml).
### Base Functionalities
The following features are implemented out-of-the-box:
- login/register (using [better-auth email/password](https://www.better-auth.com/docs/authentication/email-password)) credentials provider
- themes (dark/light mode using [next-themes](https://github.com/pacocoursey/next-themes))
- web/server integration ([oRPC](https://orpc.unnoq.com/docs/getting-started) API example for creating/listing posts)
You can visit the [live demo](https://rtstack.nktnet.uk) to see these features in action.
### Inspirations & Goals
Many aspects of the RT Stack were derived from the
[t3-oss/create-t3-turbo](https://github.com/t3-oss/create-t3-turbo). However,
there is a preference for:
- [tanstack-router](https://tanstack.com/router/latest) (web) + [hono](https://hono.dev) (server) instead of [nextjs](https://nextjs.org) (fullstack)
- [better-auth](https://www.better-auth.com) for authentication instead of [auth.js (next-auth)](https://authjs.dev)
- [orpc](https://orpc.unnoq.com) with openapi support instead of [trpc](https://trpc.io)
- [valibot](https://valibot.dev) for input validation instead of [zod](https://zod.dev)
- [tanstack-form](https://tanstack.com/form/latest) for type-safe forms instead of [react-hook-form](https://react-hook-form.com)
- using `.env` in each application/package instead of globally, as per [turborepo's recommendations](https://turbo.build/repo/docs/crafting-your-repository/using-environment-variables#best-practices)
## Quick Start
### Prerequisites
Ensure the following tools are available on your system:
1. [node](https://nodejs.org/en/download) (version 24+)
1. [pnpm](https://pnpm.io/installation) (version 10+)
1. [postgres](https://www.postgresql.org) database, which you can easily run using tools like:
- [docker](https://docs.docker.com/engine/install) and [docker-compose](https://docs.docker.com/compose)
- [podman](https://podman.io/docs/installation) and [podman-compose](https://github.com/containers/podman-compose)
- [supabase](https://supabase.com)'s free tier cloud database
### Setup
```bash
# Create a repository using the rt-stack template (replace YOUR_PROJECT)
pnpm dlx create-turbo@latest -m pnpm -e https://github.com/nktnet1/rt-stack YOUR_PROJECT
# Enter the directory or open in your IDE (replace YOUR_PROJECT)
cd YOUR_PROJECT
# Install all dependencies for apps and packages
pnpm install
# Copy .env.example to .env for all applications and the @repo/db package
pnpm env:copy-example
# Start a local postgres instance in the background (e.g. using docker)
docker compose up db --detach
# Push the drizzle schema to your database
pnpm db:push
```
You can then start all applications with
```bash
pnpm dev
```
By default, the following URLs will be accessible:
- Web (frontend): http://localhost:8085
- Server (backend): http://localhost:3035
- API - OpenAPI reference: http://localhost:3035/api
- Auth - OpenAPI reference: http://localhost:3035/api/auth/reference
The [OpenAPI](https://www.openapis.org) reference uses [Scalar](https://github.com/scalar/scalar) to display all available endpoints.
### Using an External Database
When using an external postgres database (e.g. from [supabase](https://supabase.com)), you can skip the step that spins up a local postgres instance with docker.
Instead, you will need to modify the following environment variables:
1. `SERVER_POSTGRES_URL` in the file `apps/server/.env`
- used at runtime by the backend server in `pnpm dev`
1. `DB_POSTGRES_URL` in the file `packages/db/.env`
- used in database schema migrations with `pnpm db:push`
## Development
### Working with a single package
Use [`pnpm --filter=`](https://pnpm.io/filtering) (where `` is
defined in the `package.json` of each package).
Example usage:
```bash
# Install the nuqs package for our web application:
pnpm --filter=web install nuqs
# Format only the ui package:
pnpm --filter=@repo/ui format
```
You can get a list of all package names using the command below:
```bash
pnpm list --depth -1 --recursive
```
### Adding new shadcn components
To install a single Shadcn/UI component, e.g. `button`, use the command
```bash
pnpm ui-add button
```
You can also open an interactive session to select components using a TUI by not passing any arguments
```bash
pnpm ui-add
```
- press `i` to enter interactive mode on startup
- use `j/k` (or arrow keys) to navigate up and down.
- use `` to toggle select your desired component(s)
- hit `` to install all selected components
### Adding new better-auth plugins
When integrating more better-auth plugins, e.g.
- [admin](https://better-auth.vercel.app/docs/plugins/admin)
- [organization](https://better-auth.vercel.app/docs/plugins/organization)
You should
1. Modify the auth package server and client files in accordance with the plugin's
respective documentations.
2. Run the interactive command:
```bash
pnpm auth:schema:generate
```
Press `i` to enter interactive mode, then `y` to overwrite [packages/db/src/schemas/auth.ts](packages/db/src/schemas/auth.ts).
3. Format and fix all linting issues, e.g. with
```bash
pnpm format:fix
pnpm lint:fix
```
4. Push your new schema to the database
```bash
pnpm db:push
```
5. Occasionally, the type inference will not work immediately in your IDE (e.g. in VSCode).
This can be resolved by running
```bash
pnpm clean && pnpm install
```
followed by a restarting your TS Server or reloading VSCode.
You can find an example in the [better-auth-admin-organization-plugins](https://github.com/nktnet1/rt-stack/tree/better-auth-admin-organization-plugins) branch.
### Tooling Scripts
All scripts are defined in [package.json](package.json) and
[turbo.json](turbo.json):
```bash
pnpm clean # remove all .cache, .turbo, dist, node_modules
pnpm typecheck # report typescript issues
pnpm format # report prettier issues
pnpm format:fix # auto-fix prettier issues
pnpm lint # report eslint issues
pnpm lint:fix # auto-fix eslint issues
pnpx codemod pnpm/catalog # migrate dependencies to pnpm-workspace.yaml
```
## Containerisation (Docker/Podman)
Both the `web` and `server` applications have been containerised. You can start
see this in action by running the commands:
```bash
# Start all applications
docker compose up --build
# Push the drizzle schema to your database. While you can use `pnpm db:push` on
# the host machine if you have installed all the required dependencies, it is
# also possible to do everything within docker alone.
# Open a second terminal and run the command:
docker compose run --build --rm drizzle
# Upon completion, you will be inside the `drizzle` docker container instead
# of the host machine. You can now push the schema with:
node dist/push.mjs
```
You can then open the web link below in your browser:
- http://localhost:8085
Please note that these containers are run in production mode. For further
details, see
- [compose.yaml](compose.yaml)
- [apps/server/Dockerfile](apps/server/Dockerfile)
- [apps/web/Dockerfile](apps/web/Dockerfile)
- [apps/web/nginx.conf](apps/web/nginx.conf)
## Deployment
> [!TIP]
> The [live demo](https://rtstack.nktnet.uk) of RT Stack is currently deployed to
>
> - vercel for the web frontend
> - fly.io for the server backend and postgres database
### Using Containers
You can deploy applications to any services that supports docker deployment.
Using docker compose (see [compose.yaml](compose.yaml)) is also an option,
although this alone may not be production-ready at scale. However, it can be
paired with
- reverse proxies and load balancers offered by tools like
[Traefik](https://github.com/traefik/traefik) or
[Caddy](https://github.com/caddyserver/caddy)
- container orchestration platforms like [Docker Swarm](https://docs.docker.com/engine/swarm) and [Kubernetes](https://kubernetes.io)
Personally, I recommend setting up a Virtual Private Server (e.g. on [Hetzner](https://www.hetzner.com))
and make use of self-hostable PaaS software which automatically handles the complexity of deployment
mentioned above for you - this includes:
- Coolify
- https://github.com/coollabsio/coolify
- https://www.coolify.io
- Dokploy
- https://github.com/Dokploy/dokploy
- http://dokploy.com
Do note that for the **web** application, the `PUBLIC_SERVER_URL` variable
available at build time (as a docker build argument), rather than an environment
variable at runtime.
Also, both the **server** application's `PUBLIC_WEB_URL` and the **web**
application's `PUBLIC_SERVER_URL` needs to be set as internet-accessible URLs
when deployed, e.g. `https://mycompany.com` and `https://api.mycompany.com`,
rather than referencing `http://localhost:8085` like in development.
### Using Major Platforms
The **web** application is a simple React static site powered by Vite, which is
easily deployed to platforms such as GitHub/GitLab pages, Vercel and Netlify.
You can refer to the [vite documentation](https://vite.dev/guide/static-deploy)
for deployment guides on all major platforms.
The **server** application uses the [hono](https://hono.dev) web framework with the [Node.js runtime](https://hono.dev/docs/getting-started/nodejs). However,
this can be exchanged with other runtimes before deploying to your chosen
platforms. For example, deploying to Netlify is covered within
[Hono's documentations](https://hono.dev/docs/getting-started/netlify#_4-deploy).
Specific to deploying the **server** application to vercel, please see [GitHub Discussion #21](https://github.com/nktnet1/rt-stack/discussions/21#discussioncomment-14380642).
### Domain Caveat
When deploying your web frontend and server backend to two different
root domains, you will need to [tweak your better-auth configurations](https://www.better-auth.com/docs/integrations/hono#cross-domain-cookies).
Apple's Safari browser also does not support third-party cookies (and other browsers will soon follow suit), so auth will
not function as expected without any proxy workarounds.
To keep things simple, it is recommended that you host your frontend and
backend on the same root domain and differ by subdomains. For example, the
frontend can be served at either `example.com` or `web.example.com`, and the
backend hosted at `api.example.com`.
## Other Notes
### Tanstack Router
The following is configured in [vite.config.ts](apps/web/vite.config.ts) web
application:
```ts
TanStackRouterVite({
routeToken: 'layout',
}),
```
This enables the use of a `layout.tsx` file in each directory similar to Next.js.
You can read more about this
[here](https://github.com/TanStack/router/discussions/1102#discussioncomment-10946603).
Also, it is recommended that you exclude the `routerTree.gen.ts` from your IDE.
For example, in VSCode, you can add the following `.vscode/settings.json` at the
root of your turborepo:
```json
{
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
}
}
```
### Server API Artificial Delays
There is an artificial delay added in development mode to simulate API usage in
real-world environments. You can disable this by removing the `timingMiddleware`
in [./packages/api/src/server/orpc.ts](./packages/api/src/server/orpc.ts)
### Environment Variables
This template was made to follow the recommendation of
- @tyleralbee in [this turborepo's GitHub discussion](https://github.com/vercel/turborepo/discussions/9458#discussioncomment-11443969)
- @cjkihl in [create-t3-turbo issue #397](https://github.com/t3-oss/create-t3-turbo/issues/397#issuecomment-1630028405)
- turborepo official docs on [environment variables best practices](https://turbo.build/repo/docs/crafting-your-repository/using-environment-variables#best-practices)
In using this template, it is recommended that
1. each application has a local `.env` file instead of a global `.env` at the
root of your repository
1. packages should be pure, i.e. rely on factory methods and receiving inputs to
instantiate rather than consuming environment variables directly
- one exception is the `@repo/db` package, which requires the
`DB_POSTGRES_URL` variable for schema migration with `pnpm db:push`
1. environment variables are prefixed, e.g. `SERVER_AUTH_SECRET` instead of
`AUTH_SECRET`. Caching in the app's `turbo.json` can then be configured to
use wildcards such as:
```json
"tasks": {
"build": {
"env": ["SERVER_*"],
}
}
```
There is also a script that creates a `.env` from `.env.example` of each
app/package, which can be run with:
```bash
# NOTE: This will not overwrite existing local .env files
pnpm env:copy-example
# To reset any modifications to your .env and restore the examples, run:
pnpm env:remove
pnpm env:copy-example
```
It is recommended that any new apps that uses environment variables follow the
example script set in [apps/server/package.json](apps/server/package.json).
Extensions to Existing Template
The table below demonstrates how you can build and extend upon the existing RT Stack template:
Feature
Description
Branch Link
NextJS
Adds a docs application that uses Next.js and Fumadocs, along with the workspace @repo/ui package.
nextjs-fumadocs
Multi-language support
Implements internationalisation support, e.g. switching between English and Vietnamese.
i18n
Better-auth Plugins
Demonstrates how to integrate better-auth plugins in a type-safe and CLI-compatible manner.
better-auth-admin-organization-plugins