Skip to content

Commit

Permalink
feat: improve context API
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Jul 17, 2024
1 parent 2ceec2e commit d388765
Show file tree
Hide file tree
Showing 10 changed files with 570 additions and 111 deletions.
135 changes: 104 additions & 31 deletions _examples/server.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,119 @@
// Copyright 2018-2024 the oak authors. All rights reserved.

import * as colors from "jsr:@std/fmt@0.225/colors";
import { KeyStack } from "jsr:@std/crypto@0.224/unstable-keystack";
import * as v from "@valibot/valibot";
import { Router, Status, v } from "../mod.ts";
import { assert } from "@oak/commons/assert";

import { Router } from "../router.ts";
import { Status, STATUS_TEXT } from "jsr:@oak/commons@^0.12/status";
const router = new Router({ logger: { console: true } });

const keys = new KeyStack(["super secret"]);
const db = await Deno.openKv();

const router = new Router({
keys,
logger: { console: { level: "DEBUG" } },
const book = v.object({
author: v.string(),
title: v.string(),
});

const bookList = v.array(book);

type Book = v.InferOutput<typeof book>;

const bookPatch = v.object({
author: v.optional(v.string()),
title: v.optional(v.string()),
});

router.get("/", async (ctx) => {
let count = parseInt((await ctx.cookies.get("count")) ?? "0");
await ctx.cookies.set("count", String(++count));
const count = parseInt(await ctx.cookies.get("count") ?? "0", 10) + 1;
await ctx.cookies.set("count", String(count));
return { hello: "world", count };
}, {
schema: {
querystring: v.object({
a: v.optional(v.string()),
b: v.optional(v.string()),
}),
response: v.object({ hello: v.number(), count: v.number() }),
},
});
router.get("/error", () => {
throw new Error("test");
});

router.on(Status.NotFound, () => {
return Response.json({ message: "Not Found" }, {
status: Status.NotFound,
statusText: STATUS_TEXT[Status.NotFound],
router.get("/redirect", (ctx) => {
return ctx.redirect("/book/:id", {
params: { id: "1" },
status: Status.TemporaryRedirect,
});
});

router.listen({
port: 3000,
onListen({ hostname, port }) {
console.log(`Listening on ${colors.yellow(`http://${hostname}:${port}/`)}`);
},
router.get("/book", async () => {
const books: Book[] = [];
const bookEntries = db.list<Book>({ prefix: ["books"] });
for await (const { key, value } of bookEntries) {
if (key[1] === "id") {
continue;
}
console.log(key, value);
books.push(value);
}
return books;
}, { schema: { response: bookList } });

router.get("/book/:id", async (ctx) => {
const id = parseInt(ctx.params.id, 10);
const maybeBook = await db
.get<Book>(["books", id]);
if (!maybeBook.value) {
ctx.throw(Status.NotFound, "Book not found");
}
return maybeBook.value;
}, { schema: { response: book } });

router.post("/book", async (ctx) => {
const body = await ctx.body();
assert(body, "Body required.");
const idEntry = await db.get<number>(["books", "id"]);
const id = (idEntry.value ?? 0) + 1;
const result = await db.atomic()
.check({ key: ["books", "id"], versionstamp: idEntry.versionstamp })
.set(["books", "id"], id)
.set(["books", id], body)
.commit();
if (!result.ok) {
ctx.throw(Status.InternalServerError, "Conflict updating the book id");
}
return ctx.created(body, {
location: `/book/:id`,
params: { id: String(id) },
});
}, { schema: { body: book, response: book } });

router.put("/book/:id", async (ctx) => {
const body = await ctx.body();
const id = parseInt(ctx.params.id, 10);
const bookEntry = await db.get<Book>(["books", id]);
if (!bookEntry.value) {
ctx.throw(Status.NotFound, "Book not found");
}
const result = await db.atomic()
.check({ key: ["books", id], versionstamp: bookEntry.versionstamp })
.set(["books", id], body)
.commit();
if (!result.ok) {
ctx.throw(Status.InternalServerError, "Conflict updating the book");
}
return book;
}, { schema: { body: book, response: book } });

router.patch("/book/:id", async (ctx) => {
const body = await ctx.body();
const id = parseInt(ctx.params.id, 10);
const bookEntry = await db.get<Book>(["books", id]);
if (!bookEntry.value) {
ctx.throw(Status.NotFound, "Book not found");
}
const book = { ...bookEntry.value, ...body };
const result = await db.atomic()
.check({ key: ["books", id], versionstamp: bookEntry.versionstamp })
.set(["books", id], book)
.commit();
if (!result.ok) {
ctx.throw(Status.InternalServerError, "Conflict updating the book");
}
return book;
}, { schema: { body: bookPatch, response: book } });

router.delete("/book/:id", async (ctx) => {
const id = parseInt(ctx.params.id, 10);
await db.delete(["books", id]);
});

router.listen({ port: 3000 });
1 change: 1 addition & 0 deletions constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export const BODYLESS_METHODS = ["GET", "HEAD"];
export const CONTENT_TYPE_HTML = contentType("html")!;
export const CONTENT_TYPE_JSON = contentType("json")!;
export const CONTENT_TYPE_TEXT = contentType("text/plain")!;
export const NOT_ALLOWED = Symbol.for("acorn.NotAllowed");
Loading

0 comments on commit d388765

Please sign in to comment.