Simple API gateway for webhooks

save data to deno kv

finxol.io 1b6bfad6 d71ba0ab

verified
Changed files
+104 -11
src
+2 -1
deno.json
···
{
"imports": {
"@hono/ua-blocker": "npm:@hono/ua-blocker@^0.1.9",
-
"hono": "npm:hono@^4.9.6"
+
"hono": "npm:hono@^4.9.6",
+
"zod": "npm:zod@^4.1.5"
},
"tasks": {
"start": "deno run --allow-net src/main.ts"
+7 -2
deno.lock
···
"specifiers": {
"npm:@hono/ua-blocker@~0.1.9": "0.1.9_hono@4.9.6",
"npm:@types/node@*": "24.2.0",
-
"npm:hono@^4.9.6": "4.9.6"
+
"npm:hono@^4.9.6": "4.9.6",
+
"npm:zod@^4.1.5": "4.1.5"
},
"npm": {
"@hono/ua-blocker@0.1.9_hono@4.9.6": {
···
},
"undici-types@7.10.0": {
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="
+
},
+
"zod@4.1.5": {
+
"integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg=="
}
},
"workspace": {
"dependencies": [
"npm:@hono/ua-blocker@~0.1.9",
-
"npm:hono@^4.9.6"
+
"npm:hono@^4.9.6",
+
"npm:zod@^4.1.5"
]
}
}
+95 -8
src/sensors.ts
···
import { Hono } from "hono"
+
import { validator } from "hono/validator"
+
import { z } from "zod"
-
const sensors = new Hono().get("/get", (c) => c.text("Hello World!")).post(
-
"/set",
-
async (c) => {
-
const body = await c.req.text()
-
console.log(body)
-
return c.text(`Received: ${body}`)
-
},
-
)
+
const SensorsSchema = z.object({
+
sensorData: z.array(
+
z.discriminatedUnion("sensorType", [
+
z.object({
+
timestamp: z.string(),
+
sensorType: z.literal("location"),
+
data: z.object({
+
altitude: z.number(),
+
speed: z.number(),
+
horizontalAccuracy: z.number(),
+
verticalAccuracy: z.number(),
+
longitude: z.number(),
+
course: z.number(),
+
floor: z.number().nullable(),
+
latitude: z.number(),
+
}),
+
}),
+
z.object({
+
timestamp: z.string(),
+
sensorType: z.literal("gyroscope"),
+
data: z.object({
+
x: z.number(),
+
y: z.number(),
+
z: z.number(),
+
}),
+
}),
+
z.object({
+
timestamp: z.string(),
+
sensorType: z.literal("battery"),
+
data: z.object({
+
batteryState: z.string(),
+
batteryLevel: z.number(),
+
}),
+
}),
+
z.object({
+
timestamp: z.string(),
+
sensorType: z.literal("altitude"),
+
data: z.object({
+
altitude: z.number(),
+
}),
+
}),
+
z.object({
+
timestamp: z.string(),
+
sensorType: z.literal("heading"),
+
data: z.object({
+
x: z.number(),
+
y: z.number(),
+
z: z.number(),
+
trueHeading: z.number(),
+
headingAccuracy: z.number(),
+
magneticHeading: z.number(),
+
}),
+
}),
+
]),
+
),
+
isSuccess: z.boolean(),
+
deviceId: z.string(),
+
timestamp: z.coerce.date(),
+
deviceName: z.string(),
+
})
+
+
type Sensors = z.infer<typeof SensorsSchema>
+
+
const kv = await Deno.openKv()
+
+
const sensors = new Hono()
+
.get("/get", async (c) => {
+
const data = await kv.get<Sensors>(["sensors", "latest"])
+
if (!data.value) {
+
return c.text("No data found", 404)
+
}
+
return c.json(data.value)
+
})
+
.post(
+
"/set",
+
validator("json", (value, c) => {
+
const parsed = SensorsSchema.safeParse(value)
+
if (!parsed.success) {
+
return c.text("Invalid!", 401)
+
}
+
return parsed.data
+
}),
+
async (c) => {
+
const body = c.req.valid("json")
+
console.log(`Receiving sensor data at ${body.timestamp.toISOString()}`)
+
+
// save data to kv store
+
await kv.set(["sensors", body.timestamp.toISOString()], body)
+
await kv.set(["sensors", "latest"], body)
+
+
return c.json(body)
+
},
+
)
export { sensors }