Skip to content

Commit

Permalink
Initial update
Browse files Browse the repository at this point in the history
  • Loading branch information
gilgardosh committed Nov 11, 2024
1 parent ac2967d commit 2b5ebfb
Show file tree
Hide file tree
Showing 18 changed files with 363 additions and 133 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ packages/graphql-yoga/src/landing-page-html.ts
packages/graphql-yoga/src/graphiql-html.ts
.tool-versions
**/generated/**
**/*.generated.*
examples/apollo-federation-compatibility/src/resolvers-types.ts
**/node_modules
**/dist
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ packages/graphql-yoga/src/landing-page-html.ts
packages/render-graphiql/src/graphiql.ts
.changeset/
examples/apollo-federation-compatibility/src/resolvers-types.ts
examples/tutorial/**/*.generated.*
out/

# Since prettier doesn't support MDX2 he breaks formatting for the following
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ CREATE TABLE "Comment" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"body" TEXT NOT NULL,
"linkId" INTEGER,
CONSTRAINT "Comment_linkId_fkey" FOREIGN KEY ("linkId") REFERENCES "Link" ("id") ON DELETE SET NULL ON UPDATE CASCADE
"linkId" INTEGER NOT NULL,
CONSTRAINT "Comment_linkId_fkey" FOREIGN KEY ("linkId") REFERENCES "Link" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
4 changes: 2 additions & 2 deletions examples/hackernews/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ model Comment {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
body String
link Link? @relation(fields: [linkId], references: [id])
linkId Int?
link Link @relation(fields: [linkId], references: [id])
linkId Int
}
55 changes: 46 additions & 9 deletions examples/hackernews/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createGraphQLError, createSchema } from 'graphql-yoga';
import { createSchema } from 'graphql-yoga';
import { Prisma, type Link } from '@prisma/client';
import type { GraphQLContext } from './context';
import { GraphQLError } from 'graphql';

const typeDefinitions = /* GraphQL */ `
type Link {
Expand All @@ -13,12 +14,14 @@ const typeDefinitions = /* GraphQL */ `
type Comment {
id: ID!
body: String!
link: Link
}
type Query {
hello: String!
info: String!
feed(filterNeedle: String, skip: Int, take: Int): [Link!]!
comment(id: ID!): Comment
link(id: ID!): Link
}
type Mutation {
Expand All @@ -34,9 +37,16 @@ const parseIntSafe = (value: string): number | null => {
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 createGraphQLError(
throw new GraphQLError(
`'take' argument value '${params.value}' is outside the valid range of '${params.min}' to '${params.max}'.`,
);
}
Expand All @@ -45,7 +55,7 @@ const applyTakeConstraints = (params: { min: number; max: number; value: number

const resolvers = {
Query: {
hello: () => `Hello World!`,
info: () => `This is the API of a Hackernews Clone`,
feed: async (
parent: unknown,
args: { filterNeedle?: string; skip?: number; take?: number },
Expand All @@ -66,9 +76,11 @@ const resolvers = {
value: args.take ?? 30,
});

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

return context.prisma.link.findMany({
where,
skip: args.skip,
skip,
take,
});
},
Expand All @@ -77,6 +89,11 @@ const resolvers = {
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,
Expand Down Expand Up @@ -112,11 +129,15 @@ const resolvers = {
const linkId = parseIntSafe(args.linkId);
if (linkId === null) {
return Promise.reject(
createGraphQLError(`Cannot post common on non-existing link with id '${args.linkId}'.`),
new GraphQLError(`Cannot post common on non-existing link with id '${args.linkId}'.`),
);
}

const comment = await context.prisma.comment
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,
Expand All @@ -126,16 +147,32 @@ const resolvers = {
.catch((err: unknown) => {
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
return Promise.reject(
createGraphQLError(
new GraphQLError(
`Cannot post common on non-existing link with id '${args.linkId}'.`,
),
);
}
return Promise.reject(err);
});
return comment;

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({
Expand Down
16 changes: 16 additions & 0 deletions examples/tutorial/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;
15 changes: 15 additions & 0 deletions examples/tutorial/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/tutorial/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/tutorial/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;
};
7 changes: 7 additions & 0 deletions examples/tutorial/src/schema/base/resolvers/Query/comment.ts
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/tutorial/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/tutorial/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/tutorial/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/tutorial/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/tutorial/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;
24 changes: 24 additions & 0 deletions examples/tutorial/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { GraphQLError } from 'graphql';

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

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

export 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;
};
237 changes: 117 additions & 120 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

0 comments on commit 2b5ebfb

Please sign in to comment.