Es imposible aprender, en cada clase hay codigo y archivos nuevos, porque no te pones a programar todo de cero, la idea es aprender a estructurar y saber como funcionan estas tecnologias
Introducción
¿Por qué GraphQL?
Arquitectura backend con Node.js y GraphQL
Construyendo una query simple con Apollo Server
Scalars, filtros y queries avanzadas
Base de datos y ORM
ORMs, Prisma y automatización
Modelando la base datos y schema
Resolvers para mutaciones y filtros
Filtros en Prisma
Paginación y filtros avanzados
Archivos estáticos
Archivos estáticos con Express.js y Apollo Server
Producción
Protección de recursos en GraphQL
Testing
Pruebas unitarias, integración y End to End
Preparando la app para producción
Preparando el servidor para Heroku
Introspección
Bases de datos en producción
Deploying
Próximos pasos
Reto: guestbook
Desventajas de GraphQL
Frameworks de GraphQL
Conectemos el frontend
Crea una cuenta o inicia sesión
¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera
Aportes 11
Preguntas 2
Es imposible aprender, en cada clase hay codigo y archivos nuevos, porque no te pones a programar todo de cero, la idea es aprender a estructurar y saber como funcionan estas tecnologias
La verdad que la forma de explicar es bastante desordenada. No hay estructura, no se explica muchas veces de dónde y por qué vienen los conceptos. Es muy difícil de seguirlo.
¿Estas obteniendo un error en GraphQL por alguno de los atributos?
.
Aunque en una clase posterior los agregamos, tal vez puedas necesitarlo ahora dependiendo si tú query solicita atributos.
.
Se le pide a Prisma que incluya atributos: https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#include
.
Ej:
export async function createAvo(...) {
// ...
const avo = await orm.avocado.create({
data: {
// ...
},
include: { attributes: true }, // <--- 👈
})
// ...
}
Las variables del profesor:
{
"data": {
"name": "Lamb Hass Avocado",
"price": 1.34,
"image": "/images/lamb.jpg",
"description": "The Lamb Hass Avocado",
"shape": "Obovate",
"hardiness": "-2 ºC",
"taste": "Great, is an avocado"
}
}
Si intentan hacer una consulta asi:
query Query {
avos {
id
name
attributes {
description
}
}
}
Tendran el error de Cannot read properties of undefined (reading 'description')
Esto es porque al usar avocado.findMany()
no incluye los attributes. Para que se incluyan hay que usar avocado.findMany({ include: { attributes: true } })
.
<h5>📚 Repositorio</h5>.
Para esta sección, en vez de utilizar los Data sources, desarrollo una abstracción mediante el patrón MVC (Model-View-Controller). Para ello defino la siguiente estructura de trabajo:
src
└─⫸ controllers
│ └─⫸ Account.controller.ts
└─⫸ graphql
│ └─⫸ schemas
│ └─⫸ Account.schema.ts
└─⫸ models
└─⫸ Account
└─⫸ Account.entity.ts
└─⫸ Account.model.ts
Partiendo de la capa del modelo tenemos que:
Un modelo define la capa de definición y abstracción a la información.
.
export interface Account {
id: number
email: string
password: string
}
export type Query = Pick<Account, 'id'>
export type Payload = Pick<Account, 'email' | 'password'>
export default class AccountModel extends Model<Account, Query, Payload> {
/**
* @private
* @description Prisma ORM definition handler library. */
private client = new Prisma('account')
/**
* @description Find an account by id.
* @param {Query} query
* @returns Account */
async findUnique(query: Query): Promise<Account> {
return await this.client.findUnique({
where: query,
})
}
// More code ...
}
Para el controlador tenemos que:
Un controlador gestiona y administra el acceso a los datos, así como la secuencia necesaria para su manipulación basadas en las reglas de negocio.
.
import Error from '@controllers/Error.controller'
import Account from '@models/Account/Account.model'
import { Query, Payload } from '@models/Account/Account.entity'
export default class AccountController {
private model = new Account()
/**
* @description Find an account by id.
* @param {Query} query
* @returns Account */
async findAccount(query: Query) {
const account = await this.model.findUnique({
id: Number(query.id),
})
if (!account) throw new Error('NOT_FOUND')
return account
}
// More code ...
}
Por último, el esquema la defino como su puente o límite mediante prototipos:
import { gql } from 'apollo-server'
import { Query, Payload } from '@models/Account/Account.entity'
import AccountController from '@controllers/Account.contoller'
const accountController = new AccountController()
export default {
Schema: gql`
type Account {
id: ID!
email: String!
password: String!
}
input CreateAccount {
email: String!
password: String!
}
extend type Query {
account(id: ID!): Account!
accounts: [Account!]!
}
`,
Query: {
account: async (_: any, query: Query) => {
return await accountController.findAccount(query)
},
accounts: async () => {
return await accountController.findAccounts()
},
},
Mutation: {
createAccount: async (_: any, { account }: { account: Payload }) => {
validate(account)
return await accountController.createAccount(account)
},
},
}
Si alguno tiene problemas al levantar querer consultar los ‘avos’ con un error similar a este 👇🏻 es un problema con el monorepo, si saca la api a un repositorio normal con la configuración del monorepo + config del Repo Api ya la consulta correrá correctamente.
Ahora si alguno lo pudo solucionar utilizando el monorepo y quiere postear la solución se agradecerá.
"\nInvalid `context.orm.avocado.findMany()` invocation in\n/Users/.../proyectos/nextjs/nextjs-graphql-fullstack/api/src/modules/avocado/avocado.resolver.ts:14:30\n\n 11 args: unknown,\n 12 context: ResolverContext\n 13 ): Promise<Avocado[]> {\n→ 14 return context.orm.avocado.findMany(\n The table `main.Avocado` does not exist in the current database.",
👉 El código con la versión finalizada de esta clase lo puedes encontrar en la etiqueta 2-queries
Para chequearlo crea un nuevo branch usando:
git checkout -b filtros-queries 2-queries
filtros-queries es el nombre del branch. Puedes usar cualquier otro nombre
Para quienes tengan problemas con el findMany() y los atributos, pasen un objeto con el atributo include pidiendo que incluya la asociación de la siguiente manera:
export function findAll(): Promise<Avocado[]> {
return prisma.avocado.findMany(
{
include: { attributes: true }
}
)
}
Para crear un avocado y que regrese los attributes, la función de createAvo se tendría que ver así
export function createAvo(
parent: unknown,
{
data,
}: { data: Pick<Avocado, 'name' | 'price' | 'image'> & Attributes },
context: ResolverContext,
): Promise<Avocado> {
const { name, price, image, ...attributes } = data;
return context.orm.avocado.create({
data: {
name,
price,
image,
sku: Date.now().toString(36) + Math.random().toString(36).substring(2),
attributes: {
create: attributes,
}
},
include: {
attributes: true,
}
});
}
¿Cómo hizo el profe lo del minuto 15:19, es algun pluggin o así?
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?