# Azion Runtime quickstart

This guide takes you through your first function on Azion Runtime, from the code to a request on the deployed domain.

- Create a JavaScript function project with the Azion CLI.
- Write the handler in the recommended ES Modules shape, `export default { fetch(request, env, ctx) }`.
- Call a Web API and read the request metadata, an Azion API.
- Run the function with `azion dev`, then deploy it and compare the two answers.

The function returns four values as JSON. `EdgeRuntime` names where the code runs, `URL` and `crypto.randomUUID()` are [Web APIs](/en/documentation/devtools/runtime/api-reference/javascript/), and `request.metadata` holds the [request metadata](/en/documentation/devtools/runtime/api-reference/metadata/) that Azion adds to every request.

---

## Prerequisites

- The Azion CLI, installed and logged in. For the setup, refer to [Azion CLI quickstart](/en/documentation/devtools/cli/quickstart/).
- Node.js and npm. The CLI runs the Azion bundler with `npx` to build the project.
- `curl` on your machine.

---

## Create the project

Create a project named `my-runtime-function` with the CLI:

```bash
azion init --name my-runtime-function
```

The command asks five questions. Select the *Javascript* preset and the *Hello World* template, enter `Y` to install the dependencies, and enter `n` to the local server and to the deploy. You run both after you write the handler. The command confirms the project:

```text
Your application my-runtime-function was initialized successfully
```

The function entry is `index.js`, at the root of the new `my-runtime-function` directory. The template writes it in the older `export default main` shape, which the build reports as deprecated. For every shape the runtime accepts, refer to [Handlers](/en/documentation/devtools/runtime/api-reference/handlers/).

---

## Write the handler

Replace the contents of `index.js` with this handler:

```javascript
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    return Response.json({
      runtime: EdgeRuntime,
      path: url.pathname,
      requestId: crypto.randomUUID(),
      country: request.metadata?.geoip_country_name ?? null,
    });
  },
};
```

The runtime calls `fetch` once per request:

- `request` is the incoming [Request](/en/documentation/devtools/runtime/api-reference/request/), and `request.metadata` carries the GeoIP and network data of the request.
- `env` holds the environment variables, and `ctx` carries `waitUntil()`. This handler uses neither.
- `Response.json()` builds a [Response](/en/documentation/devtools/runtime/api-reference/response/) with the object as a JSON body and `content-type: application/json`.

The optional chaining in `request.metadata?.` keeps the handler working under `azion dev`, where the request carries no metadata.

---

## Run the function locally

Go to the project directory and start the local development server:

```bash
cd my-runtime-function
azion dev
```

The command builds the function and serves it on port 3333:

```text
[Azion] [Build] › ℹ  info      Using preset: javascript
[Azion] [Pre-Build] › ℹ  info      Starting pre-build...
[Azion] [Pre-Build] › ℹ  info      Pre-build completed successfully
[Azion] [Build] › ℹ  info      Using preset default entry: index.js
[Azion] [Build] › ℹ  info      Starting build...
[Azion] [Build] › ✔  success   Build completed successfully
[Azion] [Post-Build] › ℹ  info      Starting post-build...
[Azion] [Post-Build] › ✔  success   Post-build completed successfully
[Azion] [Server] › ✔  success   Function running on port 0.0.0.0:3333, url: http://localhost:3333
[Azion] [Server] › ℹ  info      Initial scan complete. Ready for changes.
```

The build prints no deprecation warning, because the handler uses the ES Modules shape. In a second terminal, request a path:

```bash
curl -s -i 'http://localhost:3333/hello'
```

The function answers with JSON:

```text
HTTP/1.1 200 OK
content-type: application/json
Date: Thu, 01 Jan 2026 12:00:00 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

{"runtime":"edge-runtime","path":"/hello","requestId":"9b2138b1-02f0-433c-aee5-9442142996b2","country":null}
```

Locally, `EdgeRuntime` is `edge-runtime` and `country` is `null`, because `azion dev` adds no request metadata. To stop the server, press `Ctrl+C`. For everything that differs between `azion dev` and a deployed function, refer to [How Azion Runtime works](/en/documentation/devtools/runtime/how-it-works/#local-and-deployed-behavior).

---

## Deploy the function

From the `my-runtime-function` directory, deploy the project:

```bash
azion deploy
```

The command uploads the project, builds it, creates the function, the application, and the workload, and prints the domain of the workload:

```text
Running deploy command
Uploading source files

Upload completed successfully!
…
Your Application was deployed successfully

To visualize your application access the Domain: https://<your-azion-domain>
Your application is being deployed to all Azion Locations and it might take a few minutes.
```

The first deploy can take several minutes to answer from every location. Until a location has the function, it answers `404` with a page titled `There's nothing here yet`. Wait and retry before you diagnose the deploy.

---

## Request the deployed function

Request the same path from the domain that `azion deploy` printed. Replace `<your-azion-domain>` with that domain:

```bash
curl -s -i 'https://<your-azion-domain>/hello'
```

The deployed function answers with the same keys:

```text
HTTP/2 200 
date: Thu, 01 Jan 2026 12:00:00 GMT
content-type: application/json
x-azion-request-id: 0123456789abcdef0123456789abcdef
x-azion-edge-location: <edge-location>
alt-svc: h3=":443"; ma=86400

{"runtime":"azion","path":"/hello","requestId":"1aa1a614-a0ca-4291-93a1-66fa1c57ebc9","country":"Brazil"}
```

Deployed, `EdgeRuntime` is `azion`, and `country` holds the country of the client that sent the request, from the request metadata. `requestId` changes on every request. The function runs on Azion Runtime and answers on the workload domain.

To change the function, edit `index.js` and run `azion deploy` again from the `my-runtime-function` directory. Later deploys take about two minutes to answer from every location.

---

## Next steps

- [How Azion Runtime works](/en/documentation/devtools/runtime/how-it-works.md): The execution model, the handler shapes, the bundler, and what differs under azion dev.
- [Web APIs](/en/documentation/devtools/runtime/api-reference/javascript.md): Every Web API the runtime provides, from fetch and streams to Web Crypto.
- [Node.js APIs](/en/documentation/devtools/runtime/node.md): The Node.js modules the build resolves, and how each one behaves when deployed.
- [Functions quickstart](/en/documentation/platform/functions/quickstart.md): Create a function in Azion Console and run it on an application.
