Deno TypeScript con Claude Code: permisos, deno.json y Deno.serve
Guía práctica para Deno con Claude Code: permisos mínimos, tareas en deno.json, Deno.serve y fmt/lint/test con código copiable.
Empieza por el modelo de permisos de Deno
Deno es un runtime para JavaScript y TypeScript: la capa que ejecuta tu código. Su diferencia práctica es que es seguro por defecto. El acceso a archivos, red, variables de entorno y procesos externos no está abierto hasta que lo permites. La referencia oficial es Security and permissions.
Claude Code acelera mucho el trabajo porque puede generar servidor, tests, deno.json y checklist de revisión. Pero si el prompt es vago, puede producir -A, suposiciones de Node.js o herramientas innecesarias. Conviene decirle qué API usar, qué no usar, qué permisos abrir y qué comando debe pasar.
Términos básicos: un permiso es la lista de recursos que el programa puede tocar; un deno task es un comando con nombre dentro de deno.json; formatter, linter y test runner son herramientas para formato, revisión estática y pruebas. Deno las incluye. Para ampliar, revisa desarrollo de API, estrategias de testing y tips de TypeScript.
Prompt recomendado para Claude Code
Crea una API JSON pequeña con Deno. Usa
Deno.serve, no Express. Creadeno.jsoncon tareasdev,start,fmt,lint,testycheck. No uses-A. Permite solo--allow-net=127.0.0.1:8000,--allow-read=./datay--allow-write=./data. Escribe las pruebas conDeno.test().
Ese prompt se alinea con la documentación oficial de configuration, deno task, Deno.serve y deno test.
deno.json con tareas reproducibles
{
"tasks": {
"dev": "deno run --watch --allow-net=127.0.0.1:8000 --allow-read=./data --allow-write=./data server.ts",
"start": "deno run --allow-net=127.0.0.1:8000 --allow-read=./data --allow-write=./data server.ts",
"fmt": "deno fmt",
"lint": "deno lint",
"test": "deno test",
"check": "deno fmt --check && deno lint && deno test"
},
"fmt": {
"lineWidth": 100,
"semiColons": true
},
"lint": {
"rules": {
"tags": ["recommended"]
}
},
"imports": {
"@std/assert": "jsr:@std/assert"
}
}
La tarea check es el contrato de calidad: formato, lint y tests en una sola entrada para Claude Code y CI.
API copiable con Deno.serve
// app.ts
export type Item = {
id: string;
title: string;
done: boolean;
};
export interface ItemStore {
list(): Promise<Item[]>;
save(items: Item[]): Promise<void>;
}
export class FileItemStore implements ItemStore {
constructor(private readonly path = "./data/items.json") {}
async list(): Promise<Item[]> {
try {
const text = await Deno.readTextFile(this.path);
return JSON.parse(text) as Item[];
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return [];
}
throw error;
}
}
async save(items: Item[]): Promise<void> {
await Deno.writeTextFile(this.path, JSON.stringify(items, null, 2));
}
}
export function createHandler(store: ItemStore): (request: Request) => Promise<Response> {
return async (request: Request): Promise<Response> => {
const url = new URL(request.url);
if (url.pathname === "/health") {
return Response.json({ ok: true });
}
if (url.pathname === "/api/items" && request.method === "GET") {
return Response.json(await store.list());
}
if (url.pathname === "/api/items" && request.method === "POST") {
const body = await request.json().catch(() => null) as { title?: unknown } | null;
if (!body || typeof body.title !== "string" || body.title.trim() === "") {
return Response.json({ error: "title is required" }, { status: 400 });
}
const items = await store.list();
const item: Item = {
id: crypto.randomUUID(),
title: body.title.trim(),
done: false
};
await store.save([...items, item]);
return Response.json(item, { status: 201 });
}
return new Response("Not Found", { status: 404 });
};
}
// server.ts
import { createHandler, FileItemStore } from "./app.ts";
Deno.serve(
{ hostname: "127.0.0.1", port: 8000 },
createHandler(new FileItemStore())
);
mkdir -p data
printf "[]\n" > data/items.json
deno task dev
curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/api/items \
-H "content-type: application/json" \
-d '{"title":"Deno article draft"}'
curl http://127.0.0.1:8000/api/items
Pruebas sin permisos extra
// app_test.ts
import { assertEquals } from "@std/assert";
import { createHandler, type Item, type ItemStore } from "./app.ts";
class MemoryStore implements ItemStore {
private items: Item[] = [];
async list(): Promise<Item[]> {
return [...this.items];
}
async save(items: Item[]): Promise<void> {
this.items = [...items];
}
}
Deno.test("GET /health returns ok", async () => {
const handler = createHandler(new MemoryStore());
const response = await handler(new Request("http://localhost/health"));
assertEquals(response.status, 200);
assertEquals(await response.json(), { ok: true });
});
Deno.test("POST /api/items creates an item", async () => {
const handler = createHandler(new MemoryStore());
const response = await handler(
new Request("http://localhost/api/items", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ title: "Write article" })
})
);
const created = await response.json() as Item;
assertEquals(response.status, 201);
assertEquals(created.title, "Write article");
assertEquals(created.done, false);
});
Deno.test("POST /api/items rejects an empty title", async () => {
const handler = createHandler(new MemoryStore());
const response = await handler(
new Request("http://localhost/api/items", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ title: "" })
})
);
assertEquals(response.status, 400);
});
Casos de uso reales
Primero, prototipos de API internas: paneles de administración, receptores de webhooks y demos con JSON pueden empezar con Deno.serve antes de adoptar un framework grande.
Segundo, automatización del repositorio: revisar contenido, validar configuración o guardar snapshots de APIs encaja bien en deno task.
Tercero, onboarding del equipo: los errores de permisos explican de forma visible por qué un script necesita red, lectura o escritura.
Cuarto, servicios HTTP pequeños que podrían moverse a un entorno edge. Usar Request y Response estándar reduce el acoplamiento.
Errores frecuentes
No dejes -A en las tareas generadas. Abre todos los permisos y elimina la principal ventaja de seguridad.
No instales Express, Jest, Prettier y ESLint antes de probar las herramientas incluidas por Deno.
No olvides crear data/items.json. Si quieres que el código cree directorios, revisa con cuidado el permiso de escritura.
No hagas que todas las pruebas usen archivos o sockets reales. Usa una tienda en memoria para unit tests.
No copies ejemplos antiguos sin revisar la documentación oficial de Deno.
CTA y verificación
Si este flujo se repite, documenta permisos y comandos en CLAUDE.md. Empieza con el cheatsheet gratuito, usa productos y plantillas para prompts reutilizables y considera formación o consultoría si lo necesitas para un equipo.
Verificación práctica: crea data/items.json, ejecuta deno task dev, publica un item con curl y luego corre deno task check. En la revisión, prioriza eliminar -A, limitar rutas y mantener unit tests sin permisos.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.