From ee161d7abf5ec05061fbcb490d1ab2d2fddfa1a9 Mon Sep 17 00:00:00 2001 From: Valentin Knabel Date: Thu, 12 Feb 2026 11:02:11 +0100 Subject: [PATCH] docs(examples): typescript / javascript --- examples/README.md | 3 +++ examples/js/bun.lock | 33 ++++++++++++++++++++++++ examples/js/ip-list.ts | 55 ++++++++++++++++++++++++++++++++++++++++ examples/js/package.json | 22 ++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 examples/js/bun.lock create mode 100644 examples/js/ip-list.ts create mode 100644 examples/js/package.json diff --git a/examples/README.md b/examples/README.md index e75f4f33..1799a9d7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -21,4 +21,7 @@ sh curl/ip-list.sh # Python: python python/ip-list.py + +# TypeScript / JavaScript: +cd js && bun i && bun run ip-list.ts ``` diff --git a/examples/js/bun.lock b/examples/js/bun.lock new file mode 100644 index 00000000..2685540c --- /dev/null +++ b/examples/js/bun.lock @@ -0,0 +1,33 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "metal-stack-api-example", + "dependencies": { + "@connectrpc/connect": "^2.1.1", + "@connectrpc/connect-web": "^2.1.1", + "@metal-stack/api": "^0.0.47", + }, + "devDependencies": { + "@types/node": "^25.2.3", + "typescript": "^5.9.3", + }, + }, + }, + "packages": { + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.11.0", "", {}, "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ=="], + + "@connectrpc/connect": ["@connectrpc/connect@2.1.1", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.7.0" } }, "sha512-JzhkaTvM73m2K1URT6tv53k2RwngSmCXLZJgK580qNQOXRzZRR/BCMfZw3h+90JpnG6XksP5bYT+cz0rpUzUWQ=="], + + "@connectrpc/connect-web": ["@connectrpc/connect-web@2.1.1", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.7.0", "@connectrpc/connect": "2.1.1" } }, "sha512-J8317Q2MaFRCT1jzVR1o06bZhDIBmU0UAzWx6xOIXzOq8+k71/+k7MUF7AwcBUX+34WIvbm5syRgC5HXQA8fOg=="], + + "@metal-stack/api": ["@metal-stack/api@0.0.47", "", { "dependencies": { "@bufbuild/protobuf": "^2.11.0", "@connectrpc/connect-web": "^2.1.1" } }, "sha512-Tnzyg/vqry9y1jpgKe3Ignvtmc5777ruk7d6ufRFRUf2WDBYc2uffw9mgf8RtzJYZltbyxullp+WtqdCvEB2SA=="], + + "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + } +} diff --git a/examples/js/ip-list.ts b/examples/js/ip-list.ts new file mode 100644 index 00000000..5fcc8131 --- /dev/null +++ b/examples/js/ip-list.ts @@ -0,0 +1,55 @@ +import * as apiv2 from "@metal-stack/api/js/metalstack/api/v2/ip_pb"; +import { createClient } from "@connectrpc/connect"; +import { createConnectTransport } from "@connectrpc/connect-web"; + +import { Code, ConnectError, Interceptor } from "@connectrpc/connect"; + +class AuthInterceptor { + private authToken: string; + + constructor(authToken: string) { + this.authToken = authToken; + } + + interceptor: Interceptor = (next) => async (req) => { + if (!this.authToken) { + throw new ConnectError("Missing auth token", Code.Unauthenticated); + } + + req.header.append("Authorization", `Bearer ${this.authToken}`); + + try { + const res = await next(req); + return res; + } catch (e) { + if (e instanceof ConnectError && e.code === Code.Unauthenticated) { + // e.g. message: "token has expired" + console.error("unauthenticated", e); + } + throw e; + } + }; +} + +async function main() { + const token = process.env["API_TOKEN"]; + const project = process.env["PROJECT_ID"]; + const baseUrl = process.env["METAL_APISERVER_URL"]; + + const auth = new AuthInterceptor(token!); + const client = createClient( + apiv2.IPService, + createConnectTransport({ + baseUrl: baseUrl!, + interceptors: [auth.interceptor], + }), + ); + + const listResp = await client.list({ project }); + + for (const ip of listResp.ips) { + console.log("ip", ip); + } +} + +main(); diff --git a/examples/js/package.json b/examples/js/package.json new file mode 100644 index 00000000..e6925558 --- /dev/null +++ b/examples/js/package.json @@ -0,0 +1,22 @@ +{ + "name": "metal-stack-api-example", + "version": "1.0.0", + "private": true, + "description": "@metal-stack/api example", + "main": "ip-list.ts", + "scripts": { + "build": "tsc --project tsconfig.json", + "ip-list": "npm run build && node ./ip-list.js" + }, + "author": "metal-stack", + "license": "MIT", + "dependencies": { + "@connectrpc/connect": "^2.1.1", + "@connectrpc/connect-web": "^2.1.1", + "@metal-stack/api": "^0.0.47" + }, + "devDependencies": { + "@types/node": "^25.2.3", + "typescript": "^5.9.3" + } +}