Node.js

Node

Tutorial Node: Estructura de proyecto y package.json

Aprende a crear y organizar la estructura de un proyecto Node.js con package.json, gestión de dependencias y buenas prácticas esenciales.

Aprende Node y certifícate

Inicialización con npm init y estructura

La inicialización de un proyecto Node.js comienza con la creación de un archivo package.json, que actúa como el manifiesto de nuestro proyecto. Este archivo contiene toda la información esencial sobre nuestra aplicación, desde su nombre y versión hasta las dependencias que necesita para funcionar correctamente.

Creación del proyecto con npm init

El comando npm init es la herramienta estándar para inicializar un nuevo proyecto Node.js. Este comando nos guía a través de un proceso interactivo donde definimos las características básicas de nuestro proyecto:

npm init

Durante la ejecución, npm nos solicitará información como:

  • Nombre del proyecto (package name)
  • Versión inicial (version)
  • Descripción del proyecto (description)
  • Punto de entrada principal (entry point)
  • Comando de pruebas (test command)
  • Repositorio git (git repository)
  • Palabras clave (keywords)
  • Autor (author)
  • Licencia (license)

Para proyectos donde queremos acelerar el proceso, podemos usar la bandera -y que acepta todos los valores por defecto:

npm init -y

Estructura típica de un proyecto Node.js

Una vez inicializado el proyecto, la estructura básica recomendada para un proyecto Node.js incluye los siguientes elementos:

mi-proyecto/
├── package.json
├── package-lock.json
├── node_modules/
├── src/
│   ├── index.js
│   ├── controllers/
│   ├── models/
│   └── utils/
├── test/
├── docs/
└── README.md

Descripción de cada elemento:

  • package.json: Archivo de configuración principal del proyecto
  • package-lock.json: Archivo generado automáticamente que fija las versiones exactas de las dependencias
  • node_modules/: Directorio donde se instalan todas las dependencias del proyecto
  • src/: Carpeta que contiene el código fuente de la aplicación
  • index.js: Punto de entrada principal de la aplicación
  • test/: Directorio para archivos de pruebas
  • docs/: Documentación del proyecto
  • README.md: Archivo de documentación principal

Anatomía del archivo package.json

El archivo package.json generado contiene una estructura JSON con campos fundamentales para el funcionamiento del proyecto:

{
  "name": "mi-proyecto-node",
  "version": "1.0.0",
  "description": "Mi primera aplicación Node.js",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["node", "javascript", "api"],
  "author": "Tu Nombre <tu.email@ejemplo.com>",
  "license": "MIT"
}

Campos principales del package.json:

  • name: Identificador único del proyecto, debe seguir las convenciones de npm
  • version: Versión actual del proyecto siguiendo el formato semántico (semver)
  • description: Breve descripción de qué hace el proyecto
  • main: Archivo principal que se ejecuta cuando se importa el proyecto
  • scripts: Comandos personalizados que se pueden ejecutar con npm run
  • keywords: Palabras clave para facilitar la búsqueda del paquete
  • author: Información del autor o autores del proyecto
  • license: Tipo de licencia bajo la cual se distribuye el código

Configuración del punto de entrada

El campo main en el package.json especifica cuál es el archivo principal de nuestra aplicación. Por convención, este archivo suele llamarse index.js y ubicarse en la raíz del proyecto o dentro de una carpeta src/:

{
  "main": "src/index.js"
}

Este archivo será el que se ejecute cuando alguien importe nuestro proyecto como módulo o cuando ejecutemos comandos como npm start. Es importante que este archivo exista y contenga el código necesario para inicializar nuestra aplicación.

Buenas prácticas en la estructura inicial

Al organizar un proyecto Node.js desde el inicio, es recomendable seguir ciertas convenciones que facilitarán el mantenimiento y la colaboración:

  • Separar el código fuente en una carpeta dedicada (src/)
  • Mantener archivos de configuración en la raíz del proyecto
  • Crear un README.md descriptivo desde el principio
  • Establecer una estructura de carpetas coherente y escalable
  • Definir scripts básicos en el package.json para tareas comunes

Esta estructura inicial proporciona una base sólida sobre la cual construir aplicaciones Node.js más complejas, manteniendo la organización y facilitando la incorporación de nuevas funcionalidades conforme el proyecto evoluciona.

Dependencias vs devDependencies

La gestión de dependencias en Node.js se organiza en dos categorías principales dentro del archivo package.json: las dependencias de producción y las dependencias de desarrollo. Esta distinción es fundamental para optimizar el tamaño de nuestras aplicaciones y separar las herramientas necesarias según el entorno de ejecución.

Dependencias de producción (dependencies)

Las dependencias de producción son aquellos paquetes que nuestra aplicación necesita para funcionar correctamente en el entorno final. Estos módulos forman parte del código que se ejecutará cuando los usuarios utilicen nuestra aplicación.

{
  "dependencies": {
    "lodash": "^4.17.21",
    "axios": "^1.6.0",
    "dotenv": "^16.3.1"
  }
}

Para instalar una dependencia de producción utilizamos el comando:

npm install nombre-del-paquete

O su versión abreviada:

npm i nombre-del-paquete

Ejemplos típicos de dependencias de producción:

  • Librerías de utilidades: como lodash para manipulación de datos
  • Clientes HTTP: como axios para realizar peticiones
  • Gestión de configuración: como dotenv para variables de entorno
  • Validadores: como joi para validación de esquemas
  • Drivers de base de datos: como mysql2 o mongodb

Dependencias de desarrollo (devDependencies)

Las dependencias de desarrollo son herramientas que solo necesitamos durante el proceso de desarrollo, testing o construcción del proyecto. Estos paquetes no se incluyen cuando la aplicación se ejecuta en producción.

{
  "devDependencies": {
    "nodemon": "^3.0.1",
    "jest": "^29.7.0",
    "eslint": "^8.50.0"
  }
}

Para instalar una dependencia de desarrollo utilizamos la bandera --save-dev:

npm install --save-dev nombre-del-paquete

O su versión abreviada:

npm i -D nombre-del-paquete

Ejemplos típicos de dependencias de desarrollo:

  • Herramientas de desarrollo: como nodemon para reinicio automático
  • Frameworks de testing: como jest o mocha para pruebas
  • Linters y formateadores: como eslint o prettier
  • Compiladores: como babel para transpilación de código
  • Herramientas de construcción: como webpack o rollup

Diferencias en la instalación

Cuando instalamos dependencias en diferentes entornos, npm se comporta de manera distinta según el contexto:

Instalación completa (desarrollo):

npm install

Este comando instala tanto las dependencies como las devDependencies, lo cual es ideal para entornos de desarrollo donde necesitamos todas las herramientas.

Instalación solo de producción:

npm install --production

O estableciendo la variable de entorno:

NODE_ENV=production npm install

Estos comandos instalan únicamente las dependencies, excluyendo las devDependencies para mantener el entorno de producción limpio y optimizado.

Versionado semántico en dependencias

Ambos tipos de dependencias utilizan versionado semántico (semver) para especificar qué versiones son compatibles:

{
  "dependencies": {
    "express": "^4.18.2",
    "lodash": "~4.17.21",
    "axios": "4.6.0"
  }
}

Símbolos de versionado:

  • ^4.18.2: Acepta actualizaciones menores y parches (4.x.x)
  • ~4.17.21: Acepta solo actualizaciones de parches (4.17.x)
  • 4.6.0: Versión exacta, sin actualizaciones automáticas

Impacto en el rendimiento y despliegue

La separación correcta entre dependencies y devDependencies tiene implicaciones importantes:

Ventajas de la separación:

  • Menor tamaño en producción al excluir herramientas de desarrollo
  • Instalaciones más rápidas en servidores de producción
  • Menor superficie de ataque al reducir dependencias innecesarias
  • Claridad conceptual sobre qué requiere la aplicación para funcionar

Ejemplo de package.json completo:

{
  "name": "mi-api-node",
  "version": "1.0.0",
  "dependencies": {
    "http": "0.0.1-security",
    "url": "^0.11.3",
    "querystring": "^0.2.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1",
    "jest": "^29.7.0",
    "supertest": "^6.3.3"
  }
}

Gestión de dependencias en equipo

Cuando trabajamos en equipos de desarrollo, es crucial mantener consistencia en las dependencias. El archivo package-lock.json garantiza que todos los desarrolladores instalen exactamente las mismas versiones:

# Instalar dependencias según package-lock.json
npm ci

El comando npm ci es preferible a npm install en entornos de integración continua porque es más rápido y garantiza instalaciones reproducibles basadas en el archivo de bloqueo.

Esta distinción entre tipos de dependencias nos permite mantener proyectos más organizados y eficientes, separando claramente las herramientas de desarrollo de los componentes esenciales para el funcionamiento de nuestra aplicación.

Aprende Node online

Otras lecciones de Node

Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Node y certifícate

Ejercicios de programación de Node

Evalúa tus conocimientos de esta lección Estructura de proyecto y package.json con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.