> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a FastEdge HTTP application

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

FastEdge HTTP applications respond to HTTP requests directly from the edge. The handler compiles to a WebAssembly binary and runs without servers or containers. Three language options are available — complete the toolchain setup first: [Rust (Modern HTTP)](/fastedge/getting-started/setup-rust-modern), [Rust (Legacy HTTP)](/fastedge/getting-started/setup-rust-legacy), or [JavaScript](/fastedge/getting-started/setup-javascript).

## Write a handler

Each language uses a different entry point convention. Choose the tab that matches the toolchain set up earlier.

<Tabs>
  <Tab title="Modern Rust">
    Replace `src/lib.rs` with:

    ```rust theme={null}
    use wstd::http::body::Body;
    use wstd::http::{Request, Response};

    #[wstd::http_server]
    async fn main(request: Request<Body>) -> anyhow::Result<Response<Body>> {
        let url = request.uri().to_string();
        Ok(Response::builder()
            .status(200)
            .header("content-type", "text/plain;charset=UTF-8")
            .body(Body::from(format!("Request received: {url}")))?)
    }
    ```
  </Tab>

  <Tab title="Legacy Rust">
    Replace `src/lib.rs` with:

    ```rust theme={null}
    use fastedge::body::Body;
    use fastedge::http::{Request, Response, StatusCode, Error};

    #[fastedge::http]
    fn main(request: Request<Body>) -> Result<Response<Body>, Error> {
        let url = request.uri().to_string();
        Response::builder()
            .status(StatusCode::OK)
            .header("content-type", "text/plain;charset=UTF-8")
            .body(Body::from(format!("Request received: {url}")))
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    Create `src/index.js`:

    ```js theme={null}
    addEventListener('fetch', (event) => {
      const url = event.request.url;
      event.respondWith(new Response(`Request received: ${url}`));
    });
    ```
  </Tab>
</Tabs>

## Build

Compile the handler to a WebAssembly binary. The first build downloads dependencies and takes one to two minutes.

<Tabs>
  <Tab title="Modern Rust">
    ```sh theme={null}
    cargo build --release --target wasm32-wasip2
    ```

    The binary is at `./target/wasm32-wasip2/release/hello_world.wasm`.
  </Tab>

  <Tab title="Legacy Rust">
    ```sh theme={null}
    cargo build --release --target wasm32-wasip1
    ```

    The binary is at `./target/wasm32-wasip1/release/hello_world.wasm`.
  </Tab>

  <Tab title="JavaScript">
    ```sh theme={null}
    npx fastedge-build --input src/index.js --output wasm/app.wasm
    ```

    The binary is at `./wasm/app.wasm`.
  </Tab>
</Tabs>

## Deploy

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    1. In the [Gcore Customer Portal](https://portal.gcore.com), navigate to **FastEdge** > **HTTP Applications** > **Applications** and click **Create new application**.
    2. Click **Upload binary** and select the `.wasm` file produced in the Build step.
    3. Enter a **Name** for the application.
    4. Click **Save and deploy**.

    <p>The application URL appears on the details page. Test it:</p>

    ```sh theme={null}
    curl -i https://<app-name>-<id>.fastedge.app/hello
    ```
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Deploying requires two API calls: upload the binary to get its ID, then create the application. An [API token](/account-settings/api-tokens) is required.</p>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    ```

    <Warning>Do not commit API keys to source control.</Warning>

    **1. Upload the binary**

    <Tabs>
      <Tab title="Modern Rust">
        ```bash theme={null}
        curl -sX POST 'https://api.gcore.com/fastedge/v1/binaries/raw' \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H 'Content-Type: application/octet-stream' \
          --data-binary "@./target/wasm32-wasip2/release/hello_world.wasm"
        ```
      </Tab>

      <Tab title="Legacy Rust">
        ```bash theme={null}
        curl -sX POST 'https://api.gcore.com/fastedge/v1/binaries/raw' \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H 'Content-Type: application/octet-stream' \
          --data-binary "@./target/wasm32-wasip1/release/hello_world.wasm"
        ```
      </Tab>

      <Tab title="JavaScript">
        ```bash theme={null}
        curl -sX POST 'https://api.gcore.com/fastedge/v1/binaries/raw' \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H 'Content-Type: application/octet-stream' \
          --data-binary "@./wasm/app.wasm"
        ```
      </Tab>
    </Tabs>

    <p>The response contains the binary ID:</p>

    ```json theme={null}
    {"id": 4695, "api_type": "wasi-http", "status": 1}
    ```

    **2. Create the application**

    ```bash theme={null}
    export BINARY_ID=4695

    curl -sX POST 'https://api.gcore.com/fastedge/v1/apps' \
      -H "Authorization: APIKey $GCORE_API_KEY" \
      -H 'Content-Type: application/json' \
      -d "{\"name\": \"my-http-app\", \"binary\": $BINARY_ID, \"status\": 1}"
    ```

    <p>The response contains the application URL:</p>

    ```json theme={null}
    {"name": "my-http-app", "url": "https://my-http-app-1000503.fastedge.app", "api_type": "wasi-http"}
    ```

    **3. Test**

    <p>The URL becomes active within a few seconds:</p>

    ```bash theme={null}
    curl -i https://my-http-app-1000503.fastedge.app/hello
    ```
  </MethodSection>
</MethodSwitch>
