Simple API gateway for webhooks

save data to deno kv

finxol.io 1b6bfad6 d71ba0ab

verified
+104 -11
+2 -1
deno.json
··· 1 1 { 2 2 "imports": { 3 3 "@hono/ua-blocker": "npm:@hono/ua-blocker@^0.1.9", 4 - "hono": "npm:hono@^4.9.6" 4 + "hono": "npm:hono@^4.9.6", 5 + "zod": "npm:zod@^4.1.5" 5 6 }, 6 7 "tasks": { 7 8 "start": "deno run --allow-net src/main.ts"
+7 -2
deno.lock
··· 3 3 "specifiers": { 4 4 "npm:@hono/ua-blocker@~0.1.9": "0.1.9_hono@4.9.6", 5 5 "npm:@types/node@*": "24.2.0", 6 - "npm:hono@^4.9.6": "4.9.6" 6 + "npm:hono@^4.9.6": "4.9.6", 7 + "npm:zod@^4.1.5": "4.1.5" 7 8 }, 8 9 "npm": { 9 10 "@hono/ua-blocker@0.1.9_hono@4.9.6": { ··· 23 24 }, 24 25 "undici-types@7.10.0": { 25 26 "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" 27 + }, 28 + "zod@4.1.5": { 29 + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==" 26 30 } 27 31 }, 28 32 "workspace": { 29 33 "dependencies": [ 30 34 "npm:@hono/ua-blocker@~0.1.9", 31 - "npm:hono@^4.9.6" 35 + "npm:hono@^4.9.6", 36 + "npm:zod@^4.1.5" 32 37 ] 33 38 } 34 39 }
+95 -8
src/sensors.ts
··· 1 1 import { Hono } from "hono" 2 + import { validator } from "hono/validator" 3 + import { z } from "zod" 2 4 3 - const sensors = new Hono().get("/get", (c) => c.text("Hello World!")).post( 4 - "/set", 5 - async (c) => { 6 - const body = await c.req.text() 7 - console.log(body) 8 - return c.text(`Received: ${body}`) 9 - }, 10 - ) 5 + const SensorsSchema = z.object({ 6 + sensorData: z.array( 7 + z.discriminatedUnion("sensorType", [ 8 + z.object({ 9 + timestamp: z.string(), 10 + sensorType: z.literal("location"), 11 + data: z.object({ 12 + altitude: z.number(), 13 + speed: z.number(), 14 + horizontalAccuracy: z.number(), 15 + verticalAccuracy: z.number(), 16 + longitude: z.number(), 17 + course: z.number(), 18 + floor: z.number().nullable(), 19 + latitude: z.number(), 20 + }), 21 + }), 22 + z.object({ 23 + timestamp: z.string(), 24 + sensorType: z.literal("gyroscope"), 25 + data: z.object({ 26 + x: z.number(), 27 + y: z.number(), 28 + z: z.number(), 29 + }), 30 + }), 31 + z.object({ 32 + timestamp: z.string(), 33 + sensorType: z.literal("battery"), 34 + data: z.object({ 35 + batteryState: z.string(), 36 + batteryLevel: z.number(), 37 + }), 38 + }), 39 + z.object({ 40 + timestamp: z.string(), 41 + sensorType: z.literal("altitude"), 42 + data: z.object({ 43 + altitude: z.number(), 44 + }), 45 + }), 46 + z.object({ 47 + timestamp: z.string(), 48 + sensorType: z.literal("heading"), 49 + data: z.object({ 50 + x: z.number(), 51 + y: z.number(), 52 + z: z.number(), 53 + trueHeading: z.number(), 54 + headingAccuracy: z.number(), 55 + magneticHeading: z.number(), 56 + }), 57 + }), 58 + ]), 59 + ), 60 + isSuccess: z.boolean(), 61 + deviceId: z.string(), 62 + timestamp: z.coerce.date(), 63 + deviceName: z.string(), 64 + }) 65 + 66 + type Sensors = z.infer<typeof SensorsSchema> 67 + 68 + const kv = await Deno.openKv() 69 + 70 + const sensors = new Hono() 71 + .get("/get", async (c) => { 72 + const data = await kv.get<Sensors>(["sensors", "latest"]) 73 + if (!data.value) { 74 + return c.text("No data found", 404) 75 + } 76 + return c.json(data.value) 77 + }) 78 + .post( 79 + "/set", 80 + validator("json", (value, c) => { 81 + const parsed = SensorsSchema.safeParse(value) 82 + if (!parsed.success) { 83 + return c.text("Invalid!", 401) 84 + } 85 + return parsed.data 86 + }), 87 + async (c) => { 88 + const body = c.req.valid("json") 89 + console.log(`Receiving sensor data at ${body.timestamp.toISOString()}`) 90 + 91 + // save data to kv store 92 + await kv.set(["sensors", body.timestamp.toISOString()], body) 93 + await kv.set(["sensors", "latest"], body) 94 + 95 + return c.json(body) 96 + }, 97 + ) 11 98 12 99 export { sensors }