Mutation implementations (#5)
* split up files * timestamps, scalars and dayjs * lib server directory * move bun types to dev * move storybook dark mode to dev * got timestamps working * fix reference * separate schema into files and add mutations
This commit is contained in:
parent
5395e7f904
commit
37d901a86c
21 changed files with 321 additions and 80 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -31,6 +31,7 @@
|
|||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"@types/bun": "^1.1.14",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.7.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"prisma": "^6.0.1",
|
||||
"storybook": "^8.4.7",
|
||||
"storybook-dark-mode": "^4.0.2",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^3.4.9",
|
||||
|
|
@ -54,12 +56,11 @@
|
|||
"@pothos/plugin-prisma": "^4.4.0",
|
||||
"@prisma/client": "6.0.1",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/bun": "^1.1.14",
|
||||
"dayjs": "^1.11.13",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-yoga": "^5.10.4",
|
||||
"pino": "^9.5.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"storybook-dark-mode": "^4.0.2",
|
||||
"zod": "^3.24.0"
|
||||
}
|
||||
}
|
||||
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Made the column `content` on table `Post` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `name` on table `User` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Post" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"published" BOOLEAN DEFAULT false,
|
||||
"authorId" INTEGER NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Post" ("authorId", "content", "id", "published", "title") SELECT "authorId", "content", "id", "published", "title" FROM "Post";
|
||||
DROP TABLE "Post";
|
||||
ALTER TABLE "new_Post" RENAME TO "Post";
|
||||
CREATE TABLE "new_User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"email" TEXT,
|
||||
"name" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
INSERT INTO "new_User" ("email", "id", "name") SELECT "email", "id", "name" FROM "User";
|
||||
DROP TABLE "User";
|
||||
ALTER TABLE "new_User" RENAME TO "User";
|
||||
CREATE UNIQUE INDEX "User_id_key" ON "User"("id");
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
|
@ -15,17 +15,21 @@ datasource db {
|
|||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
id Int @id @default(autoincrement()) @unique
|
||||
email String? @unique
|
||||
name String
|
||||
posts Post[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String?
|
||||
published Boolean @default(false)
|
||||
content String
|
||||
published Boolean? @default(false)
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
import { prisma } from '$lib/prisma';
|
||||
import { Context } from '$lib/yoga/context';
|
||||
import SchemaBuilder from '@pothos/core';
|
||||
import PrismaPlugin, { type PrismaTypesFromClient } from '@pothos/plugin-prisma';
|
||||
|
||||
type ContextType = ReturnType<typeof Context>;
|
||||
|
||||
export const builder = new SchemaBuilder<{
|
||||
Context: ContextType;
|
||||
PrismaTypes: PrismaTypesFromClient<typeof prisma>;
|
||||
}>({
|
||||
plugins: [PrismaPlugin],
|
||||
prisma: {
|
||||
client: prisma,
|
||||
// defaults to false, uses /// comments from prisma schema as descriptions
|
||||
// for object types, relations and exposed fields.
|
||||
// descriptions can be omitted by setting description to false
|
||||
exposeDescriptions: false,
|
||||
// use where clause from prismaRelatedConnection for totalCount (defaults to true)
|
||||
filterConnectionTotalCount: true,
|
||||
// warn when not using a query parameter correctly
|
||||
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn'
|
||||
}
|
||||
});
|
||||
|
||||
const User = builder.prismaObject('User', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
email: t.exposeString('email'),
|
||||
name: t.exposeString('name'),
|
||||
posts: t.relation('posts')
|
||||
})
|
||||
});
|
||||
|
||||
const Post = builder.prismaObject('Post', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
title: t.exposeString('title'),
|
||||
content: t.exposeString('content'),
|
||||
published: t.exposeBoolean('published'),
|
||||
author: t.relation('author')
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryType({
|
||||
fields: (t) => ({
|
||||
version: t.string({
|
||||
resolve: (parent, args, context) => context.config.app_version
|
||||
}),
|
||||
users: t.prismaField({
|
||||
type: [User],
|
||||
resolve: async () => {
|
||||
return await prisma.user.findMany();
|
||||
}
|
||||
}),
|
||||
posts: t.prismaField({
|
||||
type: [Post],
|
||||
resolve: async () => {
|
||||
return await prisma.post.findMany();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const Schema = builder.toSchema();
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { logger } from '$lib/logger';
|
||||
import { logger } from '$lib/server/logger';
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface Configuration {
|
||||
28
src/lib/server/pothos/builder.ts
Normal file
28
src/lib/server/pothos/builder.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import type { Context } from '$lib/server/yoga';
|
||||
import SchemaBuilder from '@pothos/core';
|
||||
import PrismaPlugin, { type PrismaTypesFromClient } from '@pothos/plugin-prisma';
|
||||
import type { Scalars } from './schema/Scalars';
|
||||
|
||||
type PothosType = {
|
||||
Context: ReturnType<typeof Context>;
|
||||
PrismaTypes: PrismaTypesFromClient<typeof prisma>;
|
||||
Scalars: Scalars;
|
||||
};
|
||||
|
||||
SchemaBuilder.allowPluginReRegistration = true;
|
||||
|
||||
export const builder = new SchemaBuilder<PothosType>({
|
||||
plugins: [PrismaPlugin],
|
||||
prisma: {
|
||||
client: prisma,
|
||||
// defaults to false, uses /// comments from prisma schema as descriptions
|
||||
// for object types, relations and exposed fields.
|
||||
// descriptions can be omitted by setting description to false
|
||||
exposeDescriptions: false,
|
||||
// use where clause from prismaRelatedConnection for totalCount (defaults to true)
|
||||
filterConnectionTotalCount: true,
|
||||
// warn when not using a query parameter correctly
|
||||
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn'
|
||||
}
|
||||
});
|
||||
1
src/lib/server/pothos/index.ts
Normal file
1
src/lib/server/pothos/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './schema';
|
||||
14
src/lib/server/pothos/schema/Scalars/Date.ts
Normal file
14
src/lib/server/pothos/schema/Scalars/Date.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { builder } from '../../builder';
|
||||
|
||||
export const DateScalar = builder.scalarType('Date', {
|
||||
description: 'Date Scalar in ISO format',
|
||||
serialize: (date) => {
|
||||
return date.toISOString();
|
||||
},
|
||||
parseValue: (date) => {
|
||||
if (typeof date !== 'string') {
|
||||
throw new Error('Cyka blyat');
|
||||
}
|
||||
return new Date(date);
|
||||
}
|
||||
});
|
||||
8
src/lib/server/pothos/schema/Scalars/index.ts
Normal file
8
src/lib/server/pothos/schema/Scalars/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export * from './Date';
|
||||
|
||||
export type Scalars = {
|
||||
Date: {
|
||||
Input: Date;
|
||||
Output: Date;
|
||||
};
|
||||
};
|
||||
18
src/lib/server/pothos/schema/index.ts
Normal file
18
src/lib/server/pothos/schema/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { builder } from '../builder';
|
||||
|
||||
builder.queryType({});
|
||||
|
||||
builder.queryField('version', (t) =>
|
||||
t.string({
|
||||
description: 'Application version',
|
||||
resolve: (parent, args, context) => context.config.app_version
|
||||
})
|
||||
);
|
||||
|
||||
builder.mutationType({});
|
||||
|
||||
import './Scalars';
|
||||
import './posts';
|
||||
import './users';
|
||||
|
||||
export const Schema = builder.toSchema();
|
||||
110
src/lib/server/pothos/schema/posts.ts
Normal file
110
src/lib/server/pothos/schema/posts.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import { builder } from '../builder';
|
||||
|
||||
export const Post = builder.prismaObject('Post', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
title: t.exposeString('title'),
|
||||
content: t.exposeString('content'),
|
||||
published: t.exposeBoolean('published'),
|
||||
author: t.relation('author'),
|
||||
createdAt: t.expose('createdAt', {
|
||||
type: 'Date'
|
||||
}),
|
||||
updatedAt: t.expose('updatedAt', {
|
||||
type: 'Date'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const CreatePost = builder.inputType('CreatePost', {
|
||||
fields: (t) => ({
|
||||
title: t.string({
|
||||
required: true
|
||||
}),
|
||||
content: t.string({
|
||||
required: true
|
||||
}),
|
||||
published: t.boolean(),
|
||||
authorId: t.id({
|
||||
required: true
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const UpdatePost = builder.inputType('UpdatePost', {
|
||||
fields: (t) => ({
|
||||
id: t.id({
|
||||
required: true
|
||||
}),
|
||||
title: t.string(),
|
||||
content: t.string(),
|
||||
published: t.boolean(),
|
||||
authorId: t.id()
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryFields((t) => ({
|
||||
posts: t.prismaField({
|
||||
type: [Post],
|
||||
resolve: async () => {
|
||||
return await prisma.post.findMany();
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
builder.mutationFields((t) => ({
|
||||
createPost: t.field({
|
||||
type: Post,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: CreatePost })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const author = await prisma.user.findUnique({
|
||||
where: { id: Number(args.input.authorId) }
|
||||
});
|
||||
if (!author) {
|
||||
throw new Error('Author does not exist!');
|
||||
}
|
||||
const post = await prisma.post.create({
|
||||
data: {
|
||||
title: args.input.title,
|
||||
content: args.input.content,
|
||||
published: args.input.published,
|
||||
author: {
|
||||
connect: {
|
||||
id: author.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
}),
|
||||
updatePost: t.field({
|
||||
type: Post,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: UpdatePost })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.post.update({
|
||||
where: {
|
||||
id: Number(args.input.id)
|
||||
},
|
||||
data: {
|
||||
title: args.input.title ?? undefined,
|
||||
content: args.input.content ?? undefined,
|
||||
published: args.input.published,
|
||||
...(args.input.authorId && {
|
||||
author: {
|
||||
connect: {
|
||||
id: Number(args.input.authorId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
})
|
||||
}));
|
||||
83
src/lib/server/pothos/schema/users.ts
Normal file
83
src/lib/server/pothos/schema/users.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import { builder } from '../builder';
|
||||
|
||||
export const User = builder.prismaObject('User', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
email: t.exposeString('email'),
|
||||
name: t.exposeString('name'),
|
||||
posts: t.relation('posts'),
|
||||
createdAt: t.expose('createdAt', {
|
||||
type: 'Date'
|
||||
}),
|
||||
updatedAt: t.expose('updatedAt', {
|
||||
type: 'Date'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const CreateUser = builder.inputType('CreateUser', {
|
||||
fields: (t) => ({
|
||||
email: t.string({
|
||||
required: true
|
||||
}),
|
||||
name: t.string({
|
||||
required: true
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const UpdateUser = builder.inputType('UpdateUser', {
|
||||
fields: (t) => ({
|
||||
id: t.id({
|
||||
required: true
|
||||
}),
|
||||
email: t.string(),
|
||||
name: t.string()
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryFields((t) => ({
|
||||
users: t.prismaField({
|
||||
type: [User],
|
||||
resolve: async () => {
|
||||
return await prisma.user.findMany();
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
builder.mutationFields((t) => ({
|
||||
createUser: t.field({
|
||||
type: User,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: CreateUser })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.user.create({
|
||||
data: {
|
||||
email: args.input.email,
|
||||
name: args.input.name
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
}),
|
||||
updateUser: t.field({
|
||||
type: User,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: UpdateUser })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.user.update({
|
||||
where: {
|
||||
id: Number(args.input.id)
|
||||
},
|
||||
data: {
|
||||
email: args.input.email,
|
||||
name: args.input.name ?? undefined
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Config } from '$lib/config';
|
||||
import { Config } from '$lib/server/config';
|
||||
import type { YogaInitialContext } from 'graphql-yoga';
|
||||
|
||||
export const Context = (initialContext: YogaInitialContext) => ({
|
||||
2
src/lib/server/yoga/index.ts
Normal file
2
src/lib/server/yoga/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './context';
|
||||
export * from './server';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { yogaLogger } from '$lib/logger';
|
||||
import { Schema } from '$lib/pothos';
|
||||
import { yogaLogger } from '$lib/server/logger';
|
||||
import { Schema } from '$lib/server/pothos';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { createYoga } from 'graphql-yoga';
|
||||
import { Context } from './context';
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { Yoga } from '$lib/yoga';
|
||||
import { Yoga } from '$lib/server/yoga';
|
||||
|
||||
export { Yoga as GET, Yoga as POST };
|
||||
Loading…
Add table
Reference in a new issue