🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Server-Side (NodeJS Express)
  • Pre-Action
  • Structure
  • Result
  • Client-Side (React)
  • Pre-Action
  • Configuration
  • Hook
  • Result
  • REST API + GraphQL
  • Pre-Action
  • Data Source
  • Schema
  • Resolver
  • Integration

Was this helpful?

  1. Web Communication
  2. GraphQL

Implementation (NodeJS + React)

Server-Side (NodeJS Express)

Pre-Action

npm install apollo-server-express

Structure

Data

  • Define the data source

const studentList =  [
    { sid: 1, name: "Peter", age: 18, courseId: [1,3]},
    { sid: 2, name: "Mary", age: 23, courseId: [1,2]}
];

const courseList = [
    { cid: 1, name: "Chinese", studentId:[1,2] },
    { cid: 2, name: "English", studentId: [2] },
    { cid: 3, name: "Math", studentId:[1]}
]

export {studentList, courseList};

Schema

  • Define what type of input can be entered, the format of query, type, ......

  • Separate by domain (e.g: Student and Course)

// student.js
import { gql } from "apollo-server-express";

const schema = gql`
  input PostStudent {
    name:String!
    age:Int!
  }

  type Student{
      sid: Int!
      name: String!
      age: Int!
      courses: [Course]
  }
  
  extend type Query {
    students:[Student]
    student(id: Int!): Student!
  }

  extend type Mutation{
    submitStudent(input: PostStudent!): Student
    deleteStudent: Int
  }
`;

export default schema;
//course.js
import { gql } from "apollo-server-express";

const schema = gql`
  type Course{
    cid: Int!
    name: String!
    students: [Student!]
  }

  extend type Query {
    courses:[Course]
    course(id: Int!): Course!
  }

`;

export default schema;
  • Then group them together

import { gql } from 'apollo-server-express';
 
import studentSchema from './student.js';
import courseSchema from './course.js';
 
const linkSchema = gql`
  type Query {
    _: Boolean
  }
 
  type Mutation {
    _: Boolean
  }
 
  type Subscription {
    _: Boolean
  }
`;
 
export default [linkSchema, studentSchema, courseSchema];

Resolver

  • Define the implementation of the query, mutation, ......

  • Separate by domain (e.g: Student and Course)

// student.js
class Student {
    constructor(id,{name, age, courseId}){
      this.sid = id;
      this.name = name;
      this.age = age;
      this.courseId = courseId;
    }
}

const resolvers = {
    Query:{
      students: (parent, arg, {studentList}) => studentList.map((student, index) => new Student(index,student)),
      student: (parent, {id}, {studentList}) => new Student(id, studentList[id]),
      test: async(parent, arg, {dataSources}) => { 
        const data = await dataSources.StudentAPI.getStudentList();
        console.log(data);
        return data;
      }
    },
    
    Mutation:{
      submitStudent: (parent, {input}, {studentList}) =>{
        console.log('dataSources'. dataSources);
        id = studentList.length;
        studentList.push(input);
        return new Student(id, input);
      },
      deleteStudent: () => {
         id = studentList.length - 1;
         studentList.pop(studentList[id]);
         return id;
      }
    },
    
    Student:{
        courses: (parent, args, {courseList}) =>{
            const { courseId } = parent;
            if(courseId){
                return courseList.filter(course => courseId.includes(course.cid));
            }
            else{
                return [];
            }
        }
    }
};

export default resolvers;
// course.js
class Course {
    constructor(id, {name, studentId}){
      this.cid = cid;
      this.name = name;
      this.studentId = studentId;
    }
}

const resolvers = {
    Query:{
      courses: (parent, arg, {courseList}) => courseList.map((course, index) => new Course(index, course)),
      course: (parent, {id}, {courseList}) => new Student(id, courseList[id])
    },

    Course:{
        students: (parent, args, {studentList}) =>{
            const { studentId } = parent;
            if(studentId){
                return studentList.filter(student => studentId.includes(student.sid));
            }
            else{
                return [];
            }
        }
    }
};

export default resolvers;
  • Then put them together

import studentResolvers from './student.js';
import courseResolvers from './course.js';
 
export default [studentResolvers, courseResolvers];

Output

  • Group data, schema and resolver together

import express from "express";
const app = express();
import cors from "cors";
import bodyParser from "body-parser";
import  { ApolloServer } from "apollo-server-express";
import schema from "./graphql/schema/index.js";
import resolvers from "./graphql/resolver/index.js";
import {studentList, courseList} from "./graphql/data.js";

app.use(
    cors({
      origin: "*",
      methods: ["GET", "POST"],
      allowedHeaders: "Content-Type, Authorization, X-Requested-With",
    })
);

app.use(bodyParser.json());

const server = new ApolloServer({
  typeDefs: schema,
  resolvers,
  context: {
    studentList,
    courseList
  }
});

server.applyMiddleware({app, path:'/graphql'});

app.listen(8080, function () { 
  console.log("Server Start "); 
});

Result

Client-Side (React)

Pre-Action

npm install @apollo/client

Configuration

import {ApolloClient, InMemoryCache} from '@apollo/client';

const client = new ApolloClient({
  uri: 'http://localhost:8080/graphql',
  cache: new InMemoryCache()
});

export default client;
import { ApolloProvider } from '@apollo/client';
import client from "./config/apollo";

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider >
  ,
  document.getElementById('root')
);

Hook

  • useQuery (To get the ideal format of data from API End Point)

import React,{useEffect} from "react";
import { gql, useQuery } from "@apollo/client";
import { Table } from 'reactstrap';

//Define the query
const getUsersQuery = gql`
    query getStudents($id: Int!) {
     studentList: students{
        ...studentData
     }
     singleStudent: student(id: $id){
       ...studentData
     }
    }
    fragment studentData on Student{
        id
        name
        age
    }
  `;

  // the format of data
interface studentData{
   studentList:student[];
   singleStudent:student;
}

interface student{
    id: number;
    name:string;
    age: number;
}

// the format of input entered to query
interface studentDataVars {
    id: number;
}

const StudentList:React.FC<{reload:boolean}> = ({reload}) => {

    const {loading, data} = useQuery<studentData, studentDataVars>
    (getUsersQuery, {variables:{id: 1}});    

    if(!loading)
        console.log('data', data);
    
    return(
        <Table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Age</th>
                </tr>
            </thead>
            <tbody>
                {data?.studentList.map(({id, name, age}) => {
                    return(
                    <tr key={id}>
                        <td>{name}</td>
                        <td>{age}</td>
                    </tr>
                    )
                })}
            </tbody>
        </Table>
    );
};

export default StudentList;
  • useMutation (Update or Insert the new data)

import React, { useState, useEffect} from 'react';
import "../style/output.css"
import IconButton from '@material-ui/core/IconButton';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DialogActions from '@material-ui/core/DialogActions';
import TextField from '@material-ui/core/TextField';
import { Button } from 'reactstrap';
import { gql, useMutation } from "@apollo/client";

const StudentEditor:React.FC<{setReload:()=>void}> = ({setReload}) => {
    const [name, setName] = useState<string>("");
    const [age, setAge] = useState<number> (0);
    const [createDialogOpen, setCreateDialogOpen] = useState<boolean>(false);
    
    //Define the query for insertion
    const ADD_STUDENT = gql`
    mutation AddNewStudent($input: PostStudent!){
        submitStudent(input: $input){
            name
        }
    }
`;
    //Define the format of callback data
    interface studentData{
        name:string
    }

    //Define the format of user input entered to query
    interface input{
        name:string;
        age:number;
    }

    const [addStudent, {data}] = useMutation<{submitStudent:studentData},{input:input}>
    (ADD_STUDENT,{variables:{input:{name:name,age:age}}});

    const submitStudent = () =>{
        console.log("name", name," age ", age);
        addStudent();
        window.location.reload();
        setCreateDialogOpen(false);
    }
    
    if(data){
        console.log('data',data,"saved");
    }

    return (
        <div className=" flex flex-col items-center justify-center">
            <IconButton onClick={()=>setCreateDialogOpen(true)}>
                <AddCircleIcon/>
            </IconButton>         
            <Dialog open={createDialogOpen} onClose={()=>setCreateDialogOpen(false)}>
                <DialogTitle>
                    Please Enter Your Information !!!
                </DialogTitle>
                <DialogContent>
                    <TextField  label="Name" onChange={(event)=>{setName(event.target.value)}} />
                </DialogContent>   
                <DialogContent>
                    <TextField  label="Age" onChange={(event)=>{setAge(parseInt(event.target.value))}} type="number" />
                </DialogContent>
                <DialogActions>
                    <Button onClick={submitStudent}>
                        <span>Submit</span>
                    </Button>
                </DialogActions>
            </Dialog>     
        </div>
    )
}

Result

REST API + GraphQL

  • we can let rest api as a datasource, reformat the format of data by using graphql

Pre-Action

npm install apollo-datasource-rest

Data Source

import {RESTDataSource} from "apollo-datasource-rest";

class StudentAPI extends RESTDataSource {
    constructor(){
      super();
      this.baseURL = 'http://localhost:8081';
    }
  
    async getStudentList(){
      const data = await this.get("/getAll");
      return data;
    }

    async insertNewStudent(name, age, className){
        const data = await this.post("/insert", {
          name: name,
          age: age,
          className: className
        })
        return data;
    }
}

export default StudentAPI;

Schema

import { gql } from "apollo-server-express";

const schema = gql`
 
  input PostStudentHobby{
    name: String!
    age: Int!
    className: String!
  }

  type StudentWithHobby{
    id: String!
    name: String!
    className: String!
    hobbyList: [String]
  }

  extend type Query {
    test: [StudentWithHobby]
  }

  extend type Mutation{
    submitStudentWithHobby(input: PostStudentHobby!): String
  }

`;

export default schema;

Resolver

const resolvers = {
    Query:{
      test: async(parent, arg, {dataSources}) => { 
        const data = await dataSources.StudentAPI.getStudentList();
        return data;
      }
    },
    Mutation:{
      submitStudentWithHobby: async(parent, {input}, {dataSources}) => {
        const data = await dataSources.StudentAPI.insertNewStudent(input.name, input.age, input.className);
        return data;
      }
    }
};

export default resolvers;

Integration

import express from "express";
const app = express();
import cors from "cors";
import bodyParser from "body-parser";
import  { ApolloServer } from "apollo-server-express";
import schema from "./graphql/schema/index.js";
import resolvers from "./graphql/resolver/index.js";
import StudentAPI from "./graphql/test.js"

app.use(
    cors({
      origin: "*",
      methods: ["GET", "POST"],
      allowedHeaders: "Content-Type, Authorization, X-Requested-With",
    })
);

app.use(bodyParser.json());

const server = new ApolloServer({
  typeDefs: schema,
  resolvers,
  dataSources: () => ({
    StudentAPI: new StudentAPI()
  })
});
PreviousREST API vs GraphQLNextServer-Sent Event

Last updated 2 years ago

Was this helpful?