Introdução
Uma API de suporte precisa localizar tickets abertos e criar, atualizar e remover registros individuais com segurança. Você conectará um Worker ao D1, implementará acesso SQL parametrizado e testará como a API trata registros ausentes e entradas inválidas.
O roteador HTTP já foi fornecido, portanto a tarefa principal é integrar o banco de dados. Este laboratório independente usa um Worker descartável e um banco de dados D1, além de dados locais separados.
Use sua própria conta de aprendizagem e uma VM nova. A configuração prepara o Node.js 22.22.0 e executa npm install para instalar o Wrangler 4.131.1 localmente no projeto e quaisquer dependências da avaliação em /home/labex/project/ticket-database. As versões das dependências diretas estão fixadas; a instalação cria seu próprio lockfile. A configuração não faz login na nuvem nem executa operações avaliadas no banco de dados. Em uma máquina pessoal, instale a mesma versão do Wrangler com npm install --save-dev wrangler@4.131.1 no seu projeto.
Este exercício usa pequenos registros sintéticos dentro dos limites gratuitos do D1. O uso existente da conta também conta para esses limites. Você não precisa de um domínio comprado. Mantenha esta VM até verificar a exclusão dos recursos e o logout.
Autorizar esta VM e selecionar a conta
Nesta etapa, você conectará este terminal novo à sua própria conta de aprendizagem. Fazer login no Dashboard, por si só, não autoriza a VM. A permissão do D1 permite criar bancos de dados, alterar SQL e excluir bancos; a permissão de Workers permite fazer implantações, e a permissão de KV dá suporte ao inventário de limpeza do Wrangler. Revise a página de consentimento exibida, incluindo o acesso em segundo plano (Background Access), antes de autorizar.
Abra o projeto preparado e verifique a versão fixada da CLI:
cd /home/labex/project/ticket-database
npx wrangler --version
O resultado esperado é 4.131.1. Inicie a autorização por dispositivo; --device exibe um código para o navegador, e --browser=false deixa a escolha do navegador para você:
npx wrangler login --device --browser=false --scopes account:read user:read d1:write workers_scripts:write workers_kv:write
Abra a URL exibida no navegador, informe o código atual, confirme sua conta de aprendizagem e as permissões e autorize o acesso. Aguarde o terminal confirmar o sucesso. Nunca cole senhas ou tokens nos arquivos do projeto.
npx wrangler whoami --json
Verifique loggedIn: true e leia o name e o id da conta, mesmo quando apenas uma conta for listada. Copie o ID pretendido para a configuração abaixo. A variável de shell a seguir usa 6 bytes aleatórios (12 caracteres hexadecimais) para evitar conflitos com outros participantes. Um here-document grava o JSON entre as linhas JSON; $RUN é expandida dentro dele.
A barra invertida antes de $schema mantém essa chave JSON literal; $RUN continua sendo expandido para o nome único desta execução.
RUN=labex-c04-d02-$(openssl rand -hex 6)
cat > wrangler.jsonc <<JSON
{
"\$schema": "./node_modules/wrangler/config-schema.json",
"name": "$RUN",
"account_id": "YOUR_ACCOUNT_ID",
"main": "src/index.js",
"compatibility_date": "2026-09-15",
"workers_dev": true,
"preview_urls": false
}
JSON
Substitua YOUR_ACCOUNT_ID antes de executar o bloco. Mantenha este terminal aberto para que RUN continue disponível. name identifica esta execução; account_id seleciona a conta para as operações na nuvem. O arquivo é um JSON comum, que também é um JSONC válido. Gravá-lo não implanta nenhum Worker.
Preparar dados locais e remotos independentes
Nesta etapa, você criará a conexão com o D1 e preencherá a tabela de tickets conhecida. A configuração fornece o roteamento HTTP e a validação das entradas em src/index.js; as funções de armazenamento ausentes estão em src/store.js. Assim, seu trabalho fica concentrado no acesso SQL.
Crie um banco de dados descartável na nuvem. --binding DB fornece ao código da aplicação um nome curto, --update-config registra o nome real e o UUID em wrangler.jsonc, e --use-remote=false mantém o desenvolvimento local:
npx wrangler d1 create "$RUN-db" --binding DB --update-config --use-remote=false
Leia o nome e o ID criados e depois verifique o binding salvo:
cat wrangler.jsonc
A entrada DB deve indicar o banco de dados desta execução. Um binding é uma conexão configurada entre o código e um recurso. O UUID identifica o banco de dados na nuvem, enquanto --local usa um banco de dados SQLite separado nesta VM. Sempre inclua --local ou --remote nos comandos SQL.
cat schema.sql
npx wrangler d1 execute DB --local --file schema.sql
npx wrangler d1 execute DB --remote --file schema.sql
Agora, cada destino contém os mesmos dois tickets iniciais. DB é o nome que o handler fornecido recebe como env.DB; ele deve corresponder ao binding na configuração.
Implementar CRUD parametrizado
Nesta etapa, você implementará CRUD: criar, ler, atualizar e excluir. Uma instrução preparada mantém a estrutura SQL separada da entrada. Cada ? é um marcador de parâmetro; .bind(...) fornece os valores na ordem. Nunca concatene a entrada do usuário no SQL, mesmo quando ela parecer inofensiva.
WHERE restringe os registros afetados. .all() retorna um objeto de resultado cujo campo results contém o array de linhas. .first() retorna uma linha ou null. A cláusula RETURNING do SQLite devolve a linha alterada sem uma consulta separada. Na exclusão, .run() expõe meta.changes, que informa ao roteador se o registro realmente existia.
Grave o módulo de armazenamento:
cat > src/store.js <<'JS'
export async function list(db, status) {
const query = status === null
? db.prepare('SELECT id, subject, status, source FROM tickets ORDER BY id')
: db.prepare('SELECT id, subject, status, source FROM tickets WHERE status = ? ORDER BY id').bind(status);
const { results } = await query.all();
return results;
}
export async function get(db, id) {
return db.prepare('SELECT id, subject, status, source FROM tickets WHERE id = ?').bind(id).first();
}
export async function create(db, subject) {
return db.prepare("INSERT INTO tickets (subject, source) VALUES (?, 'api') RETURNING id, subject, status, source").bind(subject).first();
}
export async function update(db, id, status) {
return db.prepare('UPDATE tickets SET status = ? WHERE id = ? RETURNING id, subject, status, source').bind(status, id).first();
}
export async function remove(db, id) {
const result = await db.prepare('DELETE FROM tickets WHERE id = ?').bind(id).run();
return result.meta.changes === 1;
}
JS
Leia src/index.js para ver como o roteamento fornecido usa essas funções. Registros ausentes se tornam um erro 404 controlado; entradas malformadas se tornam 400; uma falha de banco de dados capturada se torna 503, sem expor detalhes internos do SQL.
Inicie o servidor local como um processo em segundo plano para manter o terminal disponível:
npx wrangler dev --ip 0.0.0.0 > dev.log 2>&1 &
Leia o log de inicialização e aguarde a mensagem indicando que o servidor está escutando:
cat dev.log
curl -i http://localhost:8787/tickets?status=open
O resultado esperado é HTTP 200 e somente o ticket 1. O servidor usa o banco de dados local. Anote o número do job exibido no terminal para fazer a limpeza depois.
Testar gravações e entradas rejeitadas localmente
Nesta etapa, você testará mais do que leituras bem-sucedidas. curl -i mostra o status HTTP e os cabeçalhos; -H fornece o tipo de conteúdo JSON, e -d envia um corpo usando POST por padrão.
Crie um assunto que contenha pontuação semelhante a SQL:
curl -i http://localhost:8787/tickets -H 'Content-Type: application/json' -d "{\"subject\":\"Printer ' OR 1=1 --\"}"
O resultado esperado é 201, com o assunto preservado como dado. Copie o id numérico retornado para TICKET_ID abaixo; não presuma que os IDs continuarão iguais após testes repetidos:
TICKET_ID=YOUR_RETURNED_ID
curl -i http://localhost:8787/tickets/$TICKET_ID
curl -i -X PATCH http://localhost:8787/tickets/$TICKET_ID -H 'Content-Type: application/json' -d '{"status":"closed"}'
curl -i -X DELETE http://localhost:8787/tickets/$TICKET_ID
curl -i http://localhost:8787/tickets/$TICKET_ID
O esperado é uma leitura 200, uma atualização 200 com closed, uma exclusão 204 sem corpo e, em seguida, 404 com {"error":"not_found"}. Os dois tickets originais devem continuar intactos.
Envie um JSON malformado e um status inválido:
curl -i http://localhost:8787/tickets -H 'Content-Type: application/json' -d '{'
curl -i -X PATCH http://localhost:8787/tickets/1 -H 'Content-Type: application/json' -d '{"status":"lost"}'
O resultado esperado é HTTP 400 com invalid_json e invalid_status, respectivamente. A parametrização SQL impede que a entrada se torne SQL, enquanto a validação da aplicação rejeita valores fora das regras de negócio. São problemas diferentes, resolvidos de maneiras diferentes.
Implantar e testar o banco de dados vinculado
Nesta etapa, você publicará o handler e o binding do D1. O banco de dados remoto já foi preenchido; a implantação não copia as linhas locais.
npx wrangler deploy
Copie a URL real https://...workers.dev exibida pela implantação para uma variável de shell. Esta é uma API sintética descartável, portanto remova-a depois dos testes:
URL='YOUR_DEPLOYED_HTTPS_URL'
curl -i "$URL/tickets?status=open"
O resultado esperado é 200 e o ticket 1. Se uma implantação nova retornar temporariamente um erro da plataforma, aguarde alguns segundos e repita esta leitura por até um minuto. Continue somente quando o status e o JSON corresponderem ao esperado; falhas persistentes precisam ser investigadas.
Repita o CRUD na API remota, copiando o ID retornado por ela:
curl -i "$URL/tickets" -H 'Content-Type: application/json' -d '{"subject":"Remote test"}'
TICKET_ID=YOUR_RETURNED_ID
curl -i -X PATCH "$URL/tickets/$TICKET_ID" -H 'Content-Type: application/json' -d '{"status":"closed"}'
curl -i -X DELETE "$URL/tickets/$TICKET_ID"
curl -i "$URL/tickets/$TICKET_ID"
curl -i "$URL/tickets"
O esperado é 201, 200, 204, 404 e, por fim, os dois tickets iniciais sem alterações. No Dashboard, abra exatamente este Worker e a visualização Bindings. Confirme que DB aponta para o seu banco de dados; acesse o link do banco para fazer uma inspeção somente leitura. Um binding salvo localmente não comprova a conexão usada pela implantação.

Este exemplo mostra o Worker implantado conectado pelo DB ao seu banco de dados D1. O sufixo aleatório identifica esta execução de exemplo; seus nomes serão diferentes. O link Value da tabela abre o banco de dados selecionado pela vinculação implantada.
Excluir os recursos descartáveis
Nesta etapa, você removerá somente os recursos deste laboratório enquanto a VM ainda estiver autorizada. Conclua primeiro todas as verificações funcionais. Mantenha a configuração até terminar de verificar a exclusão.
npx wrangler delete
Confirme que aparece somente o nome do Worker desta execução na configuração.
npx wrangler d1 delete DB
Inspecione o prompt e confirme que aparece somente o banco de dados desta execução. Em seguida, liste os bancos de dados:
npx wrangler d1 list --json
O nome e o UUID do banco de dados que você registrou devem estar ausentes em uma resposta bem-sucedida. Outros recursos podem permanecer. Um erro de autenticação ou de rede não é conclusivo: resolva o problema de acesso e repita a leitura antes de continuar. Faça a verificação desta etapa enquanto ainda estiver conectado.
Pare também o job de desenvolvimento local. Liste os jobs e encerre somente o job wrangler dev que você iniciou (substitua %1 se o número do job for diferente):
jobs
kill %1
Encerrar a autorização desta VM
Nesta etapa, você encerrará a autorização somente depois que a verificação independente da exclusão for concluída com sucesso. O logout remove a autorização armazenada do Wrangler nesta VM; fechar uma VM, por si só, não faz a limpeza na nuvem.
npx wrangler logout
npx wrangler whoami --json
O resultado esperado é loggedIn: false. Essa consulta sem autenticação pode terminar com um código diferente de zero; isso só é esperado quando a resposta estruturada indicar explicitamente que você está desconectado. Conclua a verificação e depois feche o ambiente do laboratório.
Resumo
Você praticou como adicionar busca de tickets a um Worker. Verificou resultados observáveis do banco de dados, manteve explícitos a conta selecionada e o estado local e removeu os recursos descartáveis antes de fazer logout.



