- Instalación de dependencias
- Configurando la conexión
- Estructura inicial de la base de datos
- Ejemplo de operaciones: lista, creación y visualización
- Resumen
Las aplicaciones con las que hemos interactuado en las lecciones de este curso se almacenan en variables normales. Esto fue útil para no distraernos con la interacción con la base de datos y centrarnos en las características específicas de la web. Ahora que estamos familiarizados con el framework y entendemos los principios de creación de aplicaciones sobre él, podemos comenzar a trabajar con una base de datos real.
Para comenzar a almacenar y extraer datos de la base de datos, necesitamos hacer algunas acciones:
- Conectar los paquetes necesarios para trabajar con la base de datos.
- Configurar la conexión a la base de datos y permitir el acceso desde la aplicación.
- Crear la estructura inicial de la base de datos con las tablas necesarias.
- Reescribir los métodos de los repositorios para que manejen los datos directamente a través de la base de datos.
Instalación de dependencias
Por simplicidad, utilizaremos la base de datos sqlite3 almacenada en memoria. Esto es suficiente para el aprendizaje, pero en un entorno real necesitarás instalar PostgreSQL o su equivalente.
npm i sqlite3
Configurando la conexión
import sqlite3 from 'sqlite3';
const db = new sqlite3.Database(':memory:');
En el ejemplo anterior, creamos una base de datos en la memoria utilizando el parámetro ':memory:'. Una base de datos en memoria significa que la base de datos existirá solo en la memoria operativa de la computadora mientras el servidor está en funcionamiento. Cuando el servidor se detenga, la base de datos desaparecerá. Después de crear la base de datos, tenemos una variable db a través de la cual trabajaremos con la base.
Estructura inicial de la base de datos
Como en nuestro caso la base de datos se crea al inicio de la aplicación, su inicialización también se realizará allí, durante el inicio. Para las consultas en la base de datos, el objeto db proporciona el método run(). También hay otro método útil serialize(), que garantiza que todas las consultas a la base de datos se realizan secuencialmente. Este método recibe un callback, dentro del cual se realiza todo el trabajo:
db.serialize(() => {
db.run(`
CREATE TABLE courses (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT
);
`);
db.run(`
CREATE TABLE cars (
id INTEGER PRIMARY KEY,
make VARCHAR(255) NOT NULL,
model VARCHAR(255) NOT NULL
);
`);
});
En el ejemplo anterior, se pasa una función al método serialize(), dentro de la cual se realizan las consultas con el método run(). Se pasan consultas SQL normales al método. Para facilitar la lectura, hemos dividido cada consulta en varias líneas.
Ejemplo de operaciones: lista, creación y visualización
Adición de un registro
La biblioteca ofrece muchos métodos diferentes para trabajar con la base de datos. Vamos a ver algunos de ellos a través de ejemplos. Empezaremos por añadir registros a la base de datos:
const courses = [
{ id: 1, title: 'JavaScript', description: 'Curso de programación en JavaScript' },
{ id: 2, title: 'Fastify', description: 'Curso del framework Fastify' },
];
const stmt = db.prepare('INSERT INTO courses VALUES (?, ?)');
courses.forEach((course) => {
stmt.run(course.id, course.title, course.description);
});
stmt.finalize();
El método prepare() permite preparar una consulta SQL, y luego ejecutarla con la sustitución de valores. Esto es especialmente útil cuando necesitamos realizar varias consultas con diferentes datos. En nuestro ejemplo, estamos reemplazando los valores del array y agregando varias filas a la base. Luego se llama al método finalize() para finalizar las operaciones de adición.
Extracción de la lista
Para extraer la lista, puedes utilizar el método all(). El segundo parámetro del método acepta un callback, al cual se le pasa un error si algo salió mal, y los datos correspondientes:
app.get('/courses', (req, res) => {
db.all('SELECT * FROM courses', (error, data) => {
const templateData = {
courses: data,
error,
};
res.view('courses/index', templateData);
});
});
Extracción de un solo registro
Para extraer un registro, puedes usar el método get():
app.get('/courses/:id', (req, res) => {
const { id } = req.params;
db.get(`SELECT * FROM courses WHERE id = ${id}`, (err, data) => {
const templateData = {
course: data,
error,
};
res.view('courses/show', templateData);
});
});
Agregar un registro
La adición de datos a la base de datos se puede implementar mediante el método run(). Este método se puede utilizar para hacer cambios en la base de datos:
app.post('/courses/:id', (req, res) => {
const { id } = req.params;
const { title, description } = req.body;
const stmt = db.prepare('INSERT INTO courses(title, description) VALUES (?, ?)');
stmt.run([title, description], function (error) {
if (error) {
const templateData = {
error,
course: { title, description },
};
res.view('courses/new', templateData);
return;
}
res.redirect(`/courses/${this.lastID}`);
});
});
Al agregar un registro, hay situaciones en las que necesitamos obtener nuevos datos. Por ejemplo, el identificador del recurso agregado, que se utiliza en la dirección de redirección. Para esto, puedes usar la propiedad this.lastID en el callback. Ten en cuenta que se utiliza una función normal, declarada a través de function. Si utilizas una función de flecha, el contexto será incorrecto.
Actualización de un registro
Para actualizar un registro también usamos el método run():
app.patch('/courses/:id', (req, res) => {
const { id } = req.params;
const { title, description } = req.body;
const stmt = db.prepare('UPDATE courses SET title = ?, description = ? WHERE id = ?');
stmt.run([title, description, id], (error) => {
if (error) {
const templateData = {
error,
course: { title, description },
};
res.view('courses/edit', templateData);
return;
}
res.redirect('/courses');
});
});
Eliminación de un registro
Y para eliminar un registro también podemos utilizar el método run():
app.delete('/courses/:id', (req, res) => {
const { id } = req.params;
const stmt = db.prepare('DELETE FROM courses WHERE id = ?');
stmt.run(id, (err) => {
if (err) {
res.send(err);
return;
}
res.redirect('/courses');
});
});
Resumen
Ejemplo final:
import fastify from 'fastify';
import sqlite3 from 'sqlite3';
import view from '@fastify/view';
import pug from 'pug';
import formbody from '@fastify/formbody';
const app = fastify();
const port = 3000;
const db = new sqlite3.Database(':memory:');
const prepareDatabase = () => {
db.serialize(() => {
db.run(`
CREATE TABLE courses (
id INT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT
);
`);
});
const courses = [
{ id: 1, title: 'JavaScript', description: 'Curso de programación en JavaScript' },
{ id: 2, title: 'Fastify', description: 'Curso del framework Fastify' },
];
const stmt = db.prepare('INSERT INTO courses VALUES (?, ?, ?)');
courses.forEach((course) => {
stmt.run(course.id, course.title, course.description);
});
stmt.finalize();
};
prepareDatabase();
await app.register(formbody);
await app.register(view, { engine: { pug } });
app.get('/courses', (req, res) => {
db.all('SELECT * FROM courses', (error, data) => {
const templateData = {
courses: data,
error,
};
res.view('courses/index', templateData);
});
});
app.post('/courses/:id', (req, res) => {
const { id } = req.params;
const { title, description } = req.body;
const stmt = db.prepare('INSERT INTO courses(title, description) VALUES (?, ?)');
stmt.run([title, description], function (error) {
if (error) {
const templateData = {
error,
course: { title, description },
};
res.view('/course/new', templateData);
return;
}
res.redirect(`/courses/${this.lastID}`);
});
});
app.get('/courses/:id', (req, res) => {
const { id } = req.params;
db.get(`SELECT * FROM courses WHERE id = ${id}`, (err, data) => {
const templateData = {
course: data,
error,
};
res.view('courses/show', templateData);
});
});
app.patch('/courses/:id', (req, res) => {
const { id } = req.params;
const { title, description } = req.body;
const stmt = db.prepare('UPDATE courses SET title = ?, description = ? WHERE id = ?');
stmt.run([title, description, id], (error) => {
if (error) {
const templateData = {
error,
course: { title, description },
};
res.view('courses/edit', templateData);
return;
}
res.redirect('/courses');
});
});
app.delete('/courses/:id', (req, res) => {
const { id } = req.params;
const stmt = db.prepare('DELETE FROM courses WHERE id = ?');
stmt.run(id, (err) => {
if (err) {
res.send(err);
return;
}
res.redirect('/courses');
});
});
app.listen({ port }, () => {
console.log(`La aplicación de ejemplo se está ejecutando en el puerto ${port}`);
});
Trabajo independiente
Realiza todos los pasos de la lección en tu computadora con el ejemplo de la entidad de cursos.
- Haz cambios en tu aplicación y el repositorio de cursos, de modo que los repositorios trabajen con la base de datos.
- Haz lo mismo para la entidad del usuario.
- Sube los cambios a GitHub.
Materiales adicionales
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.