// currencyConvert — convert an amount between currencies using a live rate. // // Demonstrates: outbound HTTP with the framework-provided `HttpClient`, // reading the JSON response (`yield* response.json`), transforming it, and a // typed error status when the upstream can't answer (422). Uses a public, // no-key FX endpoint so it runs out of the box. // // curl -X POST http://localhost:8910/ -d '{"from":"USD","to":"EUR","amount":42}' import { Effect, Schema } from 'effect' import { HttpClient, HttpClientRequest } from '@effect/platform' import { defineServerless, ServerlessHttpError } from '@voltro/serverless' const CODE = Schema.String.pipe(Schema.pattern(/^[A-Za-z]{3}$/)) export default defineServerless({ name: 'currency-convert', method: 'POST', input: Schema.Struct({ from: CODE, to: CODE, amount: Schema.Number.pipe(Schema.greaterThanOrEqualTo(0)), }), output: Schema.Struct({ from: Schema.String, to: Schema.String, amount: Schema.Number, rate: Schema.Number, result: Schema.Number, }), runtime: { memoryMb: 128, timeoutSeconds: 10 }, handler: ({ from, to, amount }, _ctx) => Effect.gen(function* () { const base = from.toUpperCase() const quote = to.toUpperCase() const http = yield* HttpClient.HttpClient const response = yield* http.execute(HttpClientRequest.get(`https://open.er-api.com/v6/latest/${base}`)) const body = (yield* response.json) as { rates?: Record } const rate = body.rates?.[quote] if (typeof rate !== 'number') { return yield* Effect.fail( new ServerlessHttpError({ status: 422, message: 'unknown-currency', detail: `no rate ${base}->${quote}` }), ) } return { from: base, to: quote, amount, rate, result: Math.round(amount * rate * 100) / 100 } }), })