---
title: Your First Forge Application
description: Create, run, and publish your first Forge API.
moved_from:
  - guides/tutorials/index.md
---

# Your First Forge Application

Build a complete API project from scratch using Forge platform tools and templates.

[TOC]

---

## 📋 What You'll Learn

In this tutorial, you will:

- ✅ Set up your local development environment
- ✅ Create a new Forge API project using the SAIF CLI
- ✅ Understand the project structure and key files
- ✅ Run the application locally with Aspire
- ✅ Make your first code changes
- ✅ Explore the development workflow
- ✅ Publish your project to Azure DevOps and trigger its first deployment

**Prerequisites:**

- Windows, macOS, or Linux computer
- Basic familiarity with .NET development
- Access to SAIF Corporation Azure DevOps, with permission to create repositories and pipelines in your team's project (needed for [Step 6](#step-6-from-local-to-deployed))

!!! warning "Request access before you start"
    Two prerequisites below are provisioning requests, not installs, and can take time to complete: Docker Desktop (via a ServiceNow request) and SAIFCorporation NuGet feed access. File both **now**, before you start Step 1, so they've landed by the time you need them. See [Step 1.2](#12-install-saif-cli) and [Step 1.3](#13-install-docker-desktop) for details.

**Time to complete:** ~45 minutes for the local steps once prerequisite access is granted, plus pipeline run time for the deployment in Step 6

---

## 🎯 What We're Building

By the end of this tutorial, you'll have a working API project with:

- A .NET API with proper authentication structure
- Aspire orchestration for local development
- TypeSpec API definitions
- Infrastructure-as-code with Terraform
- Unit and integration test projects
- An Azure DevOps repository and registered pipelines, deployed to your first environment

---

## Step 1: Install Prerequisites

Before creating your first project, ensure you have the required tools installed.

### 1.1 Verify .NET SDK

Open a terminal and check your .NET version:

```bash
dotnet --version
```

The version should match the Forge 3.0 .NET version in [Version Compatibility](../../reference/version-compatibility.md#net). If it doesn't, download that SDK from [dot.net](https://dot.net).

### 1.2 Install SAIF CLI

Follow the [SAIF CLI installation guide](../install-saif-cli.md) to install the CLI tool. If you haven't requested NuGet feed access yet, see [Prerequisites](../install-saif-cli.md#prerequisites) first.

Verify installation:

```bash
saif --version
```

### 1.3 Install Docker Desktop

Docker is required for running dependencies locally. See the [Docker Desktop setup guide](../../build/local-dev/index.md) for the request steps and install instructions.

### 1.4 Enable AI-Assisted Development (optional)

Forge ships an MCP server and Agent Skills that GitHub Copilot and VS Code can use to help you navigate the platform. Configure it once per machine:

```bash
saif agent init
```

This registers the Forge MCP server and Agent Plugins for VS Code and GitHub Copilot CLI. See [Forge plugin setup](../forge-plugin.md) for scope, permissions, and verification.

---

## Step 2: Create Your Project

Now let's create a new Forge API project.

### 2.1 Run the Project Generator

```bash
saif new saif-api-exp --no-publish
```

The `--no-publish` flag creates the project locally — including its pipeline YAML under `.azdo/` — without creating the Azure DevOps repository or registering those pipelines in Azure DevOps.

!!! info "Why `saif-api-exp`?"
    `saif-api-exp` creates an **Experience**-type API, the starting point for APIs called directly by a frontend or another team's client. See [API Type Options](../../build/deploy/aspire-publish.md#api-type-options) when your project fits a different pattern.

The CLI will guide you through an interactive experience, prompting you for:

- **Project ID** (e.g., `it-api-exp-tutorial`)
- **Project display name** (e.g., `Tutorial API`)
- **Description** (e.g., `My first Forge application`)
- **Team email** (e.g., `your-team@saif.com`)
- Additional configuration options

### 2.2 Explore the Generated Structure

Navigate to your new project:

```bash
cd it-api-exp-tutorial
```

The project structure includes:

```
it-api-exp-tutorial/
├── src/
│   ├── Tutorial/              # Main API project
│   ├── Tutorial.AppHost/      # Aspire orchestration
│   ├── Tutorial.TypeSpec/     # API definitions
│   └── Tutorial.UnitTests/    # Unit tests
├── infra/
│   ├── api/                   # API infrastructure
│   └── auth/                  # Authentication config
├── scripts/                   # Setup scripts
└── Tutorial.sln               # Solution file
```

---

## Step 3: Run Locally

Let's start the application using Aspire.

### 3.1 Open in Your IDE

Open the solution in Visual Studio or VS Code:

```bash
# Visual Studio
start Tutorial.sln

# VS Code
code .
```

### 3.2 Start the Aspire Host

Navigate to the AppHost directory and run:

```bash
cd src/Tutorial.AppHost
aspire run
```

This starts the Aspire orchestrator, which manages all your application resources.

### 3.3 Open the Aspire Dashboard

The terminal will display URLs. Open the Aspire Dashboard (typically `http://localhost:15888`).

You'll see:

- **Resources** - Your API and dependencies
- **Console** - Live log output
- **Traces** - Request traces for debugging

### 3.4 Test the API

The API runs on a dynamic port shown in the dashboard. Click the endpoint link to open Swagger UI.

Try the `/health` endpoint to verify everything works:

```bash
curl http://localhost:{port}/health
```

You should see: `Healthy`

---

## Step 4: Make Your First Change

Now let's modify the API to add a new endpoint.

### 4.1 Open the API Controller

Navigate to `src/Tutorial/Controllers/` and open the main controller file.

### 4.2 Add a Hello Endpoint

Add a new method:

```csharp
[HttpGet("hello")]
public IActionResult Hello([FromQuery] string name = "World")
{
    return Ok(new { message = $"Hello, {name}!" });
}
```

### 4.3 Test Your Change

Save the file. If hot reload is enabled, the change applies automatically. Otherwise, restart the AppHost.

Test the new endpoint:

```bash
curl "http://localhost:{port}/hello?name=Developer"
```

Expected response:

```json
{ "message": "Hello, Developer!" }
```

🎉 **Congratulations!** You've made your first change to a Forge application!

---

## Step 5: Run the Tests

Forge projects include unit and integration tests. Let's run them.

### 5.1 Run Unit Tests

Scaffolded projects run on xUnit.net v3's native Microsoft Testing Platform mode, which takes
`--project` rather than a positional path:

```bash
dotnet test --project src/Tutorial.UnitTests --report-trx
```

### 5.2 Review Test Structure

Explore the test project to understand the testing patterns used in Forge applications.

---

## Step 6: From Local to Deployed

So far, everything has run locally — `saif new saif-api-exp --no-publish` deliberately skipped creating an Azure DevOps repository and registering the pipelines already scaffolded under `.azdo/`. This step closes that gap.

### 6.1 Publish Your Project

From the project root, run:

```bash
saif publish
```

This reads the manifest that `saif new` wrote when it created the project, then:

- Creates the Azure DevOps repository and pushes your local `main` branch to it
- Registers the pipelines already scaffolded under `.azdo/` (main and PR) as Azure DevOps pipeline resources
- Applies branch policies and pipeline validations

For all options (including `--directory`), see the [CLI command reference](../../reference/dotnet/SAIF.Platform.CLI/commands.md#saif-publish).

`saif publish` is safe to re-run — if a step fails partway through (for example, a pipeline creation error), running it again reuses the repository it already created instead of duplicating it.

!!! note "This is different from re-running `saif new`"
    Re-running `saif new` would create a **second, separate project** and re-prompt for all its parameters. `saif publish` is the command that takes the project you already have and pushes it to Azure DevOps.

!!! note "`saif publish` doesn't deploy the app by itself"
    `saif publish` pushes your code and registers the pipelines — it doesn't run them. Your application isn't deployed until the main pipeline actually runs, which happens automatically on this push or the next time you trigger/push to `main` (see [6.2 Verify the Deployment](#62-verify-the-deployment)).

### 6.2 Verify the Deployment

- Open the Azure DevOps project and confirm the repository now exists with your pushed code.
- Confirm the main and PR pipelines were registered.
- The main pipeline auto-triggers on the push `saif publish` made — if it didn't, run it manually. This is the step that actually deploys the application to your first environment (**Test**, per the [environment flow](../../reference/environments.md)).
- Once the pipeline completes, confirm the deployment stage succeeded. The app's raw `azurewebsites.net` address isn't reachable directly; traffic follows the shared Azure Front Door and APIM path. By default, an internal API is reachable at `https://app-int-test.saif.com/api/exp/<app-name>/health` (see [Call the API](../../build/identity/configuration/subscription-key-auth.md#step-5-call-the-api) for the path pattern). See [Custom Subdomains](../../build/deploy/custom-subdomains.md) if you need a dedicated hostname instead.

!!! tip "`--no-publish` is optional"
    This tutorial used `--no-publish` so you could inspect the project locally before deploying it. For future projects, you can skip that split and let `saif new` create the Azure DevOps repository and register the pipelines immediately — just omit `--no-publish` and answer the publish prompts as part of `saif new`.

---

## 🎓 What You Learned

In this tutorial, you:

- ✅ Installed prerequisites (SAIF CLI, Docker)
- ✅ Created a new Forge API project
- ✅ Explored the project structure
- ✅ Ran the application with Aspire
- ✅ Made code changes and tested them
- ✅ Ran the unit tests
- ✅ Published the project to Azure DevOps and triggered its first deployment

---

## 🚀 Next Steps

Now that you have a working project, follow the guided path to add real capabilities:

**👉 [Build on Your App](build-on-your-app.md)** — A step-by-step guide covering authentication, data, APIs, eventing, feature flags, deployment, and observability.

Or jump directly to a specific topic:

| Topic                 | Guide                                                          |
| --------------------- | -------------------------------------------------------------- |
| Set up authentication | [Identity](../../build/identity/index.md) |
| Configure database | [Cosmos DB NoSQL](../../build/data/cosmos-nosql.md) |
| Call external APIs | [Downstream API Calls](../../build/apis/calling-apis.md) |
| Add event handling | [Event Service](../../build/eventing/event-service.md) |
| Add more services | [Aspire Publish](../../build/deploy/aspire-publish.md) |

---

## 🔍 Troubleshooting

### Docker not running

Ensure Docker Desktop is started. Check the system tray icon.

### Port conflicts

If ports are in use, stop other applications or modify the AppHost configuration.

### CLI not found

Ensure SAIF CLI is installed and your terminal has the updated PATH. Try opening a new terminal window.

---

## 📚 Related Documentation

- [SAIF CLI Installation](../install-saif-cli.md)
- [Local Development](../../build/local-dev/index.md)
- [Version Compatibility](../../reference/version-compatibility.md)
