Primeiro projeto

Existem duas formas de rodar uma aplicação Pipevine. Elas não mudam nada no YAML em si — só como ele roda. Escolha uma antes de começar.

Duas formas de rodar

Com Program.cs (desenvolvimento local)

Um projeto Exe normal, com PackageReferences aos pacotes Pipevine.* e um host de poucas linhas:

<!-- MinhaApi.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <OutputType>Exe</OutputType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Pipevine.Sdk" Version="0.0.0-*" />
    <PackageReference Include="Pipevine.Hosting.Http" Version="0.0.0-*" />
    <PackageReference Include="Pipevine.Expressions.Jsonata" Version="0.0.0-*" />
    <!-- só se usar persistência: -->
    <PackageReference Include="Pipevine.Data.PostgreSql" Version="0.0.0-*" />
    <!-- só se usar auth: -->
    <PackageReference Include="Pipevine.Security.Jwt" Version="0.0.0-*" />
  </ItemGroup>
  <ItemGroup>
    <None Include="*.pipevine.yaml" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>
</Project>
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPipevine();
builder.Services.AddPipevineJsonata();
// AddPipevineJwt() precisa vir ANTES de AddPipevineHttp() quando você usa jwt.
builder.Services.AddPipevineJwt();
builder.Services.AddPipevineHttp();
builder.Services.AddPipevineData();
builder.Services.AddPipevinePostgreSql();

var app = builder.Build();
app.UseExceptionHandler();
app.UsePipevineCorrelation();
app.MapPipevineApps();
app.MapPipevineHealthChecks();
app.Run();

Um nuget.config restrito às fontes de pacote que você realmente usa (nuget.org mais qualquer feed privado) evita ambiguidade de resolução de pacotes.

Sem Program.cs (conteinerizado)

Nenhum projeto C#. Só o YAML, rodando na imagem base genérica de host do Pipevine:

# docker-compose.yml
services:
  api:
    image: pipevine/hosting-generic:latest
    ports: ["8080:8080"]
    environment:
      PIPEVINE_PG_CONNECTION: "Host=postgres;Port=5432;Database=minhaapi;Username=postgres;Password=postgres"
    volumes:
      - ./minha-api.pipevine.yaml:/app/config/minha-api.pipevine.yaml:ro

O host genérico descobre sozinho quais pacotes usar (Postgres, JWT etc.) a partir do que o próprio YAML declara — nenhum código de aplicação em nenhum dos dois casos. Um pacote de terceiro que implemente o mecanismo de descoberta de módulos do host, solto num diretório de plugins montado, é descoberto e registrado automaticamente da mesma forma.

Cuidado ao montar volumes. A variável que aponta para onde o host genérico procura arquivos *.pipevine.yaml escaneia o diretório montado recursivamente. Monte só o arquivo YAML em si, nunca um diretório de projeto inteiro — uma pasta com bin/obj dentro faz o host encontrar cópias duplicadas do mesmo YAML (por exemplo, uma em bin/Release/...) e registrar a mesma aplicação mais de uma vez. Veja mais em Implantação.

O esqueleto mínimo

# minha-api.pipevine.yaml
apiVersion: pipevine.dev/v1
kind: Application
metadata: { name: minha-api, version: 1.0.0 }
spec:
  type: api

  routes:
    - id: ping
      method: GET
      path: /ping
      anonymous: true
      pipeline:
        - type: return
      response:
        status: 200
        body: "'pong'"

Isso já sobe — de qualquer uma das duas formas acima — e responde GET /ping com "pong".

anonymous: true dispensa autenticação nessa rota especificamente. Sem spec.auth declarado no documento, toda rota já é implicitamente anônima; o campo só passa a importar quando você adiciona autenticação (veja Autenticação e autorização).

Para onde ir daqui

  • Conceitos fundamentais — como o pipeline executa e a anatomia completa do documento YAML.
  • Validação e mapeamento — o próximo passo natural: receber e validar dados de verdade.
  • Um exemplo grande, real, rodando das duas formas acima: samples/03-inventory-orders no repositório do Pipevine — o mesmo YAML exercitando auth, persistência, transação e controle de fluxo de uma vez.