Pular para o conteúdo
Kayro Gomes

CLI para gerenciar branches no Neon

Ferramenta de linha de comando em Node.js que automatiza o ciclo de vida de branches de banco no Neon: criar preview, copiar prod para dev, listar, dropar.

  • Node.js 22
  • TypeScript
  • Commander
  • Neon HTTP API
  • Zod
Imagem ilustrativa de exemplo usada em posts do blog

Antes desse CLI, cada vez que eu queria testar uma migration localmente eu abria o painel do Neon, clicava em "Create branch", copiava o connection string, colava no .env.local. Em 5 cliques. Para 10 migrations por semana, isso é meia hora perdida. Automatizei.

Comandos disponíveis

1Usage: neon-branch [options] [command]
2
3Gerencia branches de banco no Neon via HTTP API.
4
5Options:
6 -V, --version output the version number
7 --api-key <key> Neon API key (default: process.env.NEON_API_KEY)
8 --project-id <id> Neon project ID (default: process.env.NEON_PROJECT_ID)
9 -h, --help display help for command
10
11Commands:
12 list Lista todas as branches do projeto
13 create <name> Cria uma branch nova a partir de prod
14 copy <from> <to> Copia uma branch existente
15 drop <name> Apaga uma branch (com confirmação)
16 url <name> Imprime a connection string da branch
17 reset <name> Drop + recriar a branch (wipe state)

Por que Zod para validação

A API do Neon retorna JSONs grandes com nomenclatura em snake_case. Zod me dá type-safety no boundary do que vem de fora:

1import { z } from 'zod'
2
3export const BranchSchema = z.object({
4 id: z.string().uuid(),
5 name: z.string(),
6 protected: z.boolean(),
7 created_at: z.string().datetime(),
8 updated_at: z.string().datetime(),
9 // parent_id é null para branches raiz
10 parent_id: z.string().uuid().nullable(),
11 // ...
12})
13
14export type Branch = z.infer<typeof BranchSchema>
15
16export async function listBranches(apiKey: string, projectId: string): Promise<Branch[]> {
17 const res = await fetch(`https://console.neon.tech/api/v2/projects/${projectId}/branches`, {
18 headers: { Authorization: `Bearer ${apiKey}` },
19 })
20
21 if (!res.ok) {
22 throw new Error(`Neon API error: ${res.status} ${res.statusText}`)
23 }
24
25 const json = await res.json()
26 // Valida na borda — o que entra no resto do código é Branch tipado
27 return z.array(BranchSchema).parse(json.branches)
28}

O comando "reset" é o que mais uso: pnpm neon-branch reset dev — apaga minha branch dev, recria a partir de prod, e imprime a nova connection string. Em 3 segundos, sem sair do terminal.