The all in one hono api template with prisma, postgresql and minio s3 bucket setup. Includes a CI/CD pipeline to deploy to a VPS via a docker compose.
- Hono -> api framework
- Prisma -> orm
- Postgresql -> database
- Minio -> object storage bucket
- Prettier -> code formatting
- ESLint -> linter and code integrity checker
- Zod -> typescript form validation
- Husky -> pre commit hooks
.
├── docker-compose.yml
├── Dockerfile
├── eslint.config.js
├── init.sh
├── lint-staged.config.js
├── package.json
├── pnpm-lock.yaml
├── prisma
│ ├── migrations
│ │ └── migration_lock.toml
│ └── schema.prisma
├── README.md
├── src
│ ├── index.ts
│ ├── lib
│ │ ├── prisma-client.ts
│ │ └── s3-client.ts
│ ├── routes
│ │ └── auth
│ │ ├── index.ts
│ │ └── routes.ts
│ └── utils
│ └── passwords.ts
└── tsconfig.json
-
Each route should be inside
/src/routes/<route-folder>/
-
Each
/<route-folder>/
should include 2 files-
routes.ts
-
here define the openapi specs for all your routes in this format
export const <route-name> = createRoute({ method: "", path: "/<route-name>", request: { query: z.object({ }), }, responses: { 200: { description: "", content: { "application/json": { schema: z.object({}).openapi("<route-name>Response"), }, }, }, ... }, });
-
-
index.ts
-
import your route specs here and define functionality
const <main-router-name>Router = new OpenAPIHono(); <main-router-name>Router.openapi(<imported-router>, async (ctx) => { const { } = ctx.req.valid("query"); return new Response("<description>", { status: , }); });
-
-
-
include defined router in
/src/index.ts
The project includes a pre-configured Prisma schema for role-based authentication and user management. The schema defines two main models:
- Auth: Handles authentication details.
- id: Unique identifier (UUID).
- email: Unique email for login.
- password: Encrypted password.
- createdAt: Timestamp when the record is created.
- updatedAt: Auto-updated timestamp when the record is modified.
- user: Relation to the User model (optional).
- User: Represents user information and roles.
- id: Unique identifier (UUID).
- authId: Relation to the Auth model (UUID).
- name: User's name.
- role: Enum field representing the user role (ADMIN or USER).
example auth record
{
"id": "uuid",
"email": "[email protected]",
"password": "encrypted-password",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-02T00:00:00.000Z",
"user": {
"id": "uuid",
"authId": "uuid",
"name": "John Doe",
"role": "USER"
}
}
Functions
createDocument(file: File): Promise<string>
Uploads a document to the MinIO server and returns a unique document ID.
- Parameters:
file (File)
: The document to upload.
- Returns:
Promise<string>
: A unique document ID (UUID) associated with the uploaded file.
const file = new File(["Hello, world!"], "example.txt", { type: "text/plain" });
const docId = await createDocument(file);
console.log("Uploaded document ID:", docId);
readDocument(docId: string): Promise<{ data: Buffer; contentType: string; }>
Retrieves a document from the MinIO server using its unique document ID.
-
Parameters:
docId (string)
: The unique ID of the document. -
Returns:
-
Promise<{ data: Buffer; contentType: string; }>
: An object containing:data (Buffer)
: The document's content.contentType (string)
: The MIME type of the document.
const doc = await readDocument(docId); console.log("Document content type:", doc.contentType); console.log("Document data:", doc.data.toString());
-
deleteDocument(docId: string): Promise<void>
Deletes a document from the MinIO server using its unique document ID.
-
Parameters:
docId (string)
: The unique ID of the document to delete.
-
Returns:
-
Promise<void>
: Resolves once the document is successfully deleted.await deleteDocument(docId); console.log("Document deleted successfully");
-
The authentication module provides routes for user login and registration, utilizing JWT for secure token generation and Prisma for database interactions.
POST /login
Authenticates a user and returns a JWT for accessing protected resources.
-
Request Body (JSON)
-
email (string)
: The user's email. Must be a valid email address. -
password (string)
: The user's password. Must be at least 8 characters.{ "email": "[email protected]", "password": "securepassword" }
-
-
Responses
- 200 OK
token
: JWT for authenticated access.userId
: Unique identifier of the authenticated user.
- 404 Not Found: User with the provided email does not exist.
- 403 Forbidden: Password is incorrect.
- 500 Internal Server Error: JWT secret is not set.
- 200 OK
POST /register
Registers a new user by creating entries in the Auth
and User
models.
-
Request Body (JSON)
-
email (string)
: The user's email. Must be a valid email address. -
password (string)
: The user's password. Must be at least 8 characters. -
name (string)
: Full name of the user. -
role (string)
: Role of the user (ADMIN or USER).{ "name": "John Doe", "role": "USER", "email": "[email protected]", "password": "securepassword" }
-
-
Responses
- 201 Created
message
: Confirmation of successful registration.user
: Basic user details (id and email).
- 409 Conflict: User with the provided email already exists.
- 201 Created
- Create the .env file
JWT_SECRET=
POSTGRES_DB=
DATABASE_URL=postgres://postgres:postgres@localhost:5432/${POSTGRES_DB}?schema=public
MINIO_USE_SSL=false
MINIO_ROOT_USER=
MINIO_ROOT_PASSWORD=
MINIO_ACCESS_KEY= # openssl rand -base64 20
MINIO_SECRET_KEY= # openssl rand -base64 40
MINIO_BUCKET=
- run
pnpm install
- run
pnpm run dev
- Create the
.env
file
JWT_SECRET=
POSTGRES_DB=
POSTGRES_USER=
POSTGRES_PASSWORD=
DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
MINIO_USE_SSL=false
MINIO_ROOT_USER=
MINIO_ROOT_PASSWORD=
MINIO_ACCESS_KEY= # openssl rand -base64 20
MINIO_SECRET_KEY= # openssl rand -base64 40
MINIO_BUCKET=
HONO_PORT=
MINIO_API_PORT=
MINIO_WEB_PORT=
POSTGRES_PORT=
The project comes with a docker compose file that can be put up on any container management provider or just straight docker compose up -d
- Set up a self hosted github runner and follow the instructions
- Set up repository environment variables
- copy the entire contents of
.env
into a singleENV
titled repo environment variable, not individual variables (referenced hereecho "${{ secrets.ENV }}" > .env
which creates the env file on VPS for docker compose)
- copy the entire contents of
Now it should auto deploy every time something is pushed