Added basic Apollo Server structure, Prisma database connection and form query by id

This commit is contained in:
Dmitriy Shishkov 2020-10-07 22:51:35 +05:00
parent ebca575f46
commit 898b17510b
18 changed files with 339 additions and 22 deletions

2
.gitignore vendored
View File

@ -4,6 +4,8 @@
.env.local
*.gen.ts
npm-debug.log*
yarn-debug.log*
yarn-error.log*

12
codegen.yml Normal file
View File

@ -0,0 +1,12 @@
overwrite: true
schema: "src/typeDefs/typeDefs.gql"
documents: null
generates:
src/typeDefs/typeDefs.gen.ts:
config:
useIndexSignature: true
wrapFieldDefinitions: true
enumsAsTypes: true
plugins:
- "typescript"
- "typescript-resolvers"

6
nodemon.json Normal file
View File

@ -0,0 +1,6 @@
{
"ignore": ["**/*.test.ts", "**/*.spec.ts", "node_modules"],
"watch": ["src"],
"exec": "yarn start",
"ext": "ts"
}

View File

@ -9,14 +9,20 @@
"apollo-server": "^2.18.2",
"graphql": "^15.3.0",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^1.10.1"
"jwks-rsa": "^1.10.1",
"nodemon": "^2.0.4"
},
"scripts": {
"dev": "ts-node-dev src/index.ts"
"dev": "nodemon",
"start": "ts-node src/index.ts",
"codegen": "graphql-codegen --config codegen.yml"
},
"devDependencies": {
"@graphql-codegen/cli": "1.17.10",
"@graphql-codegen/introspection": "1.18.0",
"@graphql-codegen/typescript": "1.17.10",
"@graphql-codegen/typescript-resolvers": "1.17.10",
"ts-node": "^9.0.0",
"ts-node-dev": "^1.0.0-pre.63",
"typescript": "^4.0.3"
}
}

View File

@ -0,0 +1,42 @@
# Migration `20201007134933-fix-optional-values`
This migration has been generated by Dm1tr1y147 at 10/7/2020, 6:49:33 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
ALTER TABLE "public"."Answer" ALTER COLUMN "userInput" DROP NOT NULL,
ALTER COLUMN "userChoise" DROP NOT NULL
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20201006185953-improved-schema-structure..20201007134933-fix-optional-values
--- datamodel.dml
+++ datamodel.dml
@@ -2,9 +2,9 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgres"
- url = "***"
+ url = "***"
}
generator client {
provider = "prisma-client-js"
@@ -75,10 +75,10 @@
formId Int?
}
model Answer {
- userInput String
- userChoise Int
+ userInput String?
+ userChoise Int?
type AnswerType
id Int @id @default(autoincrement())
FormSubmission FormSubmission? @relation(fields: [formSubmissionId], references: [id])
```

View File

@ -0,0 +1,91 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgres"
url = "***"
}
generator client {
provider = "prisma-client-js"
}
model Form {
title String
choisesQuestions ChoisesQuestion[]
inputQuestions InputQuestion[]
submissions FormSubmission[]
dateCreated DateTime @default(now())
author User @relation(fields: [userId], references: [id])
id Int @id @default(autoincrement())
userId Int
}
model ChoisesQuestion {
title String
variants Variant[]
type ChoiseType
number Int
id Int @id @default(autoincrement())
Form Form? @relation(fields: [formId], references: [id])
formId Int?
}
model Variant {
text String
id Int @id @default(autoincrement())
ChoisesQuestion ChoisesQuestion? @relation(fields: [choisesQuestionId], references: [id])
choisesQuestionId Int?
}
model InputQuestion {
title String
number Int
id Int @id @default(autoincrement())
Form Form? @relation(fields: [formId], references: [id])
formId Int?
}
enum ChoiseType {
SELECT
CHECK
CHOOSE
}
model User {
name String
forms Form[]
id Int @id @default(autoincrement())
formsSubmissions FormSubmission[]
}
model FormSubmission {
answers Answer[]
date DateTime @default(now())
user User @relation(fields: [userId], references: [id])
id Int @id @default(autoincrement())
userId Int
Form Form? @relation(fields: [formId], references: [id])
formId Int?
}
model Answer {
userInput String?
userChoise Int?
type AnswerType
id Int @id @default(autoincrement())
FormSubmission FormSubmission? @relation(fields: [formSubmissionId], references: [id])
formSubmissionId Int?
}
enum AnswerType {
INPUT
CHOISE
}

View File

@ -0,0 +1,17 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "UpdateField",
"model": "Answer",
"field": "userInput",
"arity": "Optional"
},
{
"tag": "UpdateField",
"model": "Answer",
"field": "userChoise",
"arity": "Optional"
}
]
}

View File

@ -1,4 +1,5 @@
# Prisma Migrate lockfile v1
20201006125838-initial-migration
20201006185953-improved-schema-structure
20201006185953-improved-schema-structure
20201007134933-fix-optional-values

View File

@ -76,8 +76,8 @@ model FormSubmission {
}
model Answer {
userInput String
userChoise Int
userInput String?
userChoise Int?
type AnswerType
id Int @id @default(autoincrement())

24
src/db/index.ts Normal file
View File

@ -0,0 +1,24 @@
import { PrismaClient } from "@prisma/client"
const getForm = async (db: PrismaClient, id: number) =>
db.form.findOne({
where: {
id: id,
},
include: {
author: true,
choisesQuestions: {
include: {
variants: true,
},
},
inputQuestions: true,
submissions: {
include: {
answers: true,
},
},
},
})
export { getForm }

View File

@ -0,0 +1,18 @@
import { ApolloServer } from "apollo-server"
import typeDefs from "./typeDefs"
import resolvers from "./resolvers"
import { PrismaClient } from "@prisma/client"
const server = new ApolloServer({
typeDefs,
resolvers,
context: async () => {
const db = new PrismaClient()
return { db }
},
})
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`)
})

View File

@ -1,5 +0,0 @@
[
{
}
]

60
src/resolvers/Form.ts Normal file
View File

@ -0,0 +1,60 @@
import { getForm } from "../db"
import {
Form,
FormSubmission,
QueryFormArgs,
QuestionResolvers,
Resolver,
AnswerResolvers,
} from "../typeDefs/typeDefs.gen"
import { ApolloContextType } from "../types"
const formQuery: Resolver<Form, {}, ApolloContextType, QueryFormArgs> = async (
_,
{ id },
{ db }
) => {
try {
const dbForm = await getForm(db, id)
if (dbForm == null) throw new Error("Not found")
const form: Form = {
id: dbForm.id,
title: dbForm.title,
questions: [...dbForm.choisesQuestions, ...dbForm.inputQuestions],
dateCreated: dbForm.dateCreated.toString(),
submissions: dbForm.submissions.map<FormSubmission>((submission) => {
return {
answers: submission.answers,
date: submission.date.toString(),
id: submission.id,
}
}),
}
return form
} catch (err) {
return err
}
}
const QuestionResolver: QuestionResolvers = {
__resolveType(obj: any, context, info) {
if (obj.type) {
return "ChoisesQuestion"
}
return "InputQuestion"
},
}
const AnswerResolver: AnswerResolvers = {
__resolveType(obj, context, info) {
if (obj.type == "CHOISE") return "ChoiseAnswer"
if (obj.type == "INPUT") return "InputAnswer"
return null
},
}
export { formQuery, QuestionResolver, AnswerResolver }

0
src/resolvers/User.ts Normal file
View File

17
src/resolvers/index.ts Normal file
View File

@ -0,0 +1,17 @@
import { ApolloContextType } from "../types"
import { Resolvers } from "../typeDefs/typeDefs.gen"
import {
formQuery as form,
QuestionResolver as Question,
AnswerResolver as Answer,
} from "./Form"
const resolvers: Resolvers<ApolloContextType> = {
Query: {
form,
},
Question,
Answer,
}
export default resolvers

8
src/typeDefs/index.ts Normal file
View File

@ -0,0 +1,8 @@
import { gql } from "apollo-server"
import fs from "fs"
const typeDefs = gql(
fs.readFileSync(__dirname.concat("/typeDefs.gql"), { encoding: "utf-8" })
)
export default typeDefs

View File

@ -1,20 +1,23 @@
type Query {
forms: [Form]
form(id: Int!): Form!
forms: [Form!]!
form(id: Int!): Form
}
type Form {
id: Int!
title: String!
choisesQuestions: [ChoisesQuestion!]!
inputQuestions: [InputQuestion!]!
questions: [Question!]!
submissions: [FormSubmission!]!
dateCreated: String!
}
type ChoisesQuestion {
interface Question {
title: String!
variants: [Variant!]!
number: Int!
}
type ChoisesQuestion implements Question {
title: String!
variants: [Variant!]
type: ChoiseType!
number: Int!
}
@ -23,7 +26,7 @@ type Variant {
text: String!
}
type InputQuestion {
type InputQuestion implements Question {
title: String!
number: Int!
}
@ -34,13 +37,23 @@ type FormSubmission {
date: String!
}
type Answer {
interface Answer {
id: Int!
userInput: String
userChoise: Int
type: AnswerType!
}
type InputAnswer implements Answer {
id: Int!
type: AnswerType!
userInput: String
}
type ChoiseAnswer implements Answer {
id: Int!
type: AnswerType!
userChoise: Int!
}
enum ChoiseType {
SELECT
CHECK

5
src/types.ts Normal file
View File

@ -0,0 +1,5 @@
import { PrismaClient } from "@prisma/client"
export type ApolloContextType = {
db: PrismaClient
}