movie page 

## evironment and etc..
- front end： react + apollo (not inclueded in this part)
- back end： graphql + express js + mongodb
- OS: macos big sur
- Others: 
  - nodejs
  - npm
  - nodemon： server gonna update with source automatically
- repo: 
  - [server-side](https://github.com/devbohan/movie_page)
  - [client-side](https://github.com/devbohan/movie_page_client)
## backend part

### server setup

- `mkdir workspace`
- `cd workspace; mkdir server; cd server`
- `npm install express`
- `npm install nodemon` (not required optional package but if not will became really noisy for reload server every time)
- `npm install graphql express-graphql`
  -  might get error then：
      - `npm uninstall nodemon` 
      - `sudo npm install -g --force nodemon`
- `touch app.js`:

  - in app.js：
    ```javascript 
    const express = require("express")
    const { graphqlHTTP } = require('express-graphql') 
    const app = express()
    
    app.use('/graphql', graphqlHTTP({
        
    }))
    
    app.listen(4000, () => {
        console.log('listen port 4000')
    })

- `nodemon app`

### schema setup

- `mkdir schema` in server
- schema.js (temporarily)
  - inside of it:
  - ``` javascript 
    const graphql = require('graphql')
    
    const {GraphQLObjectType, GraphQLID, GraphQLString} = graphql
    
    const MovieType = new GraphQLObjectType({
        name: 'Movie',
        fields: () => ({
            id: {type: GraphQLID},
            name: {type: GraphQLString},
            genre: {type: GraphQLString},
        })
    })
    
    const RootQuery = new GraphQLObjectType({
        name: 'RootQueryType',
        fields: {
            movie: {
                type: MovieType,
                args: {id: {type: GraphQLString}},
                resolve(parents, args){
    
                }
            }
        }
    })

### mongodb set up 

- [MongoDB](https://cloud.mongodb.com/v2/639186657dd02f23dedeb472#clusters/connect?clusterId=Cluster0)
- intall following packages in './server':
  - 　`npm install express-graphql`
  - 　`npm install mongodb`
  - 　`npm install mongoose`
- add code to `app.js`
  - ```javascript
    const express = require("express")
    const { graphqlHTTP }  = require('express-graphql')
    const mongoose = require('mongoose')
    const app = express()
    
    mongoose.connect('mongodb+srv://<your id here>:<your pass here>@cluster0.gtgxruw.mongodb.net/?retryWrites=true&w=majority')
    mongoose.connection.once('open', () => {
        console.log('db connected')
    })
    app.use('/graphql', graphqlHTTP({
    
    }))
    
    app.listen(4000, () => {
        console.log('listen port 4000')
    })



- [granqlHTTP error troubleshooting: some javascript grammer](https://qiita.com/shibuya01055/items/2d6846efeefc9800943a)
- run `nodemon app`
- got following then done
  - ``` 
    listen port 4000
    db connected

### query

- create records in your MongoDB Cluster DB
- add your database name in `app.js`
  - ```javascript 
     mongoose.connect('mongodb+srv://.../<add your database name here>?retryWrites=true&w=majority')

- create model `movie.js` in `server/models` <= directory create by yourself
  - ```javascript
    const mongoose = require('mongoose')

    const Schema = mongoose.Schema
    
    const movieSchema = new Schema({
        name : String,
        genre: String,
    })

    module.exports = mongoose.model('Movie', movieSchema)  

- add some source to app.js
  - ```javascript   
    app.use('/graphql', graphqlHTTP({
    schema,
    graphiql: true
    }))

- add sth to `schema.js` as well
  - ```javascript 
    const graphql = require('graphql')
    const Movie = require('../models/movie')
    const {GraphQLObjectType, GraphQLSchema, GraphQLID, GraphQLString} = graphql
    
    const MovieType = new GraphQLObjectType({
        name: 'Movie',
        fields: () => ({
            id: {type: GraphQLID},
            name: {type: GraphQLString},
            genre: {type: GraphQLString},
        })
    })
    
    const RootQuery = new GraphQLObjectType({
        name: 'RootQueryType',
        fields: {
            movie: {
                type: MovieType,
                args: {id: {type: GraphQLString}},
                resolve(parents, args){
                    return Movie.findById(args.id)
                }
            }
        }
    })
    
    module.exports = new GraphQLSchema({
        query: RootQuery
    })  

- finally go `http://localhost:4000/graphql` and get following stuff: some queries
  ![スクリーンショット 2022-12-08 18.25.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/612738cf-210d-7627-31fa-bf810305f9eb.png)

### mutation insert data 1/2
- add mutaion in `schema.js`
  - ``` javascript 
        const Mutation = new GraphQLObjectType({
            name: 'Mutation',
            fields: {
                addMovie: {
                    type: MovieType,
                    args: {
                        name: {type: GraphQLString},
                        genre: {type: GraphQLString}
                    },
                    resolve(parents, args){
                        let movie = new Movie({
                            name: args.name,
                            genre: args.genre
                        })
        
                        return movie.save()
                    }
                }
            }
        
        })
        
        module.exports = new GraphQLSchema({
            query: RootQuery,
            mutation: Mutation
        })

- test on `http://localhost:4000/graphql?`
![スクリーンショット 2022-12-08 18.47.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/4492d0fa-1b6c-552d-b4f4-f1d0e3b5b277.png)

## mutation insert data 2/2

- add another model (next to movie.js)
  - ``` javascript 
    const mongoose = require('mongoose')
    
    const Schema = mongoose.Schema
    
    const directorSchema = new Schema({
        name : String,
        age: Number,
    })
    
    module.exports = mongoose.model('Director', directorSchema)

- add another mutation in `schema.js`
  - ``` javascript 
    const Mutation = new GraphQLObjectType({
        name: 'Mutation',
        fields: {
            addMovie: {
                type: MovieType,
                args: {
                    name: {type: GraphQLString},
                    genre: {type: GraphQLString}
                },
                resolve(parents, args){
                    let movie = new Movie({
                        name: args.name,
                        genre: args.genre
                    })
    
                    return movie.save()
                }
            },
            addDirector: {
                type: DirectorType,
                args: {
                    name: {type: GraphQLString},
                    age: {type: GraphQLInt}
                },
                resolve(parents, args){
                    let director = new Director({
                        name: args.name,
                        age: args.age
                    })
    
                    return director.save()
                }
            }
        }
    })

- and run on `http://localhost:4000/graphql`
![スクリーンショット 2022-12-08 19.00.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/3903677a-b16f-63af-40cb-028aea41440b.png)

### relation

- add `directorId` to movie model
  - ```javascript
    const mongoose = require('mongoose')

    const Schema = mongoose.Schema
    
    const movieSchema = new Schema({
        name : String,
        genre: String,
        directorId: String,//
    })
    
    module.exports = mongoose.model('Movie', movieSchema)

- add `director` to `MovieType` in `schema.js` & add `movie` to `DirectorType`
  - ```javascript
    const MovieType = new GraphQLObjectType({
        name: 'Movie',
        fields: () => ({
            id: {type: GraphQLID},
            name: {type: GraphQLString},
            genre: {type: GraphQLString},
            director: { // one movie has only one director
                type: DirectorType,
                resolve(parent, args) {
                    return Director.findById(parent.directorId)
                }
            }
        })
    })
    
    const DirectorType = new GraphQLObjectType({
        name: 'Director',
        fields: () => ({
            id: {type: GraphQLID},
            name: {type: GraphQLString},
            age: {type: GraphQLInt},
            movie: { // one director might has made many movies
                type: new GraphQLList(MovieType),
                resolve(parent, args) {
                    return Movie.find({directorId: parent.id})
                }
            }
        })
    })

- add `directorId` to `Mutation` in `schema.js`
  - ```javascript
        addMovie: {
            type: MovieType,
            args: {
                name: {type: GraphQLString},
                genre: {type: GraphQLString},
                directorId: {type: GraphQLID}, 
            },
            resolve(parents, args){
                let movie = new Movie({
                    name: args.name,
                    genre: args.genre,
                    directorId: args.directorId 
                })

                return movie.save()
            }
        },

- drop movies from mongodb..
- and able to get the mutation result like following by now...
![スクリーンショット 2022-12-08 21.56.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/5c71520f-47e5-afa1-48c5-e8a6c6c34ced.png)
- and movies collection is back..
![スクリーンショット 2022-12-08 21.58.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/751ef294-614d-57c9-8c83-27bf3ec10c20.png)
- we add another record to collection movies
- and we gonna get result of query like following
![スクリーンショット 2022-12-08 22.16.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/a01ab422-a189-6650-b406-35d89a085c68.png)

### fetch a list of movies and directors

- add new queries in `schema.js`
  - ```javascript
       movies: {
            type: new GraphQLList(MovieType),//
            resolve(parent, args) {
                return Movie.find({}) //find means find a list in Movie
            }
        },
        directors: {
            type: new GraphQLList(DirectorType),//
            resolve(parent, args) {
                return Director.find({}) 
            }
        },

- list retrieved like following pic..
![スクリーンショット 2022-12-09 12.19.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/cb9c5261-9bd5-ad87-ec53-b122ac01cd1e.png)

### mutation update
- add update methods in `schema.js`
  - ```javascript
        updateMovie: {
            type: MovieType,
            args: {
                id: {type: GraphQLNonNull(GraphQLID)}, //.. how to add id automaticaly?
                name: {type: GraphQLString},
                genre: {type: GraphQLString},
                directorId: {type: GraphQLID}
            },
            resolve(parent, args) {
                let updateMovie = {}
                args.name && (updateMovie.name = args.name)
                args.genre && (updateMovie.genre = args.genre)
                args.directorId && (updateMovie.directorId = args.directorId)
                return Movie.findByIdAndUpdate(args.id, updateMovie, {new: true}) // new: true mean enable get the result after mutated
            }
        },
        updateDirector: {
            type: DirectorType,
            args: {
                id: {type: GraphQLNonNull(GraphQLID)}, //.. how to add id automaticaly?
                name: {type: GraphQLString},
                age: {type: GraphQLString}
            },
            resolve(parent, args) {
                let updateDirector = {}
                args.name && (updateDirector.name = args.name)
                args.age && (updateDirector.age = args.age)
                return Director.findByIdAndUpdate(args.id, updateDirector, {new: true}) // new: true mean enable get the result after mutated
            }
        }

- following able to be retrieved
![スクリーンショット 2022-12-09 13.02.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/0cfedcc8-69e8-8c05-421d-97efdce15e4e.png)
 
### mutation delete 
- add delete methods in `schema.js`
  - ``` javascript 
            deleteMovie: {
            type: MovieType,
            args: {
                id: {type: GraphQLNonNull(GraphQLID)},
            },
            resolve(parent, args) {
                return Movie.findByIdAndRemove(args.id)
            }
        },
        deleteDirector: {
            type: DirectorType,
            args: {
                id: {type: GraphQLNonNull(GraphQLID)},
            },
            resolve(parent, args) {
                return Director.findByIdAndRemove(args.id)
            }
        },

- removed result retrieved like following
![スクリーンショット 2022-12-09 14.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/21f8f5af-e366-fcd1-0393-cf3dff8c39c8.png)

## frontend

### create a react application
- in directory of server (not in sever) create react application: `yarn create react-app client`
  - might failed if node version is not supported by yarn then [here you go](https://qiita.com/arimuuuura/items/95e489a835d7bf31a4a5) 

- in `./client` insert `yarn start` and get the page following:
![スクリーンショット 2022-12-09 14.59.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/ab5a8499-ea1b-2e24-3603-f6554a619b4a.png)

### install reactstrap, [bootstrap](https://classic.yarnpkg.com/en/package/bootstrap#readme) and setup a simple page
- in `./client` 
  - insert `yarn add reactstrap react react-dom`
  - insert `npm install bootstrap`
  - insert `yarn add boot strap`
- do ervery ref to this [commit](https://github.com/devbohan/movie_page_client/commit/0f1400149c5c4d871b5237df569237f813fcd104) and you can get a page like following:
![スクリーンショット 2022-12-09 15.39.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/993ca38a-fcf3-f794-0ced-cba55d1bcc26.png)

- add sth in `MovieList.js` then you get this
![スクリーンショット 2022-12-09 15.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/ea122363-84c2-19e2-8fd5-ad0925fb8a5b.png)

### intall apollo & add movie list $ add side navi
- in `./client`
  - insert `npm install @apollo/client graphql`
  - insert `yarn add @apollo/client graphql`
  - according to [documents of apollo](https://www.apollographql.com/docs/react/get-started) add everthing to app.js [(fyi commit)](https://github.com/devbohan/movie_page_client/commit/017147a50e694b9314a1750fab145c731b49aa9e) 
  - if cors error then check [this commit](https://github.com/devbohan/movie_page/commit/4673d68a1453727b7206c974a10fc9e58ffa2ae9)
  - add movie list and side navi: [fyi commit](https://github.com/devbohan/movie_page_client/commit/5b1232d5b03cac39e604df120616900e3c659daf#diff-dd0cba7676b54c7fedec1355ee583af7ccba0ad18339db3ada0ce93947045a05)
  - this page by far
  ![スクリーンショット 2022-12-09 17.38.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/d0a0fcf8-7930-3dcc-c19a-c4148f33152d.png)

### intall react-hook-form: `npm install react-hook-form`
 - and folow the step [here](https://github.com/react-hook-form/react-hook-form/blob/master/README.md)
 ![スクリーンショット 2022-12-09 18.39.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/6ef7e250-a9a9-3c5b-7d37-1097c00f7a33.png)

### add mutations
- [fyi](https://github.com/devbohan/movie_page_client/commit/5e463ab50c6fe6519c37926155fe85795153b9e4)

### refetch query (means page contents will automaticaly update after query result updated)
- [fyi](https://github.com/devbohan/movie_page_client/commit/c0c5b411542a2dbaa74590db9991f01df30da0b1)

### delete from table
-[fyi](https://github.com/devbohan/movie_page_client/commit/c2d7f5e454455b2bb8b0933292781c6785ea992b)

## your page finally like this
![スクリーンショット 2022-12-09 19.36.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3019265/d170ff12-29ae-c127-fc48-f2afe154c87a.png)