The JavaScript language itself only covers basics like variables, functions, objects, and built-in types like arrays and promises. Web APIs like `window`, `document`, and `console` *aren't part of the core language at all*— browsers add them on top so that scripts can interact with the page and the rest of the browser.

Your application can do the same thing— ***by defining native API, you can give JavaScript access to a subset of your application's functions and data***— and scripts can use them just like they would any other built-in Web API.

## Creating an API

A [`js::API`](/api/cpp/2_0_0/classultralight_1_1js_1_1_a_p_i.html) allows you to expose native functions, properties, and classes to JavaScript under a global, frozen API interface.

```cpp
// Create a JavaScript API object called "app" that will be accessible to
// page scripts under a frozen (read-only), global interface named "app".
js::API app("app");

// Add some properties:
app["version"] = "2.1.0";                   // app.version
app["quit"] = js::Bind(&app_, &App::Quit);  // app.quit()
```

### Binding Functions and Values

You add properties to an API object via `operator[]`:

```cpp
app["version"] = "2.1.0";                               // a constant
app["add"] = [](double a, double b) { return a + b; };  // a function
app["quit"] = js::Bind(&app_, &App::Quit);              // a member function
app["fs.readFile"] = [](std::string path) {             // a nested path
  return ReadFile(path);
};
```

JavaScript can later access these via the "app" object:

```js
console.log(app.version);    // "2.1.0"
console.log(app.add(2, 3));  // returns 5
const text = app.fs.readFile("save.dat");
app.quit();
```

Plain values (like `app["version"] = "2.1.0"`) become read-only constants (see [Live Properties](#content-live-properties) below for true getters and setters).

A dot in a path string (like `fs.readFile`) creates a nested API (`app.fs`).

Binding properties again replaces what was there.

Parameters and return values are converted to C++ types (see [Passing Data Across the Bridge](/docs/2.0/passing-data-across-the-bridge) for what converts).

## Attaching to a View

You'll need to explicitly attach any native API objects to a View before using it on a page:

```cpp
if (app.AttachTo(view.get()))
  view->LoadURL("file:///app.html");
```

Every page the View loads from then on gets the API, so you don't need to bind again after a navigation.

Pass `js::AttachOptions` as an optional second argument to configure attach flags and origin rules (see below).

### Keeping the API Alive

You should store the `js::API` as a class member to keep it alive and bind member functions:

```cpp
class App {
 public:
  explicit App(View* view) {
    api_["quit"] = js::Bind(this, &App::Quit);
    if (api_.AttachTo(view))
      view->LoadURL("file:///app.html");
  }

  void Quit();

 private:
  js::API api_{"app"};  // detaches from every View when the App goes away
};
```

A View keeps the API attached only while the `js::API` instance exists. Keep the object alive for as long as pages need the bindings.

The `js::API` does not need to outlive the View— destroying it earlier is safe. Destroying it detaches the API from every View. Events stop reaching the page, and any functions or class constructors that the page still holds throw a `TypeError` with code `ULJS_DETACHED`.

Bound callables and anything they capture are destroyed on the Renderer's thread. Any waiting `js::Task` also stops and rejects its Promise with `ULJS_DETACHED` (see [Async Callbacks](/docs/2.0/async-callbacks)).

## What You Can Bind

The C++ type on the right-hand side decides what the page gets:

| What You Bind in C++ | What the Page Gets |
|---|---|
| A callable (lambda, free function, or `js::Bind()` member) | A function |
| A value (number, string, bool, vector, or struct) | A constant (copied when you bind it) |
| A getter and optional setter bound with `BindProperty()` | A live property |
| A callable taking a trailing `js::Resolver` or returning `js::Task<T>` | An async function that returns a Promise ([Async Callbacks](/docs/2.0/async-callbacks)) |
| A class defined with `DefineClass<T>()` | A class page script can create with `new` ([Exposing Native Classes](/docs/2.0/exposing-native-classes)) |

### Live Properties

You can use `API::BindProperty()` to declare getters and setters for a property (leave out the setter to make it read-only):

```cpp
app.BindProperty("volume", [] { return g_volume; },
                 [](double v) { g_volume = v; });
```

```js
console.log(app.volume);  // calls the getter
app.volume = 0.5;         // calls the setter
```

### Naming Parameters

To make debugging easier, you can optionally name parameters for more descriptive JavaScript errors.

Use `Bind()` instead of assignment to add the names:

```cpp
app.Bind("setConfig", [](double volume, bool muted) { /* ... */ },
         js::Param("volume"), js::Param("muted"),
         js::Doc("Applies the audio settings."));
```

The JavaScript `TypeError` will now include the parameter name:

```js
app.setConfig("loud", true);
// TypeError: app.setConfig(volume: number, muted: boolean):
//            argument 1 (volume): expected number, got 'loud'
```

If you name one parameter, you should name them *all*. The optional `js::Doc` also adds a description to the API's schema (see [API Schemas and TypeScript](/docs/2.0/api-schemas-and-typescript)).

## Throwing JavaScript Errors from Native Code

Return a `js::Result` holding a `js::Error` to throw JavaScript errors:

```cpp
app["open"] = [](std::string path) -> js::Result<double> {
  if (path.empty())
    return js::Unexpected(js::Error::TypeError("path must not be empty")
                              .WithCode("APP_BAD_PATH"));
  return 1.0;
};
```

```js
try {
  app.open('');
} catch (e) {
  if (e.code === 'APP_BAD_PATH')
    console.log(e.message);  // "path must not be empty"
}
```

`WithCode()` sets the error's `code` property, so page script can check what went wrong without parsing the message.

See [JavaScript Errors and Diagnostics](/docs/2.0/javascript-errors-and-diagnostics) for error types, the library's built-in error codes, and diagnostics.

## Choosing Which Pages Get the API

By default only local pages (eg, `file://` and `View::LoadHTML()`) can access native API objects, remote pages (eg, `http://`) will not be able to access the API unless you extend the origin rules.

### Origin Rules

To let remote pages access native API, pass origin rules to `AttachTo()`:

```cpp
std::vector<const char*> rules = { "https://*.mygame.com", "file://*" };
app.AttachTo(view.get(), { .origin_rules = rules });
```

Each rule has the form `scheme://host[:port]`:

| Part | Accepts |
|---|---|
| Scheme | An exact scheme (required, no wildcard) |
| Host | An exact host, `*` for any host, or `*.example.com` for the domain and all its subdomains |
| Port | Optional— no port matches the scheme's default port, `:8443` matches that port, and `:*` matches any port |

> 🚧 Origin Rules Replace Defaults
>
> Passing origin rules replaces the default access entirely, so only matching pages get the API. Include `"file://*"` to keep access for local pages. `View::LoadHTML()` uses the URL you provide as the page's origin, but omitting the URL gives the page an opaque origin that no rule matches— use an injection filter to allow it instead.

### Filters

For decisions origin rules can't express (like a runtime user setting), set a filter on the View:

```cpp
js::SetInjectionFilter(view.get(), [](const js::InjectionRequest& request) {
  return request.rules_allow && request.is_main_frame;
});
```

The View calls the filter each time it is about to add an attached API's bindings to a page (usually after each navigation)

Origin rules will still be evaluated first— their verdict will be stored in `rules_allow` in the injection filter's request param.

Return `true` to add the API bindings to a page (or `false` to withhold them).

### Subframes and Frozen APIs

Set `flags` in the attach options:

| Flag | Effect |
|---|---|
| `js::AllFrames` | Also adds the API object to subframes (by default only the main frame gets them) |
| `js::Mutable` | Allows page script to modify the native API object (by default they're frozen) |

```cpp
app.AttachTo(view.get(), { .flags = js::AllFrames | js::Mutable });
```

## Changing Bindings Later

You can add, replace, or remove bindings after attaching. Each page sees the change at its next navigation.

> 🚧 Bind on the Renderer's Thread
>
> Bind from one thread (usually at startup). Once the API is attached, bind and unbind only on the Renderer's thread.

### Removing a Binding

Call `Unbind()` with the path:

```cpp
app.Unbind("debug.dump");
```

Hold a `js::BindingGuard` to remove a binding when a C++ object goes away:

```cpp
app.Bind("debug.dump", [] { DumpState(); });
js::BindingGuard dump_guard(app, "debug.dump");  // unbinds when destroyed
```

### Detaching from a View

Call `DetachFrom()` to take the whole API off a View:

```cpp
app.DetachFrom(view.get());
```

Events stop reaching the View's pages right away. The current page keeps its bindings until it navigates.
