Prisma/PostgreSQL에 GraphQL Todolist 서버 구축

8458 단어 graphqlnode

Github 및 데모



GraphQL 및 Prisma에 대한 간략한 소개



GraphQL은 2015년에 Facebook에서 개발했습니다. 클라이언트 측에서는 여러 URL 또는 ORM/데이터베이스 요청이 아닌 JSON과 같은 인터페이스(위 이미지 참조)를 통해 중첩 데이터 가져오기를 더 쉽게 만듭니다. 서버 측에서는 에이징 필드에서 행을 추가하거나 삭제하여 데이터 모델을 업데이트할 수 있습니다.

Prisma은 대체 ORM 및 SQL 쿼리 빌더입니다.

다음은 GraphQL 백엔드를 처음부터 빌드하는 방법을 보여줍니다.

전제 조건:



  • 컴퓨터에 설치된 Node.js

  • PostgreSQL 데이터베이스 서버 실행 중

  • A. 설정 데이터베이스:


    mkdir todo
    mkdir todo/backend
    cd todo/backend
    npm init -y
    npm install @prisma/cli - save-dev
    npx prisma init

    데이터베이스 모델:


    code ./prisma/schema.prisma
    datasource db {
      provider = "postgresql"
      url      = env("DATABASE_URL")
    }
    
    generator client {
      provider = "prisma-client-js"
    }
    
    model Post {
      id        Int      @default(autoincrement()) @id
      createdAt DateTime @default(now())
      title     String
      content   String? //question mark means opational
      author    User     @relation(fields: [authorId], references: [id])
      authorId  Int
    }
    
    model User {
      id       Int     @default(autoincrement()) @id
      email    String  @unique
      name     String?
      password String
      posts    Post[]
    }
    
    
    code ./prisma/.env
    DATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DATABASE
    

    프리즈마 마이그레이션:


    npx prisma migrate save --name init --experimental
    npx prisma migrate up --experimental
    npx prisma generate

    나. 서버 구축


    npm i graphql-yoga @prisma/client
    code index.js

    const { PrismaClient } = require('@prisma/client')
    const prisma = new PrismaClient()
    const { GraphQLServer } = require('graphql-yoga')
    
    const Query = require('./resolvers/query.js')
    const Mutation = require('./resolvers/mutation.js')
    const User = require('./resolvers/user.js')
    const Post = require('./resolvers/post.js')
    
    const resolvers = {
      Query,
      Mutation,
      User,
      Post
    }
    
    const server = new GraphQLServer({
      typeDefs: './schema.graphql',
      resolvers,
      context: request => {
        return {
          ...request,
          prisma,
        }
      },
    })
    
    const PORT = process.env.PORT || 4000
    server.start(PORT, () => console.log(`Server is running on http://localhost:4000`))
    

  • typeDefs는 t 섹션에 작성된 스키마에 연결합니다. 데이터베이스 경로, 데이터 유형 및 이들 간의 관계를 정의했습니다.
  • resolvers는 각 관련 스크립트에서 데이터를 처리하는 방법을 정의했습니다. 예를 들어 데이터 가져오기를 담당하는 쿼리, CRUD/가입/로그인 기능에 대한 돌연변이, 나중에 다룰 것입니다. 나머지 두 개(Resolvers 필드의 User 및 Post)는 데이터 관계를 정의합니다.
  • context에는 해석기 체인을 통해 전달되는 사용자 지정 데이터가 포함되어 있습니다.

  • C. typeDefs, 해석기 및 사용자 식별 빌드


  • typeDef 정의code schema.graphql

  • type Query {
      info: [Post!]!
    }
    
    type Mutation {
      upsertPost (postId:ID!, title: String!, content: String): Post!
      deletePost (postId:ID!): Post
    
      signup(email: String!, password: String!, name: String!): AuthPayload
      login(email: String!, password: String!): AuthPayload
    }
    
    type Post {
      id: ID!
      title: String!
      content: String
      author:    User
      createdAt: String!
    }
    
    type AuthPayload {
      token: String
      user: User
    }
    
    type User {
      id: ID!
      name: String
      email: String!
      posts: [Post]
    }
    

  • 해석기를 정의
  • npm i jsonwebtoken bcryptjs dotenvmkdir resolverscode ./resolvers/query.js
    const { getUserId } = require('../utils')
    
    function info(parent, args, context, info) {
      const userId = getUserId(context)
      const Posts = context.prisma.post.findMany({
        where: {
          authorId: parseInt(userId)
        }
      })
    
      return Posts
    }
    
    
    module.exports = {
      info
    }
    
    code ./resolvers/mutation.js
    const { getUserId } = require('../utils')
    const bcrypt = require('bcryptjs')
    const jwt = require('jsonwebtoken')
    require('dotenv').config()
    const APP_SECRET = process.env.APP_SECRET
    
    function upsertPost(parent, args, context, info) {
      const userId = getUserId(context)
      const upsertPost = context.prisma.post.upsert({
        where: {
          id: parseInt(args.postId)
        },
        update: {
          title: args.title,
          content: args.content,
        },
        create: {
          title: args.title,
          content: args.content,
          author: {
            connect: { id: parseInt(userId) },
          },
        },
      })
    
      return upsertPost
    }
    
    
    function deletePost(parent, args, context, info) {
      const deletePost = context.prisma.post.delete({
    
        where: {
          id: parseInt(args.postId),
        },
      })
    
      return deletePost
    }
    
    async function signup(parent, args, context, info) {
      const password = await bcrypt.hash(args.password, 10)
      const user = await context.prisma.user.create({ data: { ...args, password } })
      const token = jwt.sign({ userId: user.id }, APP_SECRET)
    
      return {
        token,
        user,
      }
    }
    
    async function login(parent, args, context, info) {
      const user = await context.prisma.user.findOne({ where: { email: args.email } })
      if (!user) {
        throw new Error('No such user found')
      }
      const valid = await bcrypt.compare(args.password, user.password)
      if (!valid) {
        throw new Error('Invalid password')
      }
      const token = jwt.sign({ userId: user.id }, APP_SECRET)
    
      return {
        token,
        user,
      }
    }
    
    module.exports = {
      upsertPost,
      deletePost,
    
      signup,
      login,
    }
    
    code ./resolvers/user.js
    function posts(parent, args, context) {
      return context.prisma.user.findOne({ where: { id: parent.id } }).posts()
    }
    
    module.exports = {
      posts,
    }
    
    code ./resolvers/post.js
    function author(parent, args, context) {
      return context.prisma.post.findOne({ where: { id: parent.id } }).author()
    }
    
    module.exports = {
      author,
    }
    

    4개의 스크립트 기능은 위의 두 번째 항목(리졸버)에 대해 간략하게 설명했습니다.
  • 사용자 식별을 처리하는 도우미 빌드code utils.js

  • const jwt = require('jsonwebtoken')
    require('dotenv').config()
    const APP_SECRET = process.env.APP_SECRET
    
    function getUserId(context) {
      const Authorization = context.request.get('Authorization')
      if (Authorization) {
        const token = Authorization.replace('Bearer ', '')
        const { userId } = jwt.verify(token, APP_SECRET)
        return userId
      }
    
      throw new Error('Not authenticated')
    }
    
    module.exports = {
      getUserId,
    }
    
    code .env
    APP_SECRET = Your_APP_SECRET
    

    D. GraphQL 서버 시작


    node index.js
    GraphQL 왼쪽 열에서 다음 명령을 시도할 수 있습니다.

  • 가입하기

  • mutation {
      signup(
        name: "__yourname__"
        email: "[email protected]__"
        password: "__yourpassword__"
      ) {
        token
        user {
          id
        }
      }
    }
    


  • 로그인

  • mutation {
      login(
        email: "[email protected]__"
        password: "__yourpassword__"
      ) {
        token
        user {
          id
          name
          posts{
            id
            title
          }
        }
      }
    }
    

  • 할 일 추가

  • ㅏ. 헤더에 토큰 복사(왼쪽 하단)

    { "Authorization": "Bearer __Token__" }
    

    비. 명령

    mutation {
      upsertPost(
        postId:"0"
        title: "www.graphqlconf.org"
      ) {
        id
        titile
      }
    }
    


  • 할 일 삭제

  • mutation {
      deletePost(
        postId:"__Id__" //enter todo id
      ) {
        id
      }
    }
    

    좋은 웹페이지 즐겨찾기