Public
Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { EndpointDefinition, getOpenApiSpec } from "https://esm.town/v/xkonti/gptApiSchemaBuilder";
import { HttpVerb } from "https://esm.town/v/xkonti/httpUtils";
import { Hono } from "npm:hono@3";
import { z } from "npm:zod";
export interface ApiInfo {
url: string;
title: string;
description: string;
version: string;
}
export class GptApi {
app = new Hono();
info: ApiInfo;
endpoints: EndpointDefinition[] = [];
constructor(info: ApiInfo) {
this.info = info;
this.app.get("/gpt/schema", async (ctx) => {
const spec = getOpenApiSpec(
this.info.url,
this.info.title,
this.info.description,
this.info.version,
this.endpoints,
);
return ctx.text(JSON.stringify(spec, null, 2));
});
}
/**
* Register endpoint that ignores all inputs and returns a specific JSON response.
*/
nothingToJson<TResponse>(
endpointDef: EndpointDefinition,
handler: (ctx) => Promise<TResponse>,
) {
endpointDef.requestDesc = null;
endpointDef.requestSchema = null;
const handlerWrapper = async (ctx) => {
const response = await handler(ctx);
return ctx.json(response);
};
// TODO: Verify response stuff is in place
this.registerHandler(endpointDef, handlerWrapper);
}
/**
* Register endpoint that accepts a JSON DTO and returns a specific JSON response.
*/
jsonToJson<TRequest, TResponse>(
endpointDef: EndpointDefinition,
handler: (ctx, dto: TRequest) => Promise<TResponse>,
) {
// TODO: Verify request and response is in place
const handlerWrapper = async (ctx) => {
const data = await ctx.req.json() as TRequest;
// TODO: Handle invalid data format
const response = await handler(ctx, data);
return ctx.json(response);
};
this.registerHandler(endpointDef, handlerWrapper);
}
// Registers a handler for a verb + path combo.
registerHandler(
endpointDef: EndpointDefinition,
handler: (ctx) => any,
) {
this.endpoints.push(endpointDef);
const verb = endpointDef.verb;
const path = endpointDef.path;
switch (verb) {
case "GET":
this.app.get(path, handler);
break;
case "POST":
this.app.post(path, handler);
break;
case "PUT":
this.app.put(path, handler);
break;
case "DELETE":
this.app.delete(path, handler);
break;
case "PATCH":
this.app.patch(path, handler);
break;
default:
throw new Error(`HTTP verb ${verb} not supported`);
}
}
/**
* Usage: `export default gptApi.serve();`
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
May 29, 2024
Version 23 changelog: ⚠️ Breaking changes:
nothingToJson
doesn't take the genericTResponse
anymore. The type is inferred from the endpoint definition. The endpoint definition doesn't need therequestSchema
/requestDesc
defined anymore.jsonToJson
doesn't take the genericTRequest
andTResponse
anymore. Types are inferred from the endpoint definition.Other changes:
jsonToNothing
that accepts an input, but can respond only with an HTTP code.Version 24 changelog
⚠️ Breaking change:
jsonToNothing
,nothingToJson
andjsonToJson
don't accept the request/response descriptions. The descriptions are automatically extracted from request/response schemas.Version 25 changelog
The
dto
in request handlers has been renamed toreqBody
.Version 26 changelog
Added config option
policyGetter
which takes a function that returns the privacy policy. This will automatically create an API endpoint/policy
that will return it on demand. The policy getter is a function so that the policy can be imported on-demand.Version 27 changelog
The
policyGetter
can be an async function now. This supports passing it functions like this:Version 29 changelog
If the provided privacy policy ends with
</html>
, the/privacypolicy
endpoint will return it as HTML content instead of plaintext.Version 32 changelog
Version 36 changelog
/
path that deisplays URLs of privacy policy and OpenAPI specVersion 67 changelog