Skip to content

Commit

Permalink
codegen update
Browse files Browse the repository at this point in the history
  • Loading branch information
gilgardosh committed Nov 11, 2024
1 parent 4018d03 commit 9564025
Show file tree
Hide file tree
Showing 16 changed files with 549 additions and 301 deletions.
2 changes: 2 additions & 0 deletions examples/hackernews/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ node_modules
.env
prisma/migrations/migration_lock.toml
prisma/*.db*
# Ignore codegen auto-generated files
*.generated.*
16 changes: 16 additions & 0 deletions examples/hackernews/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from '@eddeee888/gcg-typescript-resolver-files';
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'src/schema/**/schema.graphql',
generates: {
'src/schema': defineConfig({
resolverGeneration: 'minimal',
typesPluginsConfig: {
contextType: '../context#GraphQLContext',
},
}),
},
hooks: { afterAllFileWrite: ['prettier --write'] },
};
export default config;
8 changes: 6 additions & 2 deletions examples/hackernews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@
"keywords": [],
"scripts": {
"check": "tsc --pretty --noEmit",
"codegen": "graphql-codegen",
"dev": "cross-env NODE_ENV=development ts-node-dev --exit-child --respawn src/main.ts",
"migrate": "prisma migrate dev",
"postinstall": "prisma generate",
"postinstall": "pnpm run prisma:generate && pnpm run codegen",
"precheck": "pnpm run codegen",
"prisma:generate": "prisma generate",
"start": "ts-node src/main.ts"
},
"dependencies": {
"graphql": "16.6.0",
"graphql-yoga": "workspace:*"
},
"devDependencies": {
"@eddeee888/gcg-typescript-resolver-files": "0.10.4",
"@graphql-codegen/cli": "5.0.2",
"@prisma/client": "5.13.0",
"@prisma/internals": "5.13.0",
"@prisma/migrate": "5.13.0",
"@types/node": "20.14.12",
"cross-env": "7.0.3",
Expand Down
178 changes: 3 additions & 175 deletions examples/hackernews/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,181 +1,9 @@
import { createSchema } from 'graphql-yoga';
import { Prisma, type Link } from '@prisma/client';
import type { GraphQLContext } from './context';
import { GraphQLError } from 'graphql';
import { resolvers } from './schema/resolvers.generated';
import { typeDefs } from './schema/typeDefs.generated';

const typeDefinitions = /* GraphQL */ `
type Link {
id: ID!
description: String!
url: String!
comments: [Comment!]!
}
type Comment {
id: ID!
body: String!
link: Link
}
type Query {
info: String!
feed(filterNeedle: String, skip: Int, take: Int): [Link!]!
comment(id: ID!): Comment
link(id: ID!): Link
}
type Mutation {
postLink(url: String!, description: String!): Link!
postCommentOnLink(linkId: ID!, body: String!): Comment!
}
`;

const parseIntSafe = (value: string): number | null => {
if (/^(\d+)$/.test(value)) {
return parseInt(value, 10);
}
return null;
};

const applySkipConstraints = (value: number) => {
if (value < 0) {
throw new GraphQLError(`'skip' argument value '${value}' is invalid, value must be positive.`);
}
return value;
};

const applyTakeConstraints = (params: { min: number; max: number; value: number }) => {
if (params.value < params.min || params.value > params.max) {
throw new GraphQLError(
`'take' argument value '${params.value}' is outside the valid range of '${params.min}' to '${params.max}'.`,
);
}
return params.value;
};

const resolvers = {
Query: {
info: () => `This is the API of a Hackernews Clone`,
feed: async (
parent: unknown,
args: { filterNeedle?: string; skip?: number; take?: number },
context: GraphQLContext,
) => {
const where = args.filterNeedle
? {
OR: [
{ description: { contains: args.filterNeedle } },
{ url: { contains: args.filterNeedle } },
],
}
: {};

const take = applyTakeConstraints({
min: 1,
max: 50,
value: args.take ?? 30,
});

const skip = applySkipConstraints(args.skip ?? 0);

return context.prisma.link.findMany({
where,
skip,
take,
});
},
comment: async (parent: unknown, args: { id: string }, context: GraphQLContext) => {
return context.prisma.comment.findUnique({
where: { id: parseInt(args.id) },
});
},
link: async (parent: unknown, args: { id: string }, context: GraphQLContext) => {
return context.prisma.link.findUnique({
where: { id: parseInt(args.id) },
});
}
},
Link: {
id: (parent: Link) => parent.id,
description: (parent: Link) => parent.description,
url: (parent: Link) => parent.url,
comments: (parent: Link, _: unknown, context: GraphQLContext) => {
return context.prisma.comment.findMany({
where: {
linkId: parent.id,
},
});
},
},
Mutation: {
postLink: async (
parent: unknown,
args: { description: string; url: string },
context: GraphQLContext,
) => {
const newLink = await context.prisma.link.create({
data: {
url: args.url,
description: args.description,
},
});
return newLink;
},
postCommentOnLink: async (
parent: unknown,
args: { linkId: string; body: string },
context: GraphQLContext,
) => {
const linkId = parseIntSafe(args.linkId);
if (linkId === null) {
return Promise.reject(
new GraphQLError(`Cannot post common on non-existing link with id '${args.linkId}'.`),
);
}

if (!args.body || args.body.trim().length === 0) {
return Promise.reject(new GraphQLError(`Comment body cannot be empty.`));
}

const newComment = await context.prisma.comment
.create({
data: {
body: args.body,
linkId,
},
})
.catch((err: unknown) => {
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
return Promise.reject(
new GraphQLError(
`Cannot post common on non-existing link with id '${args.linkId}'.`,
),
);
}
return Promise.reject(err);
});

return newComment;
},
},
Comment: {
id: (parent: { id: number }) => parent.id,
body: (parent: { body: string }) => parent.body,
link: async (parent: { linkId: number }, _: unknown, context: GraphQLContext) => {
if (!parent.linkId) {
return null;
}

return context.prisma.link.findUnique({
where: {
id: parent.linkId,
},
});
},
}
};

export const schema = createSchema({
resolvers: [resolvers],
typeDefs: [typeDefinitions],
typeDefs: [typeDefs],
});
15 changes: 15 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { CommentResolvers } from '../../types.generated';

export const Comment: CommentResolvers = {
link(parent, _arg, context) {
if (!parent.linkId) {
return null;
}

return context.prisma.link.findUnique({
where: {
id: parent.linkId,
},
});
},
};
11 changes: 11 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { LinkResolvers } from '../../types.generated';

export const Link: LinkResolvers = {
comments: async (parent, _arg, context) => {
return context.prisma.comment.findMany({
where: {
linkId: parent.id,
},
});
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { GraphQLError } from 'graphql';
import { Prisma } from '@prisma/client';
import { parseIntSafe } from '../../../../utils';
import type { MutationResolvers } from '../../../types.generated';

export const postCommentOnLink: NonNullable<MutationResolvers['postCommentOnLink']> = async (
_parent,
args,
context,
) => {
const linkId = parseIntSafe(args.linkId);
if (linkId === null) {
return Promise.reject(
new GraphQLError(`Cannot post comment on non-existing link with id '${args.linkId}'.`),
);
}

if (!args.body || args.body.trim().length === 0) {
return Promise.reject(new GraphQLError(`Comment body cannot be empty.`));
}

const newComment = await context.prisma.comment
.create({
data: {
linkId,
body: args.body,
},
})
.catch((err: unknown) => {
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
return Promise.reject(
new GraphQLError(`Cannot post comment on non-existing link with id '${args.linkId}'.`),
);
}
return Promise.reject(err);
});

return newComment;
};
15 changes: 15 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Mutation/postLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { MutationResolvers } from '../../../types.generated';

export const postLink: NonNullable<MutationResolvers['postLink']> = async (
_parent,
args,
context,
) => {
const newLink = await context.prisma.link.create({
data: {
url: args.url,
description: args.description,
},
});
return newLink;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { QueryResolvers } from '../../../types.generated';

export const comment: NonNullable<QueryResolvers['comment']> = async (_parent, args, context) => {
return context.prisma.comment.findUnique({
where: { id: parseInt(args.id) },
});
};
27 changes: 27 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Query/feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { applySkipConstraints, applyTakeConstraints } from '../../../../utils';
import type { QueryResolvers } from '../../../types.generated';

export const feed: NonNullable<QueryResolvers['feed']> = async (_parent, args, context) => {
const where = args.filterNeedle
? {
OR: [
{ description: { contains: args.filterNeedle } },
{ url: { contains: args.filterNeedle } },
],
}
: {};

const take = applyTakeConstraints({
min: 1,
max: 50,
value: args.take ?? 30,
});

const skip = applySkipConstraints(args.skip ?? 0);

return context.prisma.link.findMany({
where,
skip,
take,
});
};
5 changes: 5 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Query/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { QueryResolvers } from '../../../types.generated';

export const info: NonNullable<QueryResolvers['info']> = async (_parent, _arg, _ctx) => {
return `This is the API of a Hackernews Clone`;
};
7 changes: 7 additions & 0 deletions examples/hackernews/src/schema/base/resolvers/Query/link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { QueryResolvers } from '../../../types.generated';

export const link: NonNullable<QueryResolvers['link']> = async (_parent, args, context) => {
return context.prisma.link.findUnique({
where: { id: parseInt(args.id) },
});
};
24 changes: 24 additions & 0 deletions examples/hackernews/src/schema/base/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
type Query {
info: String!
feed(filterNeedle: String, skip: Int, take: Int): [Link!]!
comment(id: ID!): Comment
link(id: ID!): Link
}

type Mutation {
postLink(url: String!, description: String!): Link!
postCommentOnLink(linkId: ID!, body: String!): Comment!
}

type Link {
id: ID!
description: String!
url: String!
comments: [Comment!]!
}

type Comment {
id: ID!
body: String!
link: Link
}
4 changes: 4 additions & 0 deletions examples/hackernews/src/schema/base/schema.mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Comment, Link } from '@prisma/client';

export type LinkMapper = Link;
export type CommentMapper = Comment;
Loading

0 comments on commit 9564025

Please sign in to comment.