# @get

`@get(endpoint)` runs an HTTP GET on an [Endpoint](/docs/misc/endpoint) and stores the full `ApiResult<T>` in a property, or passes it to a method. It runs again when the endpoint's placeholders or the API configuration change, and on demand through `refetch`. For writes, see [@post](/docs/decorators/post), [@put](/docs/decorators/put) and [@patch](/docs/decorators/patch).

## Import

<sonic-code language="typescript">
  <template>
import { get, Endpoint, type ApiResult } from "@supersoniks/concorde/decorators";

type FlightBoard = { flights: Flight[] };
const departuresEndpoint = new Endpoint&lt;FlightBoard&gt;("flights?direction=departures");

@get(departuresEndpoint)
@state()
departures?: ApiResult&lt;FlightBoard&gt;;
  </template>
</sonic-code>

## Property

The property is `undefined` until the first response, then holds `result` (typed `T`), `response` (the HTTP response) and `request`. It is reset to `undefined` when the component disconnects.

Without a configuration key, `serviceURL` and the token are read from the HTML scope: place the component under an element that sets them (see [API configuration](/docs/misc/api-configuration)). The GET waits until `serviceURL` is available.

<sonic-code>
  <template>
    <docs-demo-sources for="demo-api-get"></docs-demo-sources>
    <demo-api-get></demo-api-get>
  </template>
</sonic-code>

## Method

Decorate a method to receive the `ApiResult<T>` after each GET instead of storing it.

<sonic-code language="typescript">
  <template>
@get(departuresEndpoint)
onDepartures(payload?: ApiResult&lt;FlightBoard&gt;) {
  this.boarding = payload?.result?.flights.filter((f) =&gt; f.status === "boarding") ?? [];
}
  </template>
</sonic-code>

<sonic-code>
  <template>
    <docs-demo-sources for="demo-api-get-method"></docs-demo-sources>
    <demo-api-get-method></demo-api-get-method>
  </template>
</sonic-code>

## Configuration key

Pass a `DataProviderKey<APIConfiguration>` as the second argument to read the configuration from the store instead of the HTML scope. Any change to that publisher runs the GET again.

<sonic-code language="typescript">
  <template>
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
import type { APIConfiguration } from "@supersoniks/concorde/utils/api";

const flightsApi = new DataProviderKey&lt;APIConfiguration&gt;("flightsApi");

@get(departuresEndpoint, flightsApi)
@state()
departures?: ApiResult&lt;FlightBoard&gt;;
  </template>
</sonic-code>

## Dynamic endpoint

`${prop}` placeholders in the endpoint resolve from properties of the same component; each change runs a new GET. While a placeholder is `null` or `undefined`, no request is sent and the property is `undefined`. Rules: [Dynamic paths](/docs/misc/dynamic-path).

<sonic-code language="typescript">
  <template>
const boardEndpoint = new Endpoint&lt;FlightBoard, { direction: string }&gt;(
  "flights?direction=${direction}",
);

@property() direction = "departures";

@get(boardEndpoint, flightsApi)
@state()
board?: ApiResult&lt;FlightBoard&gt;;
  </template>
</sonic-code>

<sonic-code>
  <template>
    <docs-demo-sources for="demo-api-get-configuration-key"></docs-demo-sources>
    <demo-api-get-configuration-key></demo-api-get-configuration-key>
  </template>
</sonic-code>

## Refetch

Keep the decorator in a constant to reuse it. `refetch(component)` runs the GET for that instance only and resolves with the new `ApiResult<T>` (or `undefined`).

<sonic-code language="typescript">
  <template>
const departuresGet = get(departuresEndpoint);

@departuresGet
@state()
departures?: ApiResult&lt;FlightBoard&gt;;

async refresh() {
  await departuresGet.refetch(this);
}
  </template>
</sonic-code>

`@refetch(handle)` does the same around a method. By default the GET runs **after** the method (and after its Promise, if it returns one); use `{ when: "before" }` to run it first. The method call resolves once the GET is done.

<sonic-code language="typescript">
  <template>
import { refetch } from "@supersoniks/concorde/decorators";

@refetch(departuresGet)
async showArrivals() {
  this.direction = "arrivals";
}
  </template>
</sonic-code>

## Polling and triggers

`refetchEveryMs` runs the GET again that many milliseconds after each response (default `0`: off). `triggerKey` runs it whenever another component calls `invalidate()` on that publisher.

<sonic-code language="typescript">
  <template>
import { dp } from "@supersoniks/concorde/utils";

const boardRefresh = new DataProviderKey&lt;number&gt;("boardRefresh");

@get(departuresEndpoint, { refetchEveryMs: 30000, triggerKey: boardRefresh })
@state()
departures?: ApiResult&lt;FlightBoard&gt;;

// anywhere else
dp(boardRefresh).invalidate();
  </template>
</sonic-code>

## API reference

| Argument | Type | Description |
|----------|------|-------------|
| `endpoint` | `Endpoint<T, U>` | Path relative to `serviceURL`. `U` lists the host properties used by `${…}` placeholders |
| `configurationKey` | `DataProviderKey<APIConfiguration>` (optional) | Read the configuration from the store instead of the HTML scope |
| `options.refetchEveryMs` | `number` (default `0`) | Polling interval after each response; `0` disables it |
| `options.triggerKey` | `DataProviderKey<unknown>` | Publisher whose `invalidate()` runs the GET again |
| `options.skipEmptyPlaceholder` | `boolean` (default `false`) | Treat a placeholder resolved to `""` as not ready |

`options` is the second argument, or the third when a `configurationKey` is passed. The returned decorator exposes `refetch(component): Promise<ApiResult<T> | undefined>`.

| `@refetch` argument | Type | Description |
|---------------------|------|-------------|
| `handle` | value returned by `get(…)` | GET to run |
| `options.when` | `"before"` or `"after"` (default `"after"`) | Run the GET before or after the method |
