Back to Baml

Rest

fern/01-guide/02-languages/rest.mdx

0.222.015.6 KB
Original Source
<Info> Requires BAML version >=0.55 </Info> <Warning> This feature is a preview feature and may change. Please provide feedback either in [Discord][discord] or on [GitHub][openapi-feedback-github-issue] so that we can stabilize the feature and keep you updated! </Warning>

BAML allows you to expose your BAML functions as RESTful APIs:

We integrate with OpenAPI (universal API definitions), so you can get typesafe client libraries for free!

<Steps> ### Install BAML VSCode Extension https://marketplace.visualstudio.com/items?itemName=boundary.baml-extension
  - syntax highlighting
  - testing playground
  - prompt previews

Install NPX + OpenAPI

 <Tabs>
    <Tab title="macOS (brew)" language="bash">
      ```bash
      brew install npm openapi-generator
      # 'npm' will install npx
      # 'openapi-generator' will install both Java and openapi-generator-cli
      ```
    </Tab>

    <Tab title="Linux (apt)" language="bash">
      OpenAPI requires `default-jdk`

      ```bash
      apt install npm default-jdk -y
      # 'npm' will install npx; 'default-jdk' will install java
      ```
    </Tab>

    <Tab title="Linux (yum/dnf)" language="bash">
      OpenAPI requires Java

      ```bash
      dnf install npm java-21-openjdk -y
      # dnf is the successor to yum
      ```

      Amazon Linux 2023:
      ```bash
      dnf install npm java-21-amazon-corretto -y
      # 'npm' will install npx
      # 'java-21-amazon-corretto' will install java
      ```

      Amazon Linux 2:
      ```bash
      curl -sL https://rpm.nodesource.com/setup_16.x | bash -
      yum install nodejs -y
      # 'nodejs' will install npx
      amazon-linux-extras install java-openjdk11 -y
      # 'java-openjdk11' will install java
      ```
    </Tab>

    <Tab title="Windows" language="powershell">
      To install `npx` and `java` (for OpenAPI):

        1. Use the [Node.js installer](https://nodejs.org/en/download/prebuilt-installer) to install `npx` (default installer settings are fine).
        2. Run `npm install -g npm@latest` to update `npx` (there is currently an [issue][npx-windows-issue] with the default install of `npx` on Windows where it doesn't work out of the box).
        3. Run the [Adoptium OpenJDK `.msi` installer](https://adoptium.net/temurin/releases/?os=windows) (install the JDK; default installer settings are fine).

      You can verify that `npx` and `java` are installed by running:

      ```powershell
      npx -version
      java -version
      ```
    </Tab>

    <Tab title="Other" language="bash">
      To install `npx`, use the [Node.js installer](https://nodejs.org/en/download/prebuilt-installer).

      To install `java` (for OpenAPI), use the [Adoptium OpenJDK packages](https://adoptium.net/installation/linux/).
    </Tab>
  </Tabs>

Add BAML to your existing project

  This will give you some starter BAML code in a `baml_src` directory.
<Tabs>

  <Tab title="C#" language="bash">
  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type csharp
  ```
  </Tab>

  <Tab title="C++" language="bash">

  <Tip>OpenAPI supports [5 different C++ client types][openapi-client-types];
  any of them will work with BAML.</Tip>

  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type cpp-restsdk
  ```
  </Tab>

  <Tab title="Go" language="bash">
  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type go
  ```
  </Tab>

  <Tab title="Java" language="bash">

  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type java
  ```

  Notice that `on_generate` has been initialized for you to:

  - run the OpenAPI generator to generate a Java client library, and _also_
  - run `mvn clean install` to install the generated client library to your
    local Maven repository

  <Warning>
    If you only use Maven through an IDE (e.g. IntelliJ IDEA), you should
    remove `&& mvn clean install` from the generated `on_generate` command.
  </Warning>

  </Tab>

  <Tab title="PHP" language="bash">
  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type php
  ```
  </Tab>

  <Tab title="Ruby" language="bash">
  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type ruby
  ```
  </Tab>

  <Tab title="Rust" language="bash">
  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type rust
  ```
  </Tab>

  <Tab title="Other" language="bash">

  As long as there's an OpenAPI client generator that works with your stack,
  you can use it with BAML. Check out the [full list in the OpenAPI docs][openapi-client-types].

  ```bash
  npx @boundaryml/baml init \
    --client-type rest/openapi --openapi-client-type $OPENAPI_CLIENT_TYPE
  ```
  </Tab>

</Tabs>

Start the BAML development server

```bash
npx @boundaryml/baml dev --preview
```

This will do four things:

- serve your BAML functions over a RESTful interface on `localhost:2024`
- generate an OpenAPI schema in `baml_client/openapi.yaml`
- run `openapi-generator -g $OPENAPI_CLIENT_TYPE` in `baml_client` directory to
  generate an OpenAPI client for you to use
- re-run the above steps whenever you modify any `.baml` files
<Note> BAML-over-REST is currently a preview feature. Please provide feedback either in [Discord][discord] or on [GitHub][openapi-feedback-github-issue] so that we can stabilize the feature and keep you updated! </Note>

Check that the server is running

After running the npx @boundaryml/baml dev command, you can check that the server is up and running by making an HTTP request to these routes:

  1. http://localhost:2024/_debug/ping: Open in the browser or use curl to check that the server is up. You should see a text response similar to this: pong (from baml v0.206.1).

  2. http://localhost:2024/docs: Open in the browser to see and interact with all your routes through the Swagger UI generated from the OpenAPI schema.

<Note> If using Docker, replace `localhost` with the container hostname or IP as appropriate. </Note>

Use a BAML function in any language!

openapi-generator will generate a README with instructions for installing and using your client; we've included snippets for some of the most popular languages below. Check out baml-examples for example projects with instructions for running them.

<Note> We've tested the below listed OpenAPI clients, but not all of them. If you run into issues with any of the OpenAPI clients, please let us know, either in [Discord][discord] or by commenting on [GitHub][openapi-feedback-github-issue] so that we can either help you out or fix it! </Note> <Tabs> <Tab title="Go" language="go">

Run this with go run main.go:

go
package main

import (
	"context"
	"fmt"
	"log"
  baml "my-golang-app/baml_client"
)

func main() {
	cfg := baml.NewConfiguration()
	b := baml.NewAPIClient(cfg).DefaultAPI
	extractResumeRequest := baml.ExtractResumeRequest{
		Resume: "Ada Lovelace (@gmail.com) was an English mathematician and writer",
	}
	resp, r, err := b.ExtractResume(context.Background()).ExtractResumeRequest(extractResumeRequest).Execute()
	if err != nil {
		fmt.Printf("Error when calling b.ExtractResume: %v\n", err)
		fmt.Printf("Full HTTP response: %v\n", r)
		return
	}
	log.Printf("Response from server: %v\n", resp)
}
</Tab> <Tab title="Java" language="java"> First, add the OpenAPI-generated client to your project. <AccordionGroup> <Accordion title="If you have 'mvn' in your PATH">

You can use the default on_generate command, which will tell baml dev to install the OpenAPI-generated client into your local Maven repository by running mvn clean install every time you save a change to a BAML file.

To depend on the client in your local Maven repo, you can use these configs:

<CodeGroup> ```xml pom.xml <dependency> <groupId>org.openapitools</groupId> <artifactId>openapi-java-client</artifactId> <version>0.1.0</version> <scope>compile</scope> </dependency> ```
kotlin
repositories {
    mavenCentral()
    mavenLocal()
}

dependencies {
    implementation("org.openapitools:openapi-java-client:0.1.0")
}
</CodeGroup> </Accordion> <Accordion title="If you don't have 'mvn' in your PATH">

You'll probably want to comment out on_generate and instead use either the OpenAPI Maven plugin or OpenAPI Gradle plugin to build your OpenAPI client.

<CodeGroup> ```xml pom.xml <build> <plugins> <plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <version>7.8.0</version> <!-- Use the latest stable version --> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <inputSpec>${project.basedir}/baml_client/openapi.yaml</inputSpec> <generatorName>baml</generatorName> <!-- or another generator name, e.g. 'kotlin' or 'spring' --> <output>${project.build.directory}/generated-sources/openapi</output> <apiPackage>com.boundaryml.baml_client.api</apiPackage> <modelPackage>com.boundaryml.baml_client.model</modelPackage> <invokerPackage>com.boundaryml.baml_client</invokerPackage> <java8>true</java8> </configuration> </execution> </executions> </plugin> </plugins> </build> ```
kotlin
plugins {
    id("org.openapi.generator") version "7.8.0"
}

openApiGenerate {
    generatorName.set("java") // Change to 'kotlin', 'spring', etc. if needed
    inputSpec.set("${projectDir}/baml_client/openapi.yaml")
    outputDir.set("$buildDir/generated-sources/openapi")
    apiPackage.set("com.boundaryml.baml_client.api")
    modelPackage.set("com.boundaryml.baml_client.model")
    invokerPackage.set("com.boundaryml.baml_client")
    additionalProperties.set(mapOf("java8" to "true"))
}

sourceSets["main"].java {
    srcDir("$buildDir/generated-sources/openapi/src/main/java")
}

tasks.named("compileJava") {
    dependsOn("openApiGenerate")
}
</CodeGroup> </Accordion> </AccordionGroup>

Then, copy this code into wherever your main function is:

Java
import com.boundaryml.baml_client.ApiClient;
import com.boundaryml.baml_client.ApiException;
import com.boundaryml.baml_client.Configuration;
// NOTE: baml_client/README.md will suggest importing from models.* - that is wrong.
// See https://github.com/OpenAPITools/openapi-generator/issues/19431 for more details.
import com.boundaryml.baml_client.model.*;
import com.boundaryml.baml_client.api.DefaultApi;

public class Example {
  public static void main(String[] args) {
    ApiClient defaultClient = Configuration.getDefaultApiClient();
    DefaultApi apiInstance = new DefaultApi(defaultClient);
    ExtractResumeRequest extractResumeRequest = new ExtractResumeRequest(); // ExtractResumeRequest |
    try {
      Resume result = apiInstance.extractResume(extractResumeRequest);
      System.out.println(result);
    } catch (ApiException e) {
      System.err.println("Exception when calling DefaultApi#extractResume");
      System.err.println("Status code: " + e.getCode());
      System.err.println("Reason: " + e.getResponseBody());
      System.err.println("Response headers: " + e.getResponseHeaders());
      e.printStackTrace();
    }
  }
}

</Tab> <Tab title="PHP" language="php"> <Warning> The PHP OpenAPI generator doesn't support OpenAPI's `oneOf` type, which is what we map BAML union types to. Please let us know if this is an issue for you, and you need help working around it. </Warning>

First, add the OpenAPI-generated client to your project:

json
    "repositories": [
        {
            "type": "path",
            "url": "baml_client"
        }
    ],
    "require": {
        "boundaryml/baml-client": "*@dev"
    }

You can now use this code to call a BAML function:

PHP
<?php
require_once(__DIR__ . '/vendor/autoload.php');

$apiInstance = new BamlClient\Api\DefaultApi(
    new GuzzleHttp\Client()
);
$extract_resume_request = new BamlClient\Model\ExtractResumeRequest();
$extract_resume_request->setResume("Marie Curie was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity");

try {
    $result = $apiInstance->extractResume($extract_resume_request);
    print_r($result);
} catch (Exception $e) {
    echo 'Exception when calling DefaultApi->extractResume: ', $e->getMessage(), PHP_EOL;
}
</Tab> <Tab title="Ruby" language="ruby">

Use ruby -Ilib/baml_client app.rb to run this:

ruby
require 'baml_client'
require 'pp'

api_client = BamlClient::ApiClient.new
b = BamlClient::DefaultApi.new(api_client)

extract_resume_request = BamlClient::ExtractResumeRequest.new(
  resume: <<~RESUME
    John Doe

    Education
    - University of California, Berkeley
    - B.S. in Computer Science
    - graduated 2020

    Skills
    - Python
    - Java
    - C++
  RESUME
)

begin
  result = b.extract_resume(extract_resume_request)
  pp result

  edu0 = result.education[0]
  puts "Education: #{edu0.school}, #{edu0.degree}, #{edu0.year}"
rescue BamlClient::ApiError => e
  puts "Error when calling DefaultApi#extract_resume"
  pp e
end
</Tab> <Tab title="Rust" language="rust"> <Tip> If you're using `cargo watch -- cargo build` and seeing build failures because it can't find the generated `baml_client`, try increasing the delay on `cargo watch` to 1 second like so:
bash
cargo watch --delay 1 -- cargo build
</Tip>

First, add the OpenAPI-generated client to your project:

toml
[dependencies]
baml-client = { path = "./baml_client" }

You can now use cargo run:

rust
use baml_client::models::ExtractResumeRequest;
use baml_client::apis::default_api as b;

#[tokio::main]
async fn main() {
    let config = baml_client::apis::configuration::Configuration::default();

    let resp = b::extract_resume(&config, ExtractResumeRequest {
        resume: "Tony Hoare is a British computer scientist who has made foundational contributions to programming languages, algorithms, operating systems, formal verification, and concurrent computing.".to_string(),
    }).await.unwrap();

    println!("{:#?}", resp);
}
</Tab> </Tabs> </Steps>