This boilerplate is your one-stop solution for building full-stack, production-ready applications with built-in authentication. Whether you're a solo developer or part of a team, this project ensures a top-notch Developer Experience (DX) and smooth deployment process. So you can focus on what matters like building and shipping features.
Say goodbye to expensive third-party services! This boilerplate comes with a robust, built-in authentication system that supports:
- Google Sign-In
- OTP-based Passwordless Login
- Role-Based Access Permissions
- Organization Management
- Feature Flag Management
- UI Dashboard for Managing Users
It's ready to use out of the box, easily extensible, and gives you complete control over your data and user management.
Omnigate.Dashboard.Demo.mov
- Zero Hassle Dev Mode: Start your app instantly with minimum setup. No CORS issues, no Docker requirement during developmentโjust code and go.
- Preconfigured Scripts: Everything is set up for youโdev, test, build, lint, lint-staged, pre-commit hooksโit just works out of the box.
- Frontend:
- Backend:
- ๐ฅ Hono.js
- PostgreSQL
- Drizzle ORM
- Hono RPC for fully type-safe API calls between frontend and backend.
- Built-in support for:
- Google Sign-In
- OTP-based passwordless login
- Role-Based Access Permissions: Define and enforce permissions based on user roles seamlessly.
- Organization Support: Manage multiple organizations with role-specific access within each organization.
- Feature Flag: Easily manage feature flag via UI Dashboard.
- Built-in Dashboard UI: Manage authentication related stuff using well crafted UI under
/admin
path.
- Dockerized Deployment: Comes with Docker Compose and optimized Dockerfiles, making it a breeze to deploy to platforms like Coolify or Caprover.
- Small Docker Image Size: It only takes up 400 MB for all services (Frontend, Backend, and Nginx).
- No need for paid third-party services for simple tasks like rate limiting, websockets, cron jobs, or authentication. This boilerplate gives you full control of your data and backend services.
- Node.js (v20+)
- pnpm
- PostgreSQL
# Clone this repo
git clone https://github.com/azharalifauzi/omnigate
cd omnigate
# Instal packages
pnpm install
# Copy environment and set values for all of them
cp .env.example .env
You need to install PostgreSQL and create a database first. After that you have to set the DATABASE_URL
inside .env
file.
Important: before run pnpm seed
you have to set values for INITIAL_USER_EMAIL
and INITIAL_USER_NAME
inside .env
file.
# Migrate DB
pnpm migrate
# Seed DB
pnpm seed
pnpm dev
Frontend and backend start together with full type safety and no CORS issues.
pnpm build
Important: Before run the test, you need to create another database specific for testing purpose, so your database that is used for development won't losing the data.
pnpm test:unit
Easily retrieve user information and manage permissions in both client and server components with these utilities.
Use the useUser
hook to access user details and check permissions in client components.
'use client'
import { useUser } from '~/hooks'
function Component() {
const { user, getPermission } = useUser()
const isGranted = getPermission('read:data')
if (!isGranted) {
return null
}
return (
<div>
<div>Name: {user.name}</div>
<div>Email: {user.email}</div>
</div>
)
}
In server components, use the getUserServerSession
utility for similar functionality.
import { getUserServerSession } from '~/utils/server'
function Page() {
const { user, getPermission } = getUserServerSession()
}
You can check whether the feature flag is enabled or not using the getFeatureFlag
function.
function Component() {
const { getFeatureFlag } = useUser()
const canExportPdf = getFeatureFlag('export-pdf')
}
You can also check multiple permissions with helper functions like somePermissions
and everyPermissions
.
import { somePermission, everyPermission } from '~/utils/auth'
function Component() {
const { getPermission } = useUser()
// Will return true if user has one of the permissions
const canReadOrWriteRole = getPermission(
somePermissions(['write:roles', 'read:roles']),
)
// Will return true when user has all the permissions
const canReadAndWriteRole = getPermission(
everyPermissions(['write:roles', 'read:roles']),
)
}
In order to get type safety when fetching data you can use client
and unwrapResponse
fetcher utility.
'use client'
import { useQuery } from '@tanstack/react-query'
import { client, unwrapResponse, QueryKey } from '~/utils/fetcher'
function Component() {
const { data } = useQuery({
queryKey: [QueryKey.Organization],
queryFn: async () => {
const res = client.api.v1.organization.$get({
query: {
page: '1',
},
})
const { data } = await unwrapResponse(res)
return data
},
})
}
generateJsonResponse
, this function will structurize the API response and still give the type safety.
import { generateJsonResponse } from '~/lib/response'
new Hono().get('/user', (c) => {
// It will return this
// {
// statusCode: 200,
// message: 'OK',
// data: {
// id: 1,
// name: 'John'
// }
// }
return generateJsonResponse(c, {
id: 1,
name: 'John',
})
})
ServerError
, this class is extended fromError
class that will help handling error.
import { ServerError } from '~/lib/error'
new Hono().get('/user', () => {
throw new ServerError({
statusCode: 404,
message: 'Failed to get user',
description: 'User is not found',
})
})
Note: If
ServerError
is used together withunwrapResponse
anduseQuery
oruseMutation
from Tanstack Query, it will automatically trigger a toast fromsonner
to give error information to user.
Deploy easily using Docker:
# App wil run on port 3000
docker-compose up --build
This project is licensed under the MIT License.