1src4 collapsed lines2├── configure3│ └── openapi4│ ├── setup-openapi.ts5│ └── setup-scalar-docs.ts6├── controllers7 collapsed lines7│ ├── employees8│ │ ├── delete.ts9│ │ ├── get.ts10│ │ ├── index.ts11│ │ ├── post.ts12│ │ └── put.ts13│ ├── healthz.ts14│ └── overtimes15│ ├── get.ts16│ ├── index.ts17│ └── post.ts45 collapsed lines18├── global.d.ts19├── index.ts20├── repositories21│ ├── employees22│ │ ├── creates.ts23│ │ ├── finds.ts24│ │ ├── index.ts25│ │ ├── removes.ts26│ │ └── updates.ts27│ ├── overtimes28│ │ ├── creates.ts29│ │ ├── finds.ts30│ │ ├── index.ts31│ │ ├── removes.ts32│ │ └── updates.ts33│ └── prisma.ts34├── schema35│ ├── branded.ts36│ ├── employee.ts37│ ├── employeeWithRelations.ts38│ ├── general.ts39│ ├── helpers.ts40│ ├── index.ts41│ ├── overtime.ts42│ └── overtimeWithRelations.ts43├── services44│ ├── employee45│ │ ├── create.ts46│ │ ├── finds.ts47│ │ ├── index.ts48│ │ ├── removes.ts49│ │ └── updates.ts50│ └── overtime51│ ├── creates.ts52│ ├── finds.ts53│ ├── index.ts54│ ├── removes.ts55│ └── updates.ts56└── types57 ├── repositories58 │ ├── employee.ts59 │ └── overtime.ts60 └── services61 ├── employee.ts62 └── overtime.ts
1import type { OvertimeService } from "../../types/services/overtime.js"2import { Hono } from "hono"3import { describeRoute } from "hono-openapi"4import { resolver, validator } from "hono-openapi/effect"5import { OvertimeSchema } from "../../schema/index.js"6 7const createOvertimeResponseSchema = OvertimeSchema.Schema.omit("deletedAt")8 9const createOvertimeDocs = describeRoute({10 responses: {11 201: {12 content: {13 "application/json": {14 schema: resolver(createOvertimeResponseSchema),15 },16 },17 description: "Get Employee by EmployeeId",18 },19 },20 tags: ["Overtime"],21})22 23const createOvertimeValidateRequest = validator("json", OvertimeSchema.CreateSchema)24 25export function setupPosts(overtimeService: OvertimeService) {26 const app = new Hono()27 28 app.post("/", createOvertimeDocs, createOvertimeValidateRequest, async (c) => {29 const body = c.req.valid("json")30 const overtime = await overtimeService.create(body)31 32 const resObj = createOvertimeResponseSchema.make(overtime)33 return c.json(resObj, 201)34 })35 36 return app37}
1import type { EmployeeService } from "../../types/services/employee.js"2import type { OvertimeService } from "../../types/services/overtime.js"3import * as S from "effect/Schema"4import { Hono } from "hono"5import { describeRoute } from "hono-openapi"6import { resolver, validator } from "hono-openapi/effect"7import { Branded, EmployeeSchema, Helpers, OvertimeWithRelationsSchema } from "../../schema/index.js"8 9const getManyResponseSchema = S.Array(OvertimeWithRelationsSchema.Schema.omit("deletedAt"))10 11const getManyDocs = describeRoute({12 responses: {13 200: {14 content: {15 "application/json": {16 schema: resolver(getManyResponseSchema),17 },18 },19 description: "Get Overtimes",20 },21 },22 tags: ["Overtime"],23})24 25const getByIdResponseSchema = OvertimeWithRelationsSchema.Schema.omit("deletedAt")26 27const getByIdDocs = describeRoute({28 responses: {29 200: {30 content: {31 "application/json": {32 schema: resolver(getByIdResponseSchema),33 },34 },35 description: "Get Overtime by OvertimeId",36 },37 404: {38 content: {39 "application/json": {40 schema: resolver(S.Struct({41 message: S.Literal("Not Found").pipe(S.optional, S.withConstructorDefault(() => "Not Found" as const)),42 })),43 },44 },45 description: "Overtime Not Found",46 },47 },48 tags: ["Overtime"],49})50 51const validateGetByIdRequest = validator("param", S.Struct({52 overtimeId: Branded.OvertimeIdFromString,53}))54 55const getOvertimeByEmployeeIdResponseSchema = OvertimeWithRelationsSchema.SchemaArray56 57const getOvertimeByEmployeeIdDoc = describeRoute({58 responses: {59 200: {60 content: {61 "application/json": {62 schema: resolver(getOvertimeByEmployeeIdResponseSchema),63 },64 },65 description: "Get Overtime by EmployeeId",66 },67 },68 tags: ["Overtime"],69})70 71const getOvertimeByEmployeeIdValidateRequest = validator("param", S.Struct({72 employeeId: Branded.EmployeeIdFromString,73}))74 75const getOvertimeByDepartmentNameDoc = describeRoute({76 responses: {77 200: {78 content: {79 "application/json": {80 schema: resolver(getOvertimeByEmployeeIdResponseSchema),81 },82 },83 description: "Get Overtime by EmployeeId",84 },85 },86 tags: ["Overtime"],87})88 89const getOvertimeByDepartmentNameValidateRequest = validator("param", S.Struct({90 department: EmployeeSchema.Schema.fields.department,91}))92 93const summaryResponseSchema = S.Struct({94 hoursWorked: S.Number.annotations({ jsonSchema: { example: 8.5, type: "number" } }),95})96 97const overtimeSummaryForEmployeeDoc = describeRoute({98 responses: {99 200: {100 content: {101 "application/json": {102 schema: resolver(summaryResponseSchema),103 },104 },105 description: "Get Overtime by EmployeeId",106 },107 },108 tags: ["Overtime"],109})110 111const validateOvertimeSummaryByEmployeeIdRequest = {112 param: validator("param", S.Struct({113 employeeId: Branded.EmployeeIdFromString,114 })),115 query: validator("query", S.Struct({116 end: S.Date.annotations({ jsonSchema: { example: "2021-01-02", format: "date", type: "string" } }),117 start: S.Date.annotations({ jsonSchema: { example: "2021-01-01", format: "date", type: "string" } }),118 })),119}120 121const overtimeSummaryForDepartmentDoc = describeRoute({122 responses: {123 200: {124 content: {125 "application/json": {126 schema: resolver(summaryResponseSchema),127 },128 },129 description: "Get Overtime by Department",130 },131 },132 tags: ["Overtime"],133})134 135const validateOvertimeSummaryByDepartmentRequest = {136 param: validator("param", S.Struct({137 department: EmployeeSchema.Schema.fields.department,138 })),139 query: validator("query", S.Struct({140 end: S.Date.annotations({ jsonSchema: { example: "2021-01-02", format: "date", type: "string" } }),141 start: S.Date.annotations({ jsonSchema: { example: "2021-01-01", format: "date", type: "string" } }),142 })),143}144 145export function setupGet(overtimeService: OvertimeService, employeeService: EmployeeService) {146 const app = new Hono()147 148 app.get("/", getManyDocs, async (c) => {149 const overtimes = await overtimeService.findMany()150 const resObj = Helpers.fromObjectToSchema(getManyResponseSchema)(overtimes)151 152 return c.json(resObj, 200)153 })154 155 app.get("/:overtimeId", getByIdDocs, validateGetByIdRequest, async (c) => {156 const { overtimeId } = c.req.valid("param")157 158 const overtime = await overtimeService.findById(overtimeId)159 160 if (overtime === null)161 return c.json({ message: "Not Found" }, 404)162 163 return c.json(getByIdResponseSchema.make(overtime), 200)164 })165 166 app.get("/employees/:employeeId", getOvertimeByEmployeeIdDoc, getOvertimeByEmployeeIdValidateRequest, async (c) => {167 const { employeeId } = c.req.valid("param")168 169 const employeeWithRelations = await employeeService.findOneById(employeeId)170 171 if (employeeWithRelations === null)172 return c.json({ message: "Not Found" }, 404)173 174 return c.json(Helpers.fromObjectToSchema(getOvertimeByEmployeeIdResponseSchema)(employeeWithRelations.overtimes), 200)175 })176 177 app.get("/department/:department", getOvertimeByDepartmentNameDoc, getOvertimeByDepartmentNameValidateRequest, async (c) => {178 const { department } = c.req.valid("param")179 const overtimes = await overtimeService.findByDepartmentName(department)180 181 const resObj = Helpers.fromObjectToSchema(getOvertimeByEmployeeIdResponseSchema)(overtimes)182 return c.json(resObj, 200)183 })184 185 app.get("/employees/:employeeId/summary", overtimeSummaryForEmployeeDoc, validateOvertimeSummaryByEmployeeIdRequest.param, validateOvertimeSummaryByEmployeeIdRequest.query, async (c) => {186 const { employeeId } = c.req.valid("param")187 const { end, start } = c.req.valid("query")188 189 const ot = await overtimeService.summaryByEmployeeId({190 dateRange: { end, start },191 employeeId,192 })193 return c.json(summaryResponseSchema.make(ot), 200)194 })195 196 app.get("/department/:department/summary", overtimeSummaryForDepartmentDoc, validateOvertimeSummaryByDepartmentRequest.param, validateOvertimeSummaryByDepartmentRequest.query, async (c) => {197 const { department } = c.req.valid("param")198 const { end, start } = c.req.valid("query")199 200 const ot = await overtimeService.summaryByDepartment({201 dateRange: { end, start },202 department,203 })204 return c.json(summaryResponseSchema.make(ot), 200)205 })206 207 return app208}
ไม่ได้มีใน requirements ใครจะทำก็ลอง implement ดูได้นะ
เราจะมาทำ function ที่เอา Routes มารวมกัน แบบนี้
1import type { EmployeeService } from "../../types/services/employee.js"2import type { OvertimeService } from "../../types/services/overtime.js"3import { Hono } from "hono"4import * as Gets from "./get.js"5import * as Posts from "./post.js"6 7export function setupOvertimesRoutes(overtimeService: OvertimeService, employeeService: EmployeeService) {8 const app = new Hono()9 10 app.route("/", Posts.setupPosts(overtimeService))11 12 app.route("/", Gets.setupGet(overtimeService, employeeService))13 14 return app15}
32 collapsed lines1import { config } from "@dotenvx/dotenvx"2import { serve } from "@hono/node-server"3import { Hono } from "hono"4import { setupOpenApi } from "./configure/openapi/setup-openapi.js"5import { setupScalarDocs } from "./configure/openapi/setup-scalar-docs.js"6import * as EmployeeControllers from "./controllers/employees/index.js"7import healthzApp from "./controllers/healthz.js"8import * as OvertimeControllers from "./controllers/overtimes/index.js"9import initEmployeeRepository from "./repositories/employees/index.js"10import { initOvertimeRepository } from "./repositories/overtimes/index.js"11import prismaClient from "./repositories/prisma.js"12 13import { initEmployeeService } from "./services/employee/index.js"14import { initOvertimeService } from "./services/overtime/index.js"15 16config()17 18const app = new Hono()19setupOpenApi(app)20 21app.route("/docs", setupScalarDocs())22 23app.route("/healthz", healthzApp)24 25const employeeRepository = initEmployeeRepository(prismaClient)26const employeeService = initEmployeeService(employeeRepository)27app.route("/employees", EmployeeControllers.setupEmployeeRoutes(employeeService))28 29app.get("/", (c) => {30 return c.text("Home")31})32 33const overtimeRepository = initOvertimeRepository(prismaClient)34const overtimeService = initOvertimeService(overtimeRepository)35app.route("/overtimes", OvertimeControllers.setupOvertimesRoutes(overtimeService, employeeService))36 7 collapsed lines37const port = 300038console.log(`Server is running on http://localhost:${port}`)39 40serve({41 fetch: app.fetch,42 port,43})
ลองเปิด Scalar ดู