Skip to content

Commit 9621215

Browse files
committed
Add OpenAPI Specification
1 parent 6ae2b48 commit 9621215

File tree

4 files changed

+349
-1
lines changed

4 files changed

+349
-1
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ Health Check and Statistics
9696
}
9797
```
9898

99+
### GET `/openapi.json`
100+
101+
OpenAPI specification
102+
99103
### GET `/v1/rates`
100104

101105
Get all exchange rates with USD as base (default)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rabbitforexapi",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"module": "src/index.ts",
55
"type": "module",
66
"private": true,

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { CloudProvider } from "./types";
66
import { logger } from "@rabbit-company/web-middleware/logger";
77
import { cors } from "@rabbit-company/web-middleware/cors";
88
import pkg from "../package.json";
9+
import { openapi } from "./openapi";
910

1011
const host = process.env.SERVER_HOST || "0.0.0.0";
1112
const port = parseInt(process.env.SERVER_PORT || "3000") || 3000;
@@ -70,6 +71,10 @@ app.get("/", (c) => {
7071
);
7172
});
7273

74+
app.get("/openapi.json", (c) => {
75+
return c.json(openapi, 200, { "Cache-Control": "public, max-age=3600 s-maxage=3600 stale-while-revalidate=36000 stale-if-error=31536000" });
76+
});
77+
7378
app.get("/v1/rates", (c) => {
7479
return c.json(
7580
{
@@ -126,6 +131,7 @@ Logger.info(`Server running on http://${host}:${port}`);
126131
Logger.info(`Exchange rates updates every ${updateInterval}s`);
127132
Logger.info("Available endpoints:");
128133
Logger.info(" GET / - Health check and stats");
134+
Logger.info(" GET /openapi.json - OpenAPI specification");
129135
Logger.info(" GET /v1/rates - Exchange rates for USD (default)");
130136
Logger.info(" GET /v1/rates/:asset - Exchange rates for specified asset");
131137
Logger.info(" GET /v1/assets - List all supported currencies and metals");

src/openapi.ts

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
import pkg from "../package.json";
2+
3+
export const openapi = {
4+
openapi: "3.1.1",
5+
info: {
6+
title: "RabbitForexAPI",
7+
description: "Foreign exchange (Forex) and precious metals API",
8+
version: pkg.version,
9+
contact: {
10+
name: "Rabbit Company",
11+
url: "https://rabbit-company.com/contact",
12+
13+
},
14+
license: {
15+
name: "GPL-3.0-only",
16+
url: "https://github.com/Rabbit-Company/RabbitForexAPI/blob/main/LICENSE",
17+
},
18+
},
19+
servers: [
20+
{
21+
url: "https://forex.rabbitmonitor.com",
22+
},
23+
],
24+
tags: [
25+
{
26+
name: "Health",
27+
description: "Health check and statistics endpoints",
28+
},
29+
{
30+
name: "Rates",
31+
description: "Exchange rates and metal prices endpoints",
32+
},
33+
{
34+
name: "Assets",
35+
description: "Supported currencies and metals information",
36+
},
37+
],
38+
paths: {
39+
"/": {
40+
get: {
41+
tags: ["Health"],
42+
summary: "Health check and statistics",
43+
description: "Returns health status and API statistics",
44+
operationId: "getHealth",
45+
responses: {
46+
"200": {
47+
description: "Successful response",
48+
content: {
49+
"application/json": {
50+
schema: {
51+
$ref: "#/components/schemas/HealthResponse",
52+
},
53+
},
54+
},
55+
},
56+
},
57+
},
58+
},
59+
"/v1/rates": {
60+
get: {
61+
tags: ["Rates"],
62+
summary: "Get all exchange rates with USD as base",
63+
description: "Returns all exchange rates with USD as the base currency",
64+
operationId: "getAllRates",
65+
responses: {
66+
"200": {
67+
description: "Successful response",
68+
content: {
69+
"application/json": {
70+
schema: {
71+
$ref: "#/components/schemas/RatesResponse",
72+
},
73+
},
74+
},
75+
},
76+
},
77+
},
78+
},
79+
"/v1/rates/{asset}": {
80+
get: {
81+
tags: ["Rates"],
82+
summary: "Get all exchange rates with specified asset as base",
83+
description: "Returns all exchange rates with the specified currency or metal as base",
84+
operationId: "getRatesByAsset",
85+
parameters: [
86+
{
87+
name: "asset",
88+
in: "path",
89+
required: true,
90+
description: "Currency code (e.g., USD, EUR, JPY) or metal code (e.g., GOLD, SILVER)",
91+
schema: {
92+
type: "string",
93+
example: "EUR",
94+
},
95+
},
96+
],
97+
responses: {
98+
"200": {
99+
description: "Successful response",
100+
content: {
101+
"application/json": {
102+
schema: {
103+
$ref: "#/components/schemas/RatesResponse",
104+
},
105+
},
106+
},
107+
},
108+
},
109+
},
110+
},
111+
"/v1/assets": {
112+
get: {
113+
tags: ["Assets"],
114+
summary: "Get lists of all supported currencies and metals",
115+
description: "Returns all supported currency and metal codes",
116+
operationId: "getAssets",
117+
responses: {
118+
"200": {
119+
description: "Successful response",
120+
content: {
121+
"application/json": {
122+
schema: {
123+
$ref: "#/components/schemas/AssetsResponse",
124+
},
125+
},
126+
},
127+
},
128+
},
129+
},
130+
},
131+
},
132+
components: {
133+
schemas: {
134+
HealthResponse: {
135+
type: "object",
136+
properties: {
137+
program: {
138+
type: "string",
139+
example: "RabbitForexAPI",
140+
},
141+
version: {
142+
type: "string",
143+
example: "2.0.0",
144+
},
145+
sourceCode: {
146+
type: "string",
147+
example: "https://github.com/Rabbit-Company/RabbitForexAPI",
148+
},
149+
monitorStats: {
150+
$ref: "#/components/schemas/MonitorStats",
151+
},
152+
httpStats: {
153+
$ref: "#/components/schemas/HttpStats",
154+
},
155+
lastUpdate: {
156+
type: "string",
157+
format: "date-time",
158+
example: "2025-11-07T07:07:54.995Z",
159+
},
160+
},
161+
required: ["program", "version", "sourceCode", "monitorStats", "httpStats", "lastUpdate"],
162+
},
163+
MonitorStats: {
164+
type: "object",
165+
properties: {
166+
currencyCount: {
167+
type: "integer",
168+
example: 174,
169+
},
170+
metalCount: {
171+
type: "integer",
172+
example: 9,
173+
},
174+
totalAssetCount: {
175+
type: "integer",
176+
example: 183,
177+
},
178+
updateInterval: {
179+
type: "string",
180+
example: "60s",
181+
},
182+
},
183+
required: ["currencyCount", "metalCount", "totalAssetCount", "updateInterval"],
184+
},
185+
HttpStats: {
186+
type: "object",
187+
properties: {
188+
pendingRequests: {
189+
type: "integer",
190+
example: 1,
191+
},
192+
},
193+
required: ["pendingRequests"],
194+
},
195+
RatesResponse: {
196+
type: "object",
197+
properties: {
198+
base: {
199+
type: "string",
200+
description: "Base currency or metal code",
201+
example: "USD",
202+
},
203+
rates: {
204+
type: "object",
205+
additionalProperties: {
206+
type: "number",
207+
},
208+
description: "Exchange rates from base to target assets",
209+
example: {
210+
USD: 1,
211+
EUR: 0.86702,
212+
JPY: 153.4793,
213+
GBP: 0.7624,
214+
CHF: 0.80776,
215+
GOLD: 0.0077614,
216+
SILVER: 0.63833,
217+
PLATINUM: 0.020007,
218+
},
219+
},
220+
timestamps: {
221+
$ref: "#/components/schemas/Timestamps",
222+
},
223+
},
224+
required: ["base", "rates", "timestamps"],
225+
},
226+
AssetsResponse: {
227+
type: "object",
228+
properties: {
229+
currencies: {
230+
type: "array",
231+
items: {
232+
type: "string",
233+
},
234+
description: "List of supported currency codes",
235+
example: ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "EUR", "USD", "GBP", "JPY", "CHF", "CAD"],
236+
},
237+
metals: {
238+
type: "array",
239+
items: {
240+
type: "string",
241+
},
242+
description: "List of supported metal codes",
243+
example: ["ALUMINUM", "COPPER", "GOLD", "LEAD", "NICKEL", "PALLADIUM", "PLATINUM", "SILVER", "ZINC"],
244+
},
245+
timestamps: {
246+
$ref: "#/components/schemas/Timestamps",
247+
},
248+
},
249+
required: ["currencies", "metals", "timestamps"],
250+
},
251+
Timestamps: {
252+
type: "object",
253+
properties: {
254+
metal: {
255+
type: "string",
256+
format: "date-time",
257+
nullable: true,
258+
description: "Last metal data update timestamp",
259+
example: "2025-11-07T07:06:07.016Z",
260+
},
261+
currency: {
262+
type: "string",
263+
format: "date-time",
264+
nullable: true,
265+
description: "Last currency data update timestamp",
266+
example: "2025-11-07T07:06:10.544Z",
267+
},
268+
},
269+
required: ["metal", "currency"],
270+
},
271+
},
272+
responses: {},
273+
parameters: {
274+
AssetParameter: {
275+
name: "asset",
276+
in: "path",
277+
required: true,
278+
description: "Currency or metal code",
279+
schema: {
280+
type: "string",
281+
example: "EUR",
282+
},
283+
},
284+
},
285+
examples: {
286+
USDBaseRates: {
287+
summary: "USD base rates example",
288+
value: {
289+
base: "USD",
290+
rates: {
291+
USD: 1,
292+
EUR: 0.86702,
293+
JPY: 153.4793,
294+
GBP: 0.7624,
295+
CHF: 0.80776,
296+
GOLD: 0.0077614,
297+
SILVER: 0.63833,
298+
PLATINUM: 0.020007,
299+
},
300+
timestamps: {
301+
metal: "2025-11-07T07:06:07.016Z",
302+
currency: "2025-11-07T07:06:10.544Z",
303+
},
304+
},
305+
},
306+
GoldBaseRates: {
307+
summary: "Gold base rates example",
308+
value: {
309+
base: "GOLD",
310+
rates: {
311+
USD: 128.8432,
312+
EUR: 111.7092,
313+
JPY: 19774.7612,
314+
GBP: 98.2304,
315+
GOLD: 1,
316+
SILVER: 82.2438,
317+
PLATINUM: 2.5778,
318+
},
319+
timestamps: {
320+
metal: "2025-11-07T07:06:07.016Z",
321+
currency: "2025-11-07T07:06:10.544Z",
322+
},
323+
},
324+
},
325+
AssetsList: {
326+
summary: "Supported assets example",
327+
value: {
328+
currencies: ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "EUR", "USD", "GBP", "JPY", "CHF", "CAD"],
329+
metals: ["ALUMINUM", "COPPER", "GOLD", "LEAD", "NICKEL", "PALLADIUM", "PLATINUM", "SILVER", "ZINC"],
330+
timestamps: {
331+
metal: "2025-11-07T07:06:07.016Z",
332+
currency: "2025-11-07T07:06:10.544Z",
333+
},
334+
},
335+
},
336+
},
337+
},
338+
};

0 commit comments

Comments
 (0)