Skip to content

Getting Started

Chris Michael edited this page Oct 6, 2026 · 1 revision

Pudu

Getting Started

Install Pudu 0.1.3 or later, then create a project and add the package:

pudu init my-app
cd my-app
pudu install @chrismichaelps/pudu-lang-httpclient@0.1.0

A first client

A factory holds the configuration of every client a program uses. You describe each client with a builder — Builder.defaults() for what every client shares, Builder.named(name) for one client — and build the factory once at startup. Factory.createClient hands back a cheap client over that name's current handler pipeline. Every request answers an Outcome, which is Result with the error side fixed to Failure:

module Ex01First

import Std.Concurrent as Concurrent
import Std.Http.Server as Server
import Std.Http.Server.Reply as Reply
import Std.Http.Server.Route as Route
import Std.Io as Io
import PuduLangHttpClient as HttpClient
import PuduLangHttpClient.Client as Client
import PuduLangHttpClient.Factory as Factory
import PuduLangHttpClient.Factory.Builder as Builder

fn serve() -> (Server.Running, Str) {
  let running = match Server.start("127.0.0.1", 0) {
    case Ok(found) => found
    case Err(problem) => panic(show(problem))
  }
  let server = Server.server(&Route.routing(&[Route.get("/greeting", |_request: Route.Request| Reply.text(200, "hello"))]))
  let _serving = Concurrent.start(fn() -> () { let _ran = Server.run(&server, &running, 0) })
  (running, "http://127.0.0.1:" + show(Server.portOf(&running)) + "/")
}

export fn main() -> Int {
  let (running, base) = serve()
  let factory = match Factory.build([
      Builder.defaults().withDefaultHeader("user-agent", "first/1.0"),
      Builder.named("greeter").withBaseAddress(base).withTimeout(5000)
    ]) {
    case Ok(built) => built
    case Err(invalid) => panic(Factory.explain(&invalid))
  }
  let greeting = Client.getText(&Factory.createClient(&factory, "greeter"), "greeting")
  let missing = Client.getText(&Factory.createClient(&factory, "greeter"), "nowhere")
  let _first = Io.writeLine(show(greeting))
  let _second = Io.writeLine(match missing {
      case Ok(text) => text
      case Err(failure) => HttpClient.describe(&failure)
    })
  Factory.dispose(&factory)
  let _stopped = Server.stop(&running)
  if greeting == Ok("hello") && missing == Err(HttpClient.Unsuccessful(404, "Not Found")) { 0 } else { 1 }
}

Check it and run it:

pudu check src/Ex01First.pudu
pudu run src/Ex01First.pudu

Output:

Ok("hello")
The response status 404 (Not Found) does not indicate success.

Factory.build takes every builder as one flat array and answers either the factory or every problem it found, which Factory.explain puts in one sentence. Client.getText answers the body of a successful response; a status outside 2xx becomes Unsuccessful with its code and reason, and HttpClient.describe renders any failure in one sentence. Factory.dispose closes the connections the factory's pipelines hold.

Make clients where they are needed: a client is a value over the name's pipeline, and the factory renews pipelines underneath it.

Related

Clone this wiki locally