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
Checklist de permisos antes de que Claude Code edite un sitio de cliente
Guía para agencias que quieren usar IA en landing pages sin tocar zonas sensibles.
Convierte tickets de soporte SaaS en pasos reproducibles con Claude Code
Flujo para transformar reportes vagos en pasos, evidencia y una nota útil para ingeniería.
Convierte tus notas viejas de Obsidian en instrucciones para Claude Code en 10 minutos
Rutina de 10 minutos para separar tus notas de Obsidian en hechos, decisiones y dudas, y darle a Claude Code instrucciones que sí funcionan.