# Primeiro deploy

Este é o fluxo base de todos os [guias por stack](/guia/nodejs-next/). Escopos do token: `projects:write`, `deploy`, `projects:read` (veja [Token de API](/comecar/token/)). Todo `POST` que cria leva `Idempotency-Key`; repetir a mesma chave com o mesmo corpo devolve a mesma resposta.

Cada bloco abaixo é autossuficiente (variáveis `P` = id do projeto, `S` = id do serviço, `D` = id do deploy).

## 1. Criar o projeto

Um projeto é um grupo de serviços que compartilham rede e podem compartilhar variáveis por referência ([Conceitos](/comecar/conceitos/)).

```bash
curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pd-proj-$(date +%s)-$RANDOM" \
  -d "{\"name\": \"meu-app-$(date +%s)-$RANDOM\"}"
```
```json
{"id": "01j9zkk07fhzv5zsmn996cdwpb", "name": "meu-app-1760000000"}
```

Guarde `id` (`P`). Em erro:

| Resposta | O que fazer |
|---|---|
| `401` | Token ausente ou inválido. Pare e confira o cabeçalho. |
| `403 forbidden` | Falta o escopo `projects:write`. Peça um token novo. |
| `409` (ex.: `slug_taken`, `project_quota_exceeded`) | Troque o nome ou apague um projeto que não usa mais. |

## 2. Criar o serviço

Duas fontes (`source`) cobrem o primeiro deploy: **imagem pública** ou **upload** de código. Para `visibility: "public"` informe o `slug` (sai em `https://<slug>.<domínio de apps>`); omitido, a plataforma deriva um do nome. O `web` que todo projeto traz nasce **público**, já com `slug` e `public_url` (para privado: `PATCH` com `{"visibility": "private"}`; veja [Conceitos](/comecar/conceitos/#o-serviço-web-padrão)).

### Opção A: imagem pública

```bash
SUF=$(date +%s)-$RANDOM
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: pd-proj2-$SUF" -d "{\"name\": \"meu-app-$SUF\"}" | jq -r .id)
curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"visibility\": \"public\",
    \"slug\": \"meu-app-$SUF\",
    \"source\": {\"type\": \"image\", \"ref\": \"ghcr.io/org/app:1.2\"},
    \"port\": 8080,
    \"healthcheck_path\": \"/healthz\"
  }"
```
```json
{"id": "01j9zk3jremq8fkd3sjt7h300r", "name": "web", "kind": "app", "visibility": "public", "port": 8080}
```

### Opção B: upload de código

Use `"source": {"type": "upload"}` e um `build` (`builder` = `auto`, `node`, `go`, `rust`, `python`, `static` ou `dockerfile`). Sem `build`, vale `auto`.

```bash
SUF=$(date +%s)-$RANDOM
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: pd-proj3-$SUF" -d "{\"name\": \"meu-site-$SUF\"}" | jq -r .id)
curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"visibility\": \"public\",
    \"slug\": \"meu-site-$SUF\",
    \"source\": {\"type\": \"upload\"},
    \"build\": {\"builder\": \"auto\"}
  }"
```
```json
{"id": "01j9zkngmvzxkw57zdyzz80b5e", "name": "web", "kind": "app", "visibility": "public", "port": 3000}
```

Regras do `name`: `^[a-z][a-z0-9-]{0,30}$`, único no projeto (é o hostname na [rede privada](/conectados/rede-privada/)). `port` padrão: 3000.

| Resposta | O que fazer |
|---|---|
| `400 invalid_request` | `name`, `source.type`, `build.builder` ou `healthcheck_path` inválidos: corrija o campo citado em `message`. |
| `404 not_found` | O projeto não existe ou não é do seu time (as duas situações são indistinguíveis). |
| `409 name_taken` / `slug_taken` | `name` ou `slug` já usado (todo projeto já nasce com um serviço `web`: use `PATCH` nele ou escolha outro nome). Troque. |

## 3. Disparar o deploy

Resposta imediata: o deploy fica `queued` e o build roda em seguida. Guarde o `id` (`D`).

```bash
# preparo (passos 1 e 2): projeto e serviço de imagem
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"pd4-$SUF\"}" | jq -r .id)
S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \
  -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)

curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services/$S/deployments" \
  -H "$H" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pd-dep-$SUF" \
  -d '{}'
```
```json
{"id": "01j9zkv1t9ndd76xmr8ahxee7p", "status": "queued"}
```

Com `source.type = "upload"`, envie o código como `multipart/form-data` nesta mesma rota (no lugar do corpo `{}`): `-F "file=@fonte.tar.gz"`. Com `git`, o deploy usa o repositório e o branch do `source`.

| Resposta | O que fazer |
|---|---|
| `403 forbidden` | Falta o escopo `deploy`. |
| `409 deploy_in_progress` | Já existe um deploy em andamento para o serviço: consulte-o em vez de disparar outro. |
| `unresolved_reference` / `reference_cycle` | Variável referenciada não existe ou há ciclo: veja [Variáveis e referências](/conectados/variaveis-e-referencias/). |
| `429` | Espere `Retry-After` ([Rate limit e retry](/erros/rate-limit-e-retry/)). |

## 4. Esperar o deploy (polling com backoff)

Não há webhooks neste ciclo. Consulte `GET .../deployments/{d}` começando em 2 s e dobrando até 30 s, por no máximo cerca de 10 minutos. Terminal de sucesso: `ready`. Terminais de falha: `failed`, `blocked`, `canceled`, `superseded`.

```bash
# preparo (passos 1 a 3): projeto, serviço de imagem e deploy
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"pd5-$SUF\"}" | jq -r .id)
S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \
  -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)
D=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services/$S/deployments" -H "$H" \
  -H "Content-Type: application/json" -d '{}' | jq -r .id)

delay=2
for i in $(seq 1 40); do
  st=$(curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/deployments/$D" -H "$H" | jq -r .status)
  echo "tentativa $i: $st"
  case "$st" in
    ready) break ;;
    failed|blocked|canceled|superseded) echo "deploy terminou em $st" >&2; exit 1 ;;
  esac
  sleep "$delay"; delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))
done
[ "$st" = ready ]
```

Em `failed`, leia o [log de build](/operacao/logs/). Em `blocked`, um serviço do qual este depende falhou ([Deploys e status](/operacao/deploys-e-status/)). Em `superseded`, um deploy mais novo tomou o lugar: acompanhe o mais novo.

## 5. Verificar a URL pública

Com `ready`, o serviço público responde em `https://<slug>.<domínio de apps>`, onde `<slug>` é o que você enviou na criação.

```bash
curl -sS -o /dev/null -w '%{http_code}\n' "https://meu-app-1760000000.nebularws.com/healthz"
```

Se não for `200`, leia os [logs de runtime](/operacao/logs/); para voltar à versão anterior, [rollback](/operacao/rollback/). Para domínio próprio, [Domínio próprio](/dominios/dominio-proprio/).

## Listar o que você criou

```bash
curl -sS "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN"
```
```json
{"items": [{"id": "01j9zkk07fhzv5zsmn996cdwpb", "name": "meu-app-1760000000"}], "next_cursor": null, "has_more": false}
```
