- Files
- Always use kebab-case.
- Performing Queries and Mutations
- Use
import { honoClient } from '@/lib/hono-client'for most hono api calls. - Use useQuery, don't destructure:
const somethingQuery = useQuery(...)is preferred overconst { data } = useQuery(...). Remembercreateprefix in solid-js is deprecated so always use useQuery or useMutation.- We also prefer
somethingQueryandsomethingMutationnaming convention.
- We also prefer
!response.okis handled and thrown by honoClient's internal fetch call, so no need to manually check this.- Thrown errors have a standard output:
- error.message will always contain a readable error message that can be rendered in toasts or UI.
- error.cause can contain
{ data: any, error: Error }type withdatabeing any type of data that might be relevant to the operation. (Almost always not needed, but we have that option to do that)
- Components
We use a bunch under src/components/ui for Shadcn (the standard location), if the UI can use that, it's preferred. With some enhancements too. i.e.
- Button has a
loadingprop. - For link buttons, use ... will work.
- Adding Icons
Powered by iconmate, a useful CLI for adding icons. Do iconmate --help for more info. For agentic use:
bun iconmate add- to add icons to the project into src/assets/iconsbun iconmate iconify- for commands to search for icons from iconify (essentially iconify API as a CLI).- For humans, if you want the TUI, use
iconmate
- Creating backend modules.
- Almost always create 4 files in modules:
-
server/modules/<module>a folder -
server/modules/<module>/<module>.controller.tsthe controller layerconst moduleService = new ModuleService(); export const moduleController = new Hono() .get("/", async () => {}) .post("/", async () => {}) .put("/", async () => {}) .delete("/", async () => {});
-
server/modules/<module>/<module>.service.tsthe business logic layerexport class ModuleService { private moduleDAO: ModuleDAO; // or more if needed constructor() { this.moduleDAO = new ModuleDAO(); } async serviceFn() {} }
-
server/modules/<module>/<module>.dao.tsthe data-access layer that talks to the databaseexport class ModuleDAO { async getRecord() {} async updateRecord() {} }
-
server/modules/<module>/<module>.dto.tsshared Zod schemas for input/output validation and type inference Make sure to always add a type under each DTO with the same name in Pascal Case.export const recordDTO = z.object(...) export type RecordDTO = z.infer<typeof recordDTO>; export const updateRecordParamsDTO = z.object(...) export type UpdateRecordParamsDTO = z.infer<typeof updateRecordParamsDTO>;
-