Introducción
\n\nGraphQL es un lenguaje de consultas para API que permite a los clientes obtener solo los datos necesarios. En combinación con TypeScript, se convierte en una herramienta poderosa para crear API tipadas, escalables y documentadas. En este tutorial paso a paso crearemos en la práctica una API GraphQL completa en TypeScript utilizando Apollo Server y TypeGraphQL.
Aprenderás:
\n- \n
- Cómo configurar un proyecto con TypeScript y Apollo Server \n
- Cómo definir un esquema GraphQL usando decoradores de TypeGraphQL \n
- Cómo escribir resolvers con tipado completo \n
- Cómo conectar una base de datos y ejecutar consultas \n
Este material será útil tanto para principiantes como para desarrolladores que ya conocen REST API pero desean dominar el enfoque moderno para construir API.
\n\n1. Configuración del proyecto e instalación de dependencias
\n\nComencemos con la inicialización del proyecto. Asegúrate de tener instalados Node.js (versión 16+) y npm/yarn.
\n\nmkdir graphql-ts-tutorial\ncd graphql-ts-tutorial\nnpm init -y\n\nInstala los paquetes necesarios:
\n\nnpm install apollo-server graphql reflect-metadata type-graphql class-validator\nnpm install -D typescript @types/node ts-node\n\nCrea el archivo tsconfig.json:
{\n "compilerOptions": {\n "target": "ES2020",\n "module": "commonjs",\n "lib": ["ES2020"],\n "experimentalDecorators": true,\n "emitDecoratorMetadata": true,\n "strict": true,\n "esModuleInterop": true,\n "outDir": "./dist",\n "rootDir": "./src"\n },\n "include": ["src/**/*"]\n}\n\nObserva las banderas experimentalDecorators y emitDecoratorMetadata — son necesarias para el funcionamiento de TypeGraphQL.
2. Definición del esquema y tipos con TypeGraphQL
\n\nCreemos la carpeta src y el archivo src/types/User.ts. Aquí describiremos el modelo de usuario.
import { ObjectType, Field, ID, InputType } from "type-graphql";\n\n@ObjectType()\nexport class User {\n @Field(() => ID)\n id!: number;\n\n @Field()\n name!: string;\n\n @Field()\n email!: string;\n\n @Field()\n createdAt!: Date;\n}\n\nExplicación:
\n- \n
@ObjectType()— marca la clase como un tipo GraphQL. \n @Field()— indica qué campos estarán disponibles en la API. \n @Field(() => ID)— para el tipo escalar ID. \n
Ahora creemos un InputType para las mutaciones (creación de usuario):
@InputType()\nexport class CreateUserInput {\n @Field()\n name!: string;\n\n @Field()\n email!: string;\n}\n\nEste tipo se utilizará en los resolvers para la validación de datos de entrada.
\n\n3. Escritura de resolvers
\n\nCreemos el archivo src/resolvers/UserResolver.ts. Aquí implementaremos la lógica de consultas y mutaciones.
import { Resolver, Query, Mutation, Arg } from "type-graphql";\nimport { User, CreateUserInput } from "../types/User";\n\n// Simulación de base de datos\nlet users: User[] = [\n { id: 1, name: "Iván Ivanov", email: "ivan@example.com", createdAt: new Date() },\n { id: 2, name: "María Petrova", email: "maria@example.com", createdAt: new Date() }\n];\n\nlet nextId = 3;\n\n@Resolver()\nexport class UserResolver {\n @Query(() => [User])\n async users(): Promise<User[]> {\n return users;\n }\n\n @Query(() => User, { nullable: true })\n async user(@Arg("id") id: number): Promise<User | undefined> {\n return users.find(u => u.id === id);\n }\n\n @Mutation(() => User)\n async createUser(@Arg("data") data: CreateUserInput): Promise<User> {\n const newUser: User = {\n id: nextId++,\n name: data.name,\n email: data.email,\n createdAt: new Date()\n };\n users.push(ne