Contenidos
Construyendo funciones auxiliares reutilizables de PostgreSQL en Deno con deno-postgres
Recientemente he estado muy metido en Deno, me encantan sus APIs, es un enfoque fresco en la interminable guerra de runtimes de JavaScript, que si bien no es tan saturada como la otra interminable guerra de librerías de UI en JavaScript, ahora nos ha dado una de las mejores formas de escribir JavaScript para el servidor.
He estado reconstruyendo algunos de mis proyectos que antes corrían en Node.js/Express para que funcionen en Deno/Oak, lo cual ha sido muy disfrutable, aunque hay cosas que todavía pueden ser molestas.
Verás, Prisma es mi ORM de preferencia, pero sigue siendo complicado integrarlo con Deno, lo que me llevó a usar Deno-Postgres, un driver fantástico y flexible para mis consultas a la base de datos. Sin embargo, al no usar un ORM, empecé a lidiar con el interminable código repetitivo de consultas a la base de datos.
En el desarrollo backend moderno, escribir código limpio y mantenible para la base de datos es esencial. El uso directo de cualquier librería puede llevar a escribir mucho código repetitivo y a una ejecución de consultas propensa a errores.
En este artículo, exploraremos cómo abstraer tu lógica de base de datos usando funciones auxiliares reutilizables para consultar PostgreSQL en Deno. Este enfoque ayuda a mantener tu base de código limpia y promueve una forma consistente de manejar las interacciones con la base de datos.
Toma este bloque de código:
const client = await pool.connect();
try {
const result = await client.queryObject("SELECT * FROM users WHERE id = $1", [id]);
return result.rows[0];
} catch (e) {
console.error("Query error", e);
} finally {
client.release();
}
Este patrón—conectarse a la base de datos, hacer la consulta, manejar errores y liberar el cliente—se repite en cada función relacionada con la base de datos. Esto ensucia tu código y aumenta el riesgo de olvidar liberar la conexión o de gestionar mal el pool de conexiones.
La solución
// db.ts
import { Pool } from "https://deno.land/x/postgres/mod.ts";
export default new Pool({
user: "postgres",
password: "yourpassword",
database: "yourdb",
hostname: "localhost",
port: 5432,
}, 3, true);
Ahora hay que crear una función para estandarizar queries:
import pool from "./db.ts";
import { QueryObjectResult } from "https://deno.land/x/postgres/mod.ts";
export async function query<T = unknown>(
sql: string,
params: unknown[] = [],
): Promise<QueryObjectResult<T>> {
const client = await pool.connect();
try {
const result = await client.queryObject<T>(sql, ...params);
return result;
} catch (error) {
console.error("DB Query Error:", error);
throw error;
} finally {
client.release();
}
}
Esto aísla la lógica de conexión y manejo de errores, de modo que tu lógica de negocio permanece enfocada.
Uso completo
// userRepo.ts
export async function getUserById(id: string) {
const result = await query<{ id: string; name: string }>(
"SELECT id, name FROM users WHERE id = $1",
[id],
);
return result.rows[0];
}
Esto es limpio, facil de hacer pruebas unitarias, y forza constistencia en todas las queries.
Bonus: Soporte para transacciones
Si necesitas soporte para transacciones, podemos expander la utilidad:
export async function withTransaction<T>(
callback: (client: ReturnType<typeof pool.connect>) => Promise<T>,
): Promise<T> {
const client = await pool.connect();
try {
await client.queryObject("BEGIN");
const result = await callback(client);
await client.queryObject("COMMIT");
return result;
} catch (err) {
await client.queryObject("ROLLBACK");
throw err;
} finally {
client.release();
}
}
Y Podemos usarla de esta forma:
await withTransaction(async (client) => {
await client.queryObject(
"UPDATE users SET credits = credits - 10 WHERE id = $1", [userId]);
await client.queryObject(
"INSERT INTO transactions (...) VALUES (...)");
});
Conclusión
Usar funciones auxiliares reutilizables para el acceso a la base de datos mejora la legibilidad, facilidad de pruebas y mantenibilidad de tu aplicación en Deno. Reduce el código repetitivo y obliga a escribir consultas de manera estructurada, facilitando la escalabilidad de tu base de código.
Ya sea que estés construyendo una API REST, un servidor de juegos o una plataforma de streaming, contar con patrones limpios de acceso a la base de datos trae beneficios a largo plazo.
Quizás te interese:
Interface vs Type – Una guía comprensiva
La vida esta llena de decisiones, escoge con sabiduría
Creando una herramienta para hacer preview a sitios estaticos
Una herramienta innecesaria (para la mayoria de la gente)