<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Nestjs &#8211; Jeff Santillán</title>
	<atom:link href="https://jeffsantillan.com/blog/category/nestjs/feed/" rel="self" type="application/rss+xml" />
	<link>https://jeffsantillan.com/blog</link>
	<description>Transformación Digital y Programación</description>
	<lastBuildDate>Fri, 19 Jun 2026 05:27:18 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>
	<item>
		<title>Nestjs manejo global de errores con Exception Filters</title>
		<link>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 05:06:50 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[REST API]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=300</guid>

					<description><![CDATA[NestJS por defecto devuelve errores genéricos como 500 Internal Server Error sin contexto. Un Exception Filter &#8230; <a title="Nestjs manejo global de errores con Exception Filters" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/"><span class="screen-reader-text">Nestjs manejo global de errores con Exception Filters</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">NestJS por defecto devuelve errores genéricos como <code>500 Internal Server Error</code> sin contexto. Un Exception Filter intercepta TODOS los errores de la app en un solo lugar antes de que lleguen al cliente, permitiéndote estandarizar las respuestas.</p>



<p class="wp-block-paragraph">Tenemos que crear un archivo llamado src/filter/http-exception.filter.ts</p>



<pre class="wp-block-code has-small-font-size"><code>import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Prisma } from '../../generated/prisma/client';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&lt;Response&gt;();
    const request = ctx.getRequest&lt;Request&gt;();

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let message: string | string&#91;] = 'Error interno del servidor';

    // Error de NestJS (404, 400, etc.)
    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const exceptionResponse = exception.getResponse();
      message =
        typeof exceptionResponse === 'object'
          ? (exceptionResponse as any).message
          : exceptionResponse;
    }

    // Error de Prisma — constraint unique (email o dni duplicado)
    else if (exception instanceof Prisma.PrismaClientKnownRequestError) {
      if (exception.code === 'P2002') {
        status = HttpStatus.CONFLICT; // 409
        const fields = (exception.meta?.target as string&#91;])?.join(', ');
        message = `Ya existe un registro con ese ${fields}`;
      }
      // Error de FK — registro relacionado no existe
      else if (exception.code === 'P2003') {
        status = HttpStatus.BAD_REQUEST; // 400
        message = 'El registro relacionado no existe';
      }
      // Registro no encontrado al hacer update/delete
      else if (exception.code === 'P2025') {
        status = HttpStatus.NOT_FOUND; // 404
        message = 'Registro no encontrado';
      }
    }

    response.status(status).json({
      success: false,
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}</code></pre>



<p class="wp-block-paragraph">@catch = Captura cualquier excepcion<br>@catch(HttpException) = Captura solo errores HTTP.<br>ArgumentHost = Es una bastraccion que permite acceder al request/response independientemente del protocolo (HTTP, WebSocket, microservicios)</p>



<p class="wp-block-paragraph">Luego registrarlo globalmente en el main.ts. importando el mismo archivo creado y registrándolo de manera global.</p>



<pre class="wp-block-code"><code>...
<strong>import { AllExceptionsFilter } from './filters/http-exception.filter';</strong>

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Exception Filter global
  <strong>app.useGlobalFilters(new AllExceptionsFilter());</strong>

...
}</code></pre>



<p class="wp-block-paragraph">Aunque existen niveles de aplicación como a un método o a un controlador (todos los endpoints) con @UseFilters() y toda la aplicación con <strong>app.useGlobalFilters</strong>()</p>



<p class="wp-block-paragraph">Antes el error genérico era algo como:</p>



<pre class="wp-block-code"><code>{ "statusCode": 500, "message": "Internal server error" }</code></pre>



<p class="wp-block-paragraph">Ahora seria algo asi</p>



<pre class="wp-block-code"><code>{
  "success": false,
  "statusCode": 409,
  "message": "Ya existe un registro con ese email",
  "timestamp": "2026-06-19T03:00:00.000Z",
  "path": "/docente"
}</code></pre>



<p class="wp-block-paragraph">Eso seria, podemos gestionar diferentes errores de manera centralizada.</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-manejo-global-de-errores-con-exception-filters/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs uso de Swagger para documentación</title>
		<link>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 04:34:51 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[swagger]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=296</guid>

					<description><![CDATA[Swagger es una herramienta que genera documentación interactiva de tu API automáticamente. En lugar de documentar &#8230; <a title="Nestjs uso de Swagger para documentación" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/"><span class="screen-reader-text">Nestjs uso de Swagger para documentación</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Swagger es una herramienta que genera documentación interactiva de tu API automáticamente. En lugar de documentar manualmente cada endpoint en un Word o Notion, Swagger lee tu código y genera una interfaz web donde cualquier developer puede ver y probar la API sin usar Postman o PowerShell</p>



<p class="wp-block-paragraph">Extendiendo el proyecto de Usuario: <a href="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/" data-type="link" data-id="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/">Enlace</a></p>



<p class="wp-block-paragraph"><strong>Podemos incluirle el Swagger para documentar la API</strong>.</p>



<p class="wp-block-paragraph">instalar</p>



<pre class="wp-block-code"><code>npm install @nestjs/swagger</code></pre>



<p class="wp-block-paragraph">Es asi que al archivo de main.ts le voy a incluir o habilitar Swagger, hay que importar en el main y agregar las siguientes lineas</p>



<pre class="wp-block-code has-small-font-size"><code>import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

// Swagger
  const config = new DocumentBuilder()
      .setTitle('Sistema de Usuario API')
      .setDescription('API para gestion de usuarios')
      .setVersion('1.0')
      .addTag('usuario')
      .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);
</code></pre>



<p class="wp-block-paragraph">Quedando el archivo main.ts de la siguiente manera.</p>



<pre class="wp-block-code"><code>import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common'; // Validator
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));

  // Swagger
  const config = new DocumentBuilder()
      .setTitle('Sistema de Usuario API')
      .setDescription('API para gestion de usuarios')
      .setVersion('1.0')
      .addTag('usuario')
      .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();</code></pre>



<p class="wp-block-paragraph">Luego hay que decorar los DTOs, importando el ApiProperty y utilizar el decorador en cada campo con la estructura de datos con example y description.</p>



<pre class="wp-block-code"><code>import { IsEmail, IsNotEmpty, IsString, IsNumber, Min, Max } from 'class-validator';
import { Type } from 'class-transformer';
<strong>import { ApiProperty } from '@nestjs/swagger';</strong>

export class CreateUsuarioDto {

    <strong>@ApiProperty({ example: 'Jefferson', description: 'Nombre del usuario'})</strong>
    @IsNotEmpty({message: 'El nombre es obligatorio'})
    @IsString()
    nombre!: string;

    <strong>@ApiProperty({ example: 'jeffsp@gmail.com', description: 'Correo del usuario'})</strong>
    @IsNotEmpty()
    @IsEmail({}, {message: 'Email inválido'})
    email!: string;

    <strong>@ApiProperty({ example: '30', description: 'Edad del usuario'})</strong>
    @IsNumber()
    @Min(18, {message: 'Debe de ser mayor de 18'})
    @Max(99)
    @Type( () =&gt; Number) //Aqui se utiliza la clase transform explicitamente.
    edad!: number;
}</code></pre>



<p class="wp-block-paragraph">Finalmente, mejorar el controlador con: ApiTags, ApiOperation y ApiResponse</p>



<pre class="wp-block-code"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
<strong>import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';</strong>

<em><strong>@ApiTags('usuario')</strong></em>
@Controller('usuario')
export class UsuarioController {
  constructor(private readonly usuarioService: UsuarioService) {}

  <strong><em>@ApiOperation({summary:'Crear nuevo usuario'})
  @ApiResponse({status:201, description:'Usuario creado'})
  @ApiResponse({status:400, description:'Datos inválidos'})</em></strong>
  @Post()
  create(@Body() createUsuarioDto: CreateUsuarioDto) {
    return this.usuarioService.create(createUsuarioDto);
  }

  @ApiOperation({summary: 'Listar todos los usuarios'})
  @ApiResponse({status:200, description:'Lista de usuarios'})
  @Get()
  findAll() {
    return this.usuarioService.findAll();
  }

  @ApiOperation({summary: 'Obtener usuario por ID'})
  @ApiResponse({status:200, description: 'Usuario encontrado'})
  @ApiResponse({status:404, description: 'Usuario no encontrado'})
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usuarioService.findOne(+id);
  }

  @ApiOperation({summary: 'Actualizar usuario'})
  @ApiResponse({status: 200, description: 'Usuario actualizado'})
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUsuarioDto: UpdateUsuarioDto) {
    return this.usuarioService.update(+id, updateUsuarioDto);
  }

  @ApiOperation({summary: 'Eliminar usuario'})
  @ApiResponse({status: 200, description: 'Usuario Eliminado'})
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usuarioService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph">Descripción de decoradores:</p>



<ul class="wp-block-list">
<li>@ApiTags(&#8216;usuario&#8217;) = Agrupa endpoints bajo una sección en la UI</li>



<li>@ApiProperty() = Documenta cada campo del DTO con ejemplo</li>



<li>@ApiOperation() = Describe qué hace el endpoint</li>



<li>@ApiResponse() = Documenta los posibles códigos de respuesta</li>
</ul>



<p class="wp-block-paragraph">Ello seria suficiente para tener una API documentada.</p>



<p class="wp-block-paragraph">Una vez configurado, accede a <strong><code>http://localhost:3000/api</code> </strong>para ver la documentación interactiva.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="882" height="885" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47.png" alt="" class="wp-image-297" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47.png 882w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-300x300.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-150x150.png 150w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-47-768x771.png 768w" sizes="(max-width: 882px) 100vw, 882px" /></figure>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-uso-de-swagger-para-documentacion/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs uso de Validator y Transformers</title>
		<link>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/#comments</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 23:28:07 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[REST API]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[programacion]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=283</guid>

					<description><![CDATA[Sin validaciones, un usuario puede enviar cualquier dato a tu API — un email inválido, una &#8230; <a title="Nestjs uso de Validator y Transformers" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/"><span class="screen-reader-text">Nestjs uso de Validator y Transformers</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Sin validaciones, un usuario puede enviar cualquier dato a tu API — un email inválido, una edad negativa, o campos vacíos — y llegarían directo a la base de datos. <code>class-validator</code> y <code>class-transformer</code> resuelven esto antes de que el request llegue al servicio</p>



<p class="wp-block-paragraph"><strong>Descripción</strong></p>



<p class="wp-block-paragraph"><em><strong><span style="text-decoration: underline;">class-transform </span></strong></em></p>



<p class="wp-block-paragraph">Se encarga de cambiar al tipo correcto de dato.</p>



<p class="wp-block-paragraph">En el servicio no se ven por que hacen su trabajo antes de que llegue el request ahí. El servicio recibe los datos ya validados y transformados.</p>



<p class="wp-block-paragraph">Donde actúan los transformers en el Param(&#8216;id&#8217;) del controlador.</p>



<pre class="wp-block-code has-small-font-size"><code>// Sin transform: true
@Get(':id')
findOne(@Param('id') id: string) {  // id llega como STRING "1"
  return this.usuarioService.findOne(+id); // necesitas el +id para convertir
}

// Con transform: true puedes hacer esto:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) { // id ya llega como NUMBER
  return this.usuarioService.findOne(id); // sin el +
}</code></pre>



<p class="wp-block-paragraph">El @body tambien convierte los tipos automaticamente</p>



<pre class="wp-block-code has-small-font-size"><code>// Si el DTO tiene edad: number
// y el cliente manda: { "edad": "25" }  ← string
// transform: true lo convierte a número automáticamente antes de llegar al servicio</code></pre>



<p class="wp-block-paragraph">Se coloco un transform explicito dentro del DTO create (CreateUsuarioDto)</p>



<pre class="wp-block-code has-medium-font-size"><code>@Type(() =&gt; Number)</code></pre>



<p class="wp-block-paragraph"><strong><em><span style="text-decoration: underline;">class-validator</span></em></strong></p>



<p class="wp-block-paragraph">Valida que los datos sean los correctos.</p>



<p class="wp-block-paragraph"><strong><em><span style="text-decoration: underline;">ValidationPipe</span></em></strong></p>



<p class="wp-block-paragraph">Se encarga de orquestar ambos.</p>



<p class="wp-block-paragraph">Vamos a utilizar el validator y transformers.</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Lo primero hay que instalar las dependencias.</p>



<pre class="wp-block-code has-small-font-size"><code>npm install class-validator class-transformer</code></pre>



<p class="wp-block-paragraph">voy a modificar el archivo principal main.ts importante los ValidationPipe y agregando unas lineas para su uso.</p>



<pre class="wp-block-code has-small-font-size"><code>import { ValidationPipe } from '@nestjs/common'; // Validator

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));</code></pre>



<p class="wp-block-paragraph"><strong>Sin </strong>whitelist considera que el campo extra llega al servicio<br><strong>Con </strong>whitelist: true considera que campo extra se elimina silenciosamente<br><strong>Con </strong>forbidNonWhitelisted: true >> lanza error 400</p>



<p class="wp-block-paragraph">Quedando de esta manera.</p>



<pre class="wp-block-code has-small-font-size"><code>import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common'; // Validator

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // Elimina los campos que no estan en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si llegan campos extra
    transform: true,  // Transforma tipos automaticamente.
  }));

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();</code></pre>



<p class="wp-block-paragraph">He generado una entidad llamada Usuario para poder hacer las validaciones.</p>



<pre class="wp-block-code has-small-font-size"><code>nest g resource Usuario</code></pre>



<p class="wp-block-paragraph">Entonces vamos a utilizar arrays en memoria dado que no me he conectado a una base de datos prisma.</p>



<pre class="wp-block-code has-small-font-size"><code>// Usaremos arrays en memoria, dado que no conecte a prisma
export class Usuario {
    id!: number;
    nombre!: string;
    email!: string;
    edad!: number;
}</code></pre>



<p class="wp-block-paragraph">DTOs <strong>(Simple sin validaciones)</strong></p>



<pre class="wp-block-code has-small-font-size"><code>export class CreateUsuarioDto {
    nombre!: string;
    email!: string;
    edad!: number;    
}</code></pre>



<p class="wp-block-paragraph">Ahora le voy a incluir las validaciones en una primera version.</p>



<pre class="wp-block-code has-small-font-size"><code>import { IsEmail, IsNotEmpty, IsString, IsNumber, Min, Max } from 'class-validator';
<em>import { Type } from 'class-transformer';</em>

export class CreateUsuarioDto {

    @IsNotEmpty({message: 'El nombre es obligatorio'})
    @IsString()
    nombre!: string;

    @IsNotEmpty()
    @IsEmail({}, {message: 'Email inválido'})
    email!: string;

    @IsNumber()
    @Min(18, {message: 'Debe de ser mayor de 18'})
    @Max(99)
    @Type( () =&gt; Number) //Aqui se utiliza la clase transform explicitamente.
    edad!: number;
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateUsuarioDto } from './create-usuario.dto';

export class UpdateUsuarioDto extends PartialType(CreateUsuarioDto) {}</code></pre>



<p class="wp-block-paragraph">Se define el servicio.</p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
import { Usuario } from './entities/usuario.entity'; // Importar entity

@Injectable()
export class UsuarioService {

  // Array en memoria como una base de datos temporal
  private usuarios: Usuario&#91;] = &#91;]
  private idCounter = 1;

  create(data: CreateUsuarioDto): Usuario {
    const usuario: Usuario = {
      id: this.idCounter++,
      ...data,
    };
    this.usuarios.push(usuario);
    return usuario;
  }

  findAll(): Usuario&#91;] {
    return this.usuarios;
  }

  findOne(id: number): Usuario {
    const usuario = this.usuarios.find(u =&gt; u.id === id);
    if (!usuario) throw new NotFoundException(`Usuario ${id} no encontrado`);
    return usuario;
  }

  update(id: number, data: UpdateUsuarioDto): Usuario {
    const usuario = this.findOne(id);
    Object.assign(usuario, data) // Actualiza solo los campos enviados
    return usuario;
  }

  remove(id: number): {message: string} {
    const index = this.usuarios.findIndex(u =&gt; u.id ===id);
    if (index === -1) throw new NotFoundException(`Usuario ${id} no encontrado`);
    this.usuarios.splice(index, 1);
    return {message: `Usuario ${id} eliminado`};
  }
  
}</code></pre>



<p class="wp-block-paragraph">En el controlador y el modulo, no hay mayor cambio.</p>



<p class="wp-block-paragraph">Formas de validar la operaciones CRUD de Usuario.</p>



<pre class="wp-block-code has-small-font-size"><code># Crear
Invoke-WebRequest -Uri http://localhost:3000/usuario -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Jeff","email":"jeff@gmail.com","edad":25}' | Select-Object -ExpandProperty Content

# Listar
Invoke-WebRequest -Uri http://localhost:3000/usuario -Method GET | Select-Object -ExpandProperty Content | ConvertFrom-Json

# Actualizar
Invoke-WebRequest -Uri http://localhost:3000/usuario/1 -Method PATCH -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Jeff Santillan"}' | Select-Object -ExpandProperty Content

# Eliminar
Invoke-WebRequest -Uri http://localhost:3000/usuario/1 -Method DELETE | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Validaciones</strong></p>



<p class="wp-block-paragraph">Error por ingreso de edad superior a 99</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="234" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1024x234.png" alt="" class="wp-image-286" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1024x234.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-300x69.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-768x176.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44-1300x298.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-44.png 1511w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Mensaje de error por que no se ingreso un nombre:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="215" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1024x215.png" alt="" class="wp-image-287" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1024x215.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-300x63.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-768x161.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45-1300x273.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-45.png 1461w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Mensaje de error por un email no valido.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="166" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1024x166.png" alt="" class="wp-image-288" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1024x166.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-300x49.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-768x124.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46-1300x210.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-46.png 1465w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Se logra la validación de manera exitosa.</p>



<pre class="wp-block-code has-small-font-size"><code>{
  "id": 1,
  "nombre": "Jeff",
  "email": "jeff@gmail.com",
  "edad": 25
}</code></pre>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-uso-de-validator-y-transformers/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs aplicación de matricula a curso – Parte IV</title>
		<link>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 20:19:15 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=272</guid>

					<description><![CDATA[Finalizando el mini proyecto, vamos a construir la estructura para el curso y su correspondiente matricula. &#8230; <a title="Nestjs aplicación de matricula a curso – Parte IV" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/"><span class="screen-reader-text">Nestjs aplicación de matricula a curso – Parte IV</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Finalizando el mini proyecto, vamos a construir la estructura para el <strong>curso </strong>y su correspondiente <strong>matricula</strong>.</p>



<p class="wp-block-paragraph">Vamos a empezar definiendo la funcionalidad para curso.</p>



<p class="wp-block-paragraph"><strong><span style="text-decoration: underline;">CURSO</span>:</strong></p>



<p class="wp-block-paragraph"><em>Entity</em>:</p>



<pre class="wp-block-code has-small-font-size"><code>export class Curso {
    id!: number;
    nombre!: string;
    codigo!: string;
    creditos!: number;
    docenteId!: number;
}</code></pre>



<p class="wp-block-paragraph"><em>DTOs, create y update:</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class <strong>CreateCursoDto </strong>{
    nombre!: string;
    codigo!: string;
    creditos!: number;
    docenteId!: number;
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateCursoDto } from './create-curso.dto';

export class <strong>UpdateCursoDto </strong>extends PartialType(CreateCursoDto) {}</code></pre>



<p class="wp-block-paragraph"><em>Services</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateCursoDto } from './dto/create-curso.dto';
import { UpdateCursoDto } from './dto/update-curso.dto';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class CursoService {

  constructor(private prisma: PrismaService){}

  async create(data: CreateCursoDto) {    
    // Necesario es verificar que el docente exista antes de crear un curso.
    const docente = await this.prisma.docente.findUnique({
      where: { id: data.docenteId},
    });
    if(!docente) throw new NotFoundException(`Docente ${data.docenteId} no encontrado`);
    return this.prisma.curso.create({data});
  }

  findAll() {
    return this.prisma.curso.findMany({
      include: {
        docente: true, //Muestra al docente.
      }
    });
  }

  async findOne(id: number) {
    const curso = await this.prisma.curso.findUnique({
      where: {id},
      include: { docente: true}
    });
    if(!curso) throw new NotFoundException(`Curso ${id} no encontrado`);
    return curso;
  }

  async update(id: number, data: UpdateCursoDto) {
    await this.findOne(id);
    return this.prisma.curso.update({ where: {id}, data });
  }

  async remove(id: number) {
    await this.findOne(id);
    return this.prisma.curso.delete({ where: {id} });
  }
}</code></pre>



<p class="wp-block-paragraph">Hay validaciones importantes como la existencia previa de un docente antes de crear el curso.</p>



<p class="wp-block-paragraph"><em>Controller</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { CursoService } from './curso.service';
import { CreateCursoDto } from './dto/create-curso.dto';
import { UpdateCursoDto } from './dto/update-curso.dto';

@Controller('curso')
export class CursoController {
  constructor(private readonly cursoService: CursoService) {}

  @Post()
  create(@Body() body: CreateCursoDto) {
    return this.cursoService.create(body);
  }

  @Get()
  findAll() {
    return this.cursoService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.cursoService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() body: UpdateCursoDto) {
    return this.cursoService.update(+id, body);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.cursoService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph"><em>Module</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Module } from '@nestjs/common';
import { CursoService } from './curso.service';
import { CursoController } from './curso.controller';

@Module({
  controllers: &#91;CursoController],
  providers: &#91;CursoService],
})
export class CursoModule {}</code></pre>



<p class="wp-block-paragraph"><strong><span style="text-decoration: underline;">MATRICULA</span>:</strong></p>



<p class="wp-block-paragraph">Para concluir</p>



<p class="wp-block-paragraph"><em>Entity</em>:</p>



<pre class="wp-block-code has-small-font-size"><code>export class Matricula {
    id!: number;
    alumnoId!: number; //FK de alumno
    cursoId!: number; //FK de curso
    fecha!: Date;
    estado!: string;
}</code></pre>



<p class="wp-block-paragraph">Aqui estamos declarando las foreign keys o las llaves que relacionan con otra tabla.</p>



<p class="wp-block-paragraph"><em>DTOs, create y update:</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class <strong>CreateMatriculaDto </strong>{
    alumnoId!: number; //FK de alumno
    cursoId!: number; //FK de curso
}</code></pre>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateMatriculaDto } from './create-matricula.dto';

export class <strong>UpdateMatriculaDto </strong>extends PartialType(CreateMatriculaDto) {
    estado!: string; // ACTIVO | RETIRADO | APROBADO
}</code></pre>



<p class="wp-block-paragraph"><em>Services</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { CreateMatriculaDto } from './dto/create-matricula.dto';
import { UpdateMatriculaDto } from './dto/update-matricula.dto';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class MatriculaService {

  constructor(private prisma: PrismaService){};

  async create(data: CreateMatriculaDto) {
    // Verificar que el alumno existe
    const alumno = await this.prisma.alumno.findUnique({
      where: { id: data.alumnoId }
    });
    if(!alumno) throw new NotFoundException(`Alumno ${data.alumnoId} no encontrado`);

    // Verificar que un curso existe
    const curso = await this.prisma.curso.findUnique({
      where: { id: data.cursoId }
    });
    if(!curso) throw new NotFoundException(`Curso ${data.cursoId} no encontrado`);

    // Verificar que no esta matriculado
    const existe = await this.prisma.matricula.findUnique({
      where: {
        alumnoId_cursoId: {
          alumnoId: data.alumnoId,
          cursoId: data.cursoId
        }
      }
    });
    if (existe) throw new BadRequestException('El alumno ya está matriculado en este curso');

    return this.prisma.matricula.create({
      data, 
      include: {
        alumno: true,
        curso: { include: {docente: true} }
      }
    });

  }

  findAll() {
    return this.prisma.matricula.findMany({
      include: {
        alumno: true,
        curso: { include: { docente: true }} // Curso con su correspondiente docente.
      }
    })
  }

  async findOne(id: number) {
    const matricula = await this.prisma.matricula.findUnique({
      where: {id},
      include: {
        alumno: true,
        curso: { include: { docente: true}}
      }
    });
    if (!matricula) throw new NotFoundException(`Matricula ${id} no encontrada`);
    return matricula;
  }

  async update(id: number, data: UpdateMatriculaDto) {
    await this.findOne(id);
    return this.prisma.matricula.update({ where: {id}, data})
  }

  async remove(id: number) {
    await this.findOne(id) ;
    return this.prisma.matricula.delete({ where : {id} });
  }

  // Curso de un alumno especifico
  findByAlumno(alumnoId: number){
    return this.prisma.matricula.findMany({
      where: { alumnoId },
      include: {
        curso: { include : {docente: true}}
      }
    });
  }

}</code></pre>



<p class="wp-block-paragraph"><em>Controller</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { MatriculaService } from './matricula.service';
import { CreateMatriculaDto } from './dto/create-matricula.dto';
import { UpdateMatriculaDto } from './dto/update-matricula.dto';

@Controller('matricula')
export class MatriculaController {
  constructor(private readonly matriculaService: MatriculaService) {}

  @Post()
  create(@Body() body: CreateMatriculaDto) {
    return this.matriculaService.create(body);
  }

  @Get()
  findAll() {
    return this.matriculaService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.matriculaService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() body: UpdateMatriculaDto) {
    return this.matriculaService.update(+id, body);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.matriculaService.remove(+id);
  }

  // Curso de un alumno especifico
  @Get('alumno/:alumnoId')
  findByAlumno(@Param('alumnoId') alumnoId: string){
    return this.matriculaService.findByAlumno(+alumnoId);
  }

}</code></pre>



<p class="wp-block-paragraph"><em>Module</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Module } from '@nestjs/common';
import { MatriculaService } from './matricula.service';
import { MatriculaController } from './matricula.controller';

@Module({
  controllers: &#91;MatriculaController],
  providers: &#91;MatriculaService],
})
export class MatriculaModule {}</code></pre>



<p class="wp-block-paragraph">Con esto concluimos el registro de matricula de un alumno dentro de un curso.</p>



<p class="wp-block-paragraph"><strong>Operaciones para Curso</strong></p>



<p class="wp-block-paragraph">Crear un curso con el docente con ID 1</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Matematicas","codigo":"MAT101","creditos":4,"docenteId":1}' | Select-Object -ExpandProperty Content</code></pre>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Programacion","codigo":"PRG101","creditos":5,"docenteId":1}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Listar cursos</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso -Method GET -<strong>UseBasicParsing </strong>| Select-Object -ExpandProperty Content | <strong>ConvertFrom-Json</strong></code></pre>



<p class="wp-block-paragraph">Obtener curso por ID</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method GET | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">Actualizar curso</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method PATCH -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Matematica Avanzada"}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Eliminar</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/curso/1 -Method DELETE | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Todas las operaciones ya estan disponibles para el curso.</p>



<p class="wp-block-paragraph"><strong>Operaciones para Matricula</strong></p>



<p class="wp-block-paragraph">Matricular alumno a un curso.</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"alumnoId":1,"cursoId":1}' -UseBasicParsing | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Ver todas las matriculas</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula -Method GET -UseBasicParsing | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">Curso de un alumno en especifico.</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/matricula/alumno/1 -Method GET -UseBasicParsing | Select-Object -ExpandProperty Content | ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">¡Saludos!</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iv/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs aplicación de matricula a curso &#8211; Parte III</title>
		<link>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iii/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iii/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:04:23 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=267</guid>

					<description><![CDATA[Vamos a continuar con la creacion del proyecto, para esta ocasion vamos a crear la sección &#8230; <a title="Nestjs aplicación de matricula a curso &#8211; Parte III" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iii/"><span class="screen-reader-text">Nestjs aplicación de matricula a curso &#8211; Parte III</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Vamos a continuar con la creacion del proyecto, para esta ocasion vamos a crear la sección de docente.</p>



<p class="wp-block-paragraph">Definimos las entidades y los DTOs.</p>



<p class="wp-block-paragraph">Entities (Entidades):</p>



<p class="wp-block-paragraph"><em>docente.entity.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class Docente {
    id!: number;
    nombre!: string;
    apellido!: string;
    dni!: string;
    email!: string;
}</code></pre>



<p class="wp-block-paragraph">DTOs (Data Transfer Object)</p>



<p class="wp-block-paragraph"><em>create-docente.dto.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>export class CreateDocenteDto {
    nombre!: string;
    apellido!: string;
    dni!: string;
    email!: string;
}</code></pre>



<p class="wp-block-paragraph">Para actualizar se entiende que extiende de Create</p>



<p class="wp-block-paragraph"><em>update-docente.dto.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateDocenteDto } from './create-docente.dto';

export class UpdateDocenteDto extends PartialType(CreateDocenteDto) {}</code></pre>



<p class="wp-block-paragraph">Ahora si empezamos con el servicio.</p>



<p class="wp-block-paragraph"><em>docente.service.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateDocenteDto } from './dto/create-docente.dto';
import { UpdateDocenteDto } from './dto/update-docente.dto';

@Injectable()
export class DocenteService {

  constructor(private prisma: PrismaService){}

  create(data: CreateDocenteDto) {
    return this.prisma.docente.create({ data })
  }

  findAll() {
    return this.prisma.docente.findMany({
      include: { cursos: true }
    });
  }

  async findOne(id: number) {
    const docente = await this.prisma.docente.findUnique({
      where: { id },
      include: { cursos: true }
    });
    if (!docente) throw new NotFoundException('Docente ${id} no ha sido encontrado');
    return docente;
  }

  async update(id: number, data: UpdateDocenteDto) {
    await this.findOne(id);
    return this.prisma.docente.update({
      where: { id },
      data
    });
  }

  async remove(id: number) {
    await this.findOne(id);
    return this.prisma.docente.delete({ where: { id }});
  }
}</code></pre>



<p class="wp-block-paragraph">Finalmente, creamos o si utilizamos los generadores, solo tendríamos que adecuar el Controller por que ya viene todo pre hecho.</p>



<p class="wp-block-paragraph"><em>docente.controller.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { DocenteService } from './docente.service';
import { CreateDocenteDto } from './dto/create-docente.dto';
import { UpdateDocenteDto } from './dto/update-docente.dto';

@Controller('docente')
export class DocenteController {
  constructor(private readonly docenteService: DocenteService) {}

  @Post()
  create(@Body() body: CreateDocenteDto) {
    return this.docenteService.create(body);
  }

  @Get()
  findAll() {
    return this.docenteService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.docenteService.findOne(+id);
  }

  @<strong>Patch</strong>(':id')
  update(@Param('id') id: string, @Body() body: UpdateDocenteDto) {
    return this.docenteService.update(+id, body);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.docenteService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph">Para este caso utilizamos el método PATCH en lugar de PUT, así tendremos que enviar el cambio o el body del cambio puntual y no toda la estructura.</p>



<p class="wp-block-paragraph">Modulo. Realmente, no hay nada que modificar aquí si fue creado con los generadores.</p>



<p class="wp-block-paragraph"><em>docente.module.ts</em></p>



<pre class="wp-block-code has-small-font-size"><code>import { Module } from '@nestjs/common';
import { DocenteService } from './docente.service';
import { DocenteController } from './docente.controller';

@Module({
  controllers: &#91;DocenteController],
  providers: &#91;DocenteService],
})
export class DocenteModule {}</code></pre>



<p class="wp-block-paragraph">Bien, vamos a probar con PowerShell.</p>



<p class="wp-block-paragraph">Levantamos el servicio con:</p>



<pre class="wp-block-code"><code>npm run start:dev</code></pre>



<p class="wp-block-paragraph">Los comandos para crear, listar, consultar único registro, actualizar y eliminar.</p>



<p class="wp-block-paragraph">Utilizaremos el comando : Select-Object -ExpandProperty Content para que solo se muestra la respuesta en formato json.</p>



<pre class="wp-block-code"><code>| Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Se puede complementar con:</p>



<pre class="wp-block-code"><code>| ConvertFrom-Json</code></pre>



<p class="wp-block-paragraph">Considera también incluir el parámetro -UseBasicParsing</p>



<pre class="wp-block-code"><code>-UseBasicParsing</code></pre>



<p class="wp-block-paragraph"><strong>Crear</strong></p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/docente -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Carlos","apellido":"Perez","dni":"12345678","email":"carlos@edu.pe"}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Listar</strong></p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/docente -Method GET | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Obtener un registro por ID</strong></p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/docente/1 -Method GET | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Actualizar </strong></p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/docente/1 -Method PUT -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Carlos Alberto"}' | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph"><strong>Eliminar</strong></p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/docente/1 -Method DELETE | Select-Object -ExpandProperty Content</code></pre>



<p class="wp-block-paragraph">Ya tenemos las funciones hacia la tabla operando.</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-iii/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs aplicación de matricula a curso &#8211; Parte II</title>
		<link>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/#comments</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 04:31:25 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=249</guid>

					<description><![CDATA[Toca escribir la funcionalidad del código de servicios y controladores. Considera que la tablas: Así que &#8230; <a title="Nestjs aplicación de matricula a curso &#8211; Parte II" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/"><span class="screen-reader-text">Nestjs aplicación de matricula a curso &#8211; Parte II</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Toca escribir la funcionalidad del código de servicios y controladores.</p>



<p class="wp-block-paragraph">Considera que la tablas:</p>



<ul class="wp-block-list">
<li>Alumno = No depende de nadie</li>



<li>Docente = No depende de nadie</li>



<li>Curso = Depende de Docente</li>



<li>Matricula = Depende de Alumno y Curso.</li>
</ul>



<p class="wp-block-paragraph">Así que lo mejor es empezar con alumno</p>



<p class="wp-block-paragraph">Empezamos por Entities y los DTOs, </p>



<p class="wp-block-paragraph">Entities:</p>



<pre class="wp-block-code has-small-font-size"><code>export class Alumno {
    id!: number;
    nombre!: string;
    apellido!: string;
    dni!: string;
    email!: string;
    createdAt!: Date;
}</code></pre>



<p class="wp-block-paragraph">DTOs</p>



<pre class="wp-block-code has-small-font-size"><code>export class CreateAlumnoDto {
    nombre!: string;
    apellido!: string;
    dni!: string;
    email!: string;    
}</code></pre>



<p class="wp-block-paragraph">Actualizar extiende parcial (PartialType) así que no es necesario definir, ya que incluye los datos de CreateAlumnoDto.</p>



<pre class="wp-block-code has-small-font-size"><code>import { PartialType } from '@nestjs/mapped-types';
import { CreateAlumnoDto } from './create-alumno.dto';

export class UpdateAlumnoDto extends PartialType(CreateAlumnoDto) {  
}</code></pre>



<p class="wp-block-paragraph">Ahora si hay que elaborar el servicio.</p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateAlumnoDto } from './dto/create-alumno.dto';
import { UpdateAlumnoDto } from './dto/update-alumno.dto';

import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class AlumnoService {

  constructor(private prisma: PrismaService){}

  create(data: CreateAlumnoDto) {
    return this.prisma.alumno.create({ data })
  }

  findAll() {
    return this.prisma.alumno.findMany();
  }

  async findOne(id: number) {
    const alumno = await this.prisma.alumno.findUnique({ where: {id} });
    if (!alumno) throw new NotFoundException('Alumno ${id} no fue encontrado');
    return alumno;
  }

  async update(id: number, data: UpdateAlumnoDto) {
    await this.findOne(id);
    return this.prisma.alumno.update({where: {id}, data});
  }

  async remove(id: number) {
    await this.findOne(id);
    return this.prisma.alumno.delete({where: {id} });
  }
}</code></pre>



<p class="wp-block-paragraph">finalmente, el controlador practicamente, ya viene implementado.</p>



<pre class="wp-block-code has-small-font-size"><code>import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { AlumnoService } from './alumno.service';
import { CreateAlumnoDto } from './dto/create-alumno.dto';
import { UpdateAlumnoDto } from './dto/update-alumno.dto';

@Controller('alumno')
export class AlumnoController {
  constructor(private readonly alumnoService: AlumnoService) {}

  @Post()
  create(@Body() createAlumnoDto: CreateAlumnoDto) {
    return this.alumnoService.create(createAlumnoDto);
  }

  @Get()
  findAll() {
    return this.alumnoService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.alumnoService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateAlumnoDto: UpdateAlumnoDto) {
    return this.alumnoService.update(+id, updateAlumnoDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.alumnoService.remove(+id);
  }
}</code></pre>



<p class="wp-block-paragraph">Ejecutemos el proyecto.</p>



<pre class="wp-block-code"><code>npm run start:dev</code></pre>



<p class="wp-block-paragraph">Registro un alumno con el Invoke-WebRequest</p>



<pre class="wp-block-code has-small-font-size"><code>Invoke-WebRequest -Uri http://localhost:3000/alumno -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"nombre":"Jeff","apellido":"Santillan","dni":"12345678","email":"jeff@gmail.com"}'</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="429" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-41-1024x429.png" alt="" class="wp-image-261" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-41-1024x429.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-41-300x126.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-41-768x321.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-41.png 1173w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Recuperamos los alumnos registrados.</p>



<pre class="wp-block-code"><code>Invoke-WebRequest -Uri http://localhost:3000/alumno -Method GET</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="391" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-43-1024x391.png" alt="" class="wp-image-263" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-43-1024x391.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-43-300x115.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-43-768x293.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-43.png 1168w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Ya estamos haciendo uso de nuestra base de datos sqlite con prisma.</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Nestjs aplicación de matricula a curso &#8211; Parte I</title>
		<link>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso/</link>
					<comments>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 03:33:13 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[prisma]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=224</guid>

					<description><![CDATA[Es necesario crear el proyecto. Luego vamos a utilizar Prisma para gestionar los datos. En caso &#8230; <a title="Nestjs aplicación de matricula a curso &#8211; Parte I" class="hm-read-more" href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso/"><span class="screen-reader-text">Nestjs aplicación de matricula a curso &#8211; Parte I</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Es necesario crear el proyecto.</p>



<pre class="wp-block-code"><code>nest new matricula-app</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="984" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-31-1024x984.png" alt="" class="wp-image-225" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-31-1024x984.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-31-300x288.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-31-768x738.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-31.png 1062w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Luego vamos a utilizar Prisma para gestionar los datos.</p>



<p class="wp-block-paragraph">En caso de no tener prisma es necesario instalarlo con npm</p>



<pre class="wp-block-code"><code>npm install prisma @prisma/client</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="446" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-32-1024x446.png" alt="" class="wp-image-226" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-32-1024x446.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-32-300x131.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-32-768x335.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-32.png 1062w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Tenemos que estar dentro de la carpeta del proyecto para poder ejecutar el comando de prisma npx.</p>



<p class="wp-block-paragraph">Vamos a definir el proveedor de base de datos que utilizare, para este ejercicio será sqlite.</p>



<pre class="wp-block-code"><code>npx prisma init --datasource-provider sqlite</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="555" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-33-1024x555.png" alt="" class="wp-image-227" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-33-1024x555.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-33-300x163.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-33-768x416.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-33.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Tenemos que editar el archivo de <em>prisma/schema.prisma</em> del proyecto con la estructura de datos a trabajar.</p>



<p class="wp-block-paragraph">Se presenta la definición de tablas solo con campos sencillos, sin relaciones aún</p>



<pre class="wp-block-code has-small-font-size"><code>model Alumno {
  id Int @id @default(autoincrement())
  nombre String
  apellido String
  dni String @unique
  email String @unique
  createdAt Datetime @default(now())  
}

model Docente {
  id Int @id @default(autoincrement())
  nombre String
  apellido String
  dni String @unique
  email String @unique    
}

model Curso {
  id Int @id @default(autoincrement())
  nombre String
  codigo String @unique
  creditos Int    
}

model Matricula {
  id Int @id @default(autoincrement())
  fecha Datetime @default(now())
  estado String @default("ACTIVO")
}</code></pre>



<p class="wp-block-paragraph">Ahora vamos a establecer las FK y relaciones 1:n y n:1 con comentarios.</p>



<pre class="wp-block-code has-small-font-size"><code>model Alumno {
  id         Int         @id @default(autoincrement())
  nombre     String
  apellido   String
  dni        String      @unique
  email      String      @unique
  createdAt  DateTime    @default(now())
  matriculas Matricula&#91;] // Relación 1:N — un alumno puede tener múltiples matrículas
}

model Docente {
  id       Int     @id @default(autoincrement())
  nombre   String
  apellido String
  dni      String  @unique
  email    String  @unique
  cursos   Curso&#91;] // Relación 1:N — un docente puede dictar múltiples cursos
}

model Curso {
  id         Int         @id @default(autoincrement())
  nombre     String
  codigo     String      @unique // Código único del curso, ej: MAT101
  creditos   Int
  docenteId  Int         // FK → Docente (responsable del curso)
  docente    Docente     @relation(fields: &#91;docenteId], references: &#91;id]) // Relación N:1 — muchos cursos pueden tener un docente
  matriculas Matricula&#91;] // Relación 1:N — un curso puede tener múltiples alumnos matriculados
}

model Matricula {
  id       Int      @id @default(autoincrement())
  alumnoId Int      // FK → Alumno
  cursoId  Int      // FK → Curso
  fecha    DateTime @default(now())
  estado   String   @default("ACTIVO") // Estados posibles: ACTIVO | RETIRADO | APROBADO
  alumno   Alumno   @relation(fields: &#91;alumnoId], references: &#91;id]) // Relación N:1 — muchas matrículas pertenecen a un alumno
  curso    Curso    @relation(fields: &#91;cursoId], references: &#91;id])   // Relación N:1 — muchas matrículas pertenecen a un curso

  @@unique(&#91;alumnoId, cursoId]) // Restricción: un alumno no puede matricularse dos veces al mismo curso
}</code></pre>



<p class="wp-block-paragraph">Luego hay que ejecutar la migracion</p>



<pre class="wp-block-code"><code>npx prisma migrate dev --name init</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="875" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-34-1024x875.png" alt="" class="wp-image-230" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-34-1024x875.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-34-300x256.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-34-768x656.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-34.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Migración realizada se creo el archivo de base de datos sqlite -&gt; dev.db</p>



<p class="wp-block-paragraph">El nombre del archivo se definió en la <strong>DATABASE_URL</strong> del archivo <em>.env</em>, también vemos que ya se creo.</p>



<p class="wp-block-paragraph"><strong>Nota</strong>: <em>Prisma 7 ya trae instalado el dotenv, asi que no es necesario instalarlo directamente.</em></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="831" height="124" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-35.png" alt="" class="wp-image-232" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-35.png 831w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-35-300x45.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-35-768x115.png 768w" sizes="auto, (max-width: 831px) 100vw, 831px" /></figure>



<ul class="wp-block-list">
<li>Se creó el archivo dev.db</li>



<li>Se creó la migración como historial de cambios prisma/migrations/20260607033831_init/migration.sql</li>



<li>Las 4 tablas han sido creadas.</li>
</ul>



<p class="wp-block-paragraph">Lo siguiente es generar el cliente de prisma con:</p>



<pre class="wp-block-code"><code>npx prisma generate</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="259" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-36-1024x259.png" alt="" class="wp-image-235" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-36-1024x259.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-36-300x76.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-36-768x194.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-36.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Ahora si necesitamos empezar a crear los recursos (resources) -&gt; module, service, controller, DTOs y entities</p>



<p class="wp-block-paragraph">Se va a utilizar el comando resource para crear todo.</p>



<p class="wp-block-paragraph"><em>g = generate</em></p>



<pre class="wp-block-code"><code>nest g resource alumno
nest g resource docente
nest g resource curso
nest g resource matricula</code></pre>



<p class="wp-block-paragraph">Hara unas preguntas, responder con </p>



<ul class="wp-block-list">
<li><em>What transport layer do you use? </em>&gt;&gt;<strong> REST API</strong></li>



<li><em>Would you like to generate CRUD entry points?</em> &gt;&gt; <strong>Yes</strong></li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="654" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-37-1024x654.png" alt="" class="wp-image-240" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-37-1024x654.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-37-300x191.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-37-768x490.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-37.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Se genera todo.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="304" height="338" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-38.png" alt="" class="wp-image-242" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-38.png 304w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-38-270x300.png 270w" sizes="auto, (max-width: 304px) 100vw, 304px" /></figure>



<p class="wp-block-paragraph">Replicar para cada entidad / tabla.</p>



<p class="wp-block-paragraph"><strong>Instalar adapter para integrar prisma con nestjs</strong> y lograr consultas a sqlite</p>



<pre class="wp-block-code"><code>npm install @prisma/adapter-better-sqlite3
npm install better-sqlite3
npm install --save-dev @types/better-sqlite3</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="875" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-39-1024x875.png" alt="" class="wp-image-245" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-39-1024x875.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-39-300x256.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-39-768x656.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-39.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Luego tenemos que crear un archivo de module y service para prisma.</p>



<pre class="wp-block-code"><code>nest generate module prisma
nest generate service prisma</code></pre>



<p class="wp-block-paragraph">cada uno de estos archivos hay que editarlos.</p>



<p class="wp-block-paragraph">prisma/prisma.service.ts</p>



<pre class="wp-block-code has-small-font-size"><code>import { Injectable, OnModuleInit } from '@nestjs/common';

import { PrismaClient } from '../../generated/prisma/client';
import { PrismaBetterSqlite3 } from '@prisma/<strong>adapter-better-sqlite3</strong>';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
    
    constructor(){
        const adapter = new PrismaBetterSqlite3({ url: 'file:./dev.db' });
        super({adapter})
    }
    
    async onModuleInit() {
        await this.$connect();
    }

}</code></pre>



<p class="wp-block-paragraph">luego el archivo del module.</p>



<p class="wp-block-paragraph">prisma/module.ts</p>



<pre class="wp-block-code has-small-font-size"><code>import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: &#91;PrismaService],
  exports: &#91;PrismaService]
})
export class PrismaModule {}
</code></pre>



<p class="wp-block-paragraph">y también asegurarnos que el module este importando a nivel de la app.module.ts</p>



<p class="wp-block-paragraph">Si utilizaron los generadores el module se importara solo.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="460" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40-1024x460.png" alt="" class="wp-image-247" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40-1024x460.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40-300x135.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40-768x345.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40-1300x584.png 1300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-40.png 1309w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Luego hay que corregir un error de conflicto en las configuraciones de prisma por formato.</p>



<p class="wp-block-paragraph">prisma/schema.prisma (Donde se definieron las tablas) hay que aclarar el formato del modulo (moduleFormat) como «cjs»</p>



<pre class="wp-block-code"><code>generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
  <strong>moduleFormat = "cjs"</strong>
}

datasource db {
  provider = "sqlite"  
}</code></pre>



<p class="wp-block-paragraph">luego ejecutar un npx prisma generate</p>



<pre class="wp-block-code"><code>npx prisma generate</code></pre>



<p class="wp-block-paragraph">Finalmente, ejecutar el proyecto para validar que todo esta funcionando.</p>



<pre class="wp-block-code"><code>npm run start:dev</code></pre>



<p class="wp-block-paragraph">Parte II &#8211; <a href="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/" data-type="link" data-id="https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso-parte-ii/">Visitar</a></p>



<p class="wp-block-paragraph">Prisma.io &#8211; <a href="https://www.prisma.io/" data-type="link" data-id="https://www.prisma.io/" target="_blank" rel="noreferrer noopener">Enlace</a></p>



<p class="wp-block-paragraph">¡Saludos!</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/nestjs-aplicacion-de-matricula-a-curso/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Hacer aplicacion ToDo en Nestjs</title>
		<link>https://jeffsantillan.com/blog/hacer-aplicacion-todo-en-nestjs/</link>
					<comments>https://jeffsantillan.com/blog/hacer-aplicacion-todo-en-nestjs/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 01:05:22 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[nestjs]]></category>
		<category><![CDATA[todo-app]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=212</guid>

					<description><![CDATA[Vamos a construir una pequeña aplicacion de todo en Nestjs Para ello utilizamos el cliente de &#8230; <a title="Hacer aplicacion ToDo en Nestjs" class="hm-read-more" href="https://jeffsantillan.com/blog/hacer-aplicacion-todo-en-nestjs/"><span class="screen-reader-text">Hacer aplicacion ToDo en Nestjs</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Vamos a construir una pequeña aplicacion de todo en Nestjs</p>



<p class="wp-block-paragraph">Para ello utilizamos el cliente de nest para generar los modulos, servicios y controladores.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="496" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-26-1024x496.png" alt="" class="wp-image-213" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-26-1024x496.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-26-300x145.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-26-768x372.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-26.png 1146w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">dentro de la carpeta todo se va a crear cada archivo TS (Typescript).</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="324" height="298" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-27.png" alt="" class="wp-image-214" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-27.png 324w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-27-300x276.png 300w" sizes="auto, (max-width: 324px) 100vw, 324px" /></figure>



<p class="wp-block-paragraph">Procedemos a diseñar el modelo junto con el servicio.</p>



<p class="wp-block-paragraph">Interfaz</p>



<pre class="wp-block-code"><code>export interface Todo {
    id: number;
    title: string;
    completed: boolean;
}</code></pre>



<p class="wp-block-paragraph">Servicio</p>



<pre class="wp-block-code"><code>import { Injectable, NotFoundException } from '@nestjs/common';
import { Todo } from './todo.model';

@Injectable()
export class TodoService {

    private todos: Todo&#91;] = &#91;];
    private idCounter = 1;

    findAll(): Todo&#91;] {
        return this.todos;
    }

    findOne(id: number): Todo {
        const todo = this.todos.find(t => t.id === id);
        if(!todo) throw new NotFoundException('Todo ${id} no fue encontrado'); // Importar en @nestjs/common
        return todo;
    }

    create(title: string): Todo {
        const todo: Todo = {id: this.idCounter++, title, completed: false };
        this.todos.push(todo);
        return todo;
    }

    update(id: number, title?: string, completed?: boolean): Todo {
        const todo = this.findOne(id);
        if(title !== undefined) todo.title = title;
        if(completed !== undefined) todo.completed = completed;        
        return todo;
    }

    remove(id: number): void{
        const index = this.todos.findIndex(t => t.id === id);
        if (index === -1) throw new NotFoundException('Todo no encontrado');
        this.todos.splice(index, 1);
    }

}</code></pre>



<p class="wp-block-paragraph">Ahora toca crear el controlador.</p>



<p class="wp-block-paragraph">Controlador:</p>



<pre class="wp-block-code"><code>import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { TodoService } from './todo.service'; 

@Controller('todos')
export class TodoController {

    // Constructor
    constructor(private readonly todoService: TodoService) {}

    @Get()
    findAll(){
        return this.todoService.findAll();
    }

    @Get(':id')
    findOne(@Param('id') id: string){
        return this.todoService.findOne(+id);
    }

    @Post()
    create(@Body() body: { title: string }){
        return this.todoService.create(body.title);
    }

    @Put(':id')
    update(@Param('id') id: string, @Body() body: {title?: string, completed?: boolean }) {
        return this.todoService.update(+id, body.title, body.completed);
    }

    @Delete(':id')
    remove(@Param('id') id: string){
        return this.todoService.remove(+id);
    }

}
</code></pre>



<p class="wp-block-paragraph">Luego de culminado el modelo, servicio y controlador. Llego el momento de probar la aplicación.</p>



<p class="wp-block-paragraph">Levantamos el servicio con el comando:</p>



<pre class="wp-block-code"><code>npm run start:dev</code></pre>



<p class="wp-block-paragraph">En el navegador veremos que la web ya levanta con el puerto 3000 en localhost</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="520" height="109" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-29.png" alt="" class="wp-image-219" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-29.png 520w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-29-300x63.png 300w" sizes="auto, (max-width: 520px) 100vw, 520px" /></figure>



<p class="wp-block-paragraph">Vamos a utilizar Invoke-Request de preferencia para poder crear un registro.</p>



<pre class="wp-block-code"><code>Invoke-WebRequest -Uri http://localhost:3000/todos -Method POST -Headers @{"Content-Type"="application/json"} -Body '{"title":"Aprender NestJS"}'</code></pre>



<p class="wp-block-paragraph">Luego vamos a leer con el curl a pesar que a veces Powershell no lo reconozca o interprete bien.</p>



<pre class="wp-block-code"><code>curl https://localhost:3000/todos</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="947" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-28-1024x947.png" alt="" class="wp-image-217" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-28-1024x947.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-28-300x277.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-28-768x710.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-28.png 1103w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">También es valido probarlo directo del navegador.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="581" height="132" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-30.png" alt="" class="wp-image-220" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-30.png 581w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-30-300x68.png 300w" sizes="auto, (max-width: 581px) 100vw, 581px" /></figure>



<p class="wp-block-paragraph">Eso seria todo.</p>



<p class="wp-block-paragraph">Nota: La información se esta guardando en memoria de arrays, aun no hemos conectado a una base de datos, de momento solo es el uso de la estructura de NestJS.</p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/hacer-aplicacion-todo-en-nestjs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Instalar nestjs el Framework progresivo para nodejs</title>
		<link>https://jeffsantillan.com/blog/instalar-nestjs-el-framework-progresivo-para-nodejs/</link>
					<comments>https://jeffsantillan.com/blog/instalar-nestjs-el-framework-progresivo-para-nodejs/#respond</comments>
		
		<dc:creator><![CDATA[Jefferson Santillan]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 00:41:23 +0000</pubDate>
				<category><![CDATA[Nestjs]]></category>
		<category><![CDATA[nestjs]]></category>
		<guid isPermaLink="false">https://jeffsantillan.com/blog/?p=205</guid>

					<description><![CDATA[Vamos a continuar potenciando a NodeJS con un framework de desarrollo de microservicios, o aplicaciones del &#8230; <a title="Instalar nestjs el Framework progresivo para nodejs" class="hm-read-more" href="https://jeffsantillan.com/blog/instalar-nestjs-el-framework-progresivo-para-nodejs/"><span class="screen-reader-text">Instalar nestjs el Framework progresivo para nodejs</span>Leer más</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Vamos a continuar potenciando a NodeJS con un framework de desarrollo de microservicios, o aplicaciones del lado del servidor, el cual es ampliamente utilizado llamado Nest que fue pensado y diseñado para proyecto grandes y estructurados.</p>



<p class="wp-block-paragraph">Para lograr la instalación del Cliente, es necesario utilizar el gestor de paquetes de Node.js (npm).</p>



<p class="wp-block-paragraph">Nota: Para windows de preferencia ejecutar un power shel con modo Administrador.</p>



<pre class="wp-block-code"><code>npm install -g @nestjs/cli</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="488" height="320" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-24.png" alt="" class="wp-image-208" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-24.png 488w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-24-300x197.png 300w" sizes="auto, (max-width: 488px) 100vw, 488px" /></figure>



<p class="wp-block-paragraph">Para crear un proyecto tenemos que lanzar el comando.</p>



<pre class="wp-block-code"><code>nest new todo-app</code></pre>



<p class="wp-block-paragraph">Se tiene que elegir el Package Manager en mi caso utilizo npm</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="729" src="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-25-1024x729.png" alt="" class="wp-image-209" srcset="https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-25-1024x729.png 1024w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-25-300x214.png 300w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-25-768x547.png 768w, https://jeffsantillan.com/blog/wp-content/uploads/2026/06/image-25.png 1103w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Es todo.</p>



<p class="wp-block-paragraph">Si quieres conocer la versión del cliente utiliza el comando</p>



<pre class="wp-block-code"><code>nest --version</code></pre>



<p class="wp-block-paragraph">Pagina Oficial &#8211; <a href="https://nestjs.com/" data-type="link" data-id="https://nestjs.com/" target="_blank" rel="noreferrer noopener">Enlace</a></p>



<p class="wp-block-paragraph">¡Saludos!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jeffsantillan.com/blog/instalar-nestjs-el-framework-progresivo-para-nodejs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
