Creating a movie website with GraphQL and React - part one
Tags: graphql, node, react, javascript
Published: 2018-04-03 13:10:07

---
title: Creating a movie website with GraphQL and React - part one
published: true
description: 
tags: graphql, node, react, javascript
series: Creating a movie website with GraphQL and React
---

As I hope I explained on my [previous tutorial](https://dev.to/aurelkurtula/exploring-graphql-api-serve-3abf) GraphQL is another way of creating an API. Using GraphQL instead of a JSON or XML API you are giving the consumers of your API the ability to query the data they require instead of giving them everything regardless of their requirements. 

In the following set of tutorials I am going to go through how we can create a GraphQL API by gathering data from existing JSON APIs and then how we can consume the newly GraphQL API using react.

## About the JSON API 

We'll use [themoviedb](https://www.themoviedb.org/documentation/api) "is available for everyone to use". It gives us access to their movie collection. So instead of creating our own movie database we'll access theirs through their JSON API. All you need is to create an account with them so that you get your API key - something you'll need in order to work through this series of tutorials. 

In order to create our application, these are all the requests we'll need to make (`https://api.themoviedb.org/3/movie/` being the root for all of them):

```
/${id}/videos?api_key=${YOUR_API}&language=en-US
/now_playing?api_key=${YOUR_API}&language=en-US&page=1
/${id}?api_key=${YOUR_API}&language=en-US&page=1
/${id}/reviews?api_key=${YOUR_API}&language=en-US&page=1
/${id}/credits?api_key=${YOUR_API}&language=en-US&page=1
```

As you can imagine we can ignore GraphQL and go straight into creating the react app and have it make the five get requests when needed. However, we are going to use GraphQL to handle the above. Hence the react developers (even though we'll do both) can make one request to the GraphQL server and pick whatever is needed.

Again, if you want to follow along go to [themoviedb](https://www.themoviedb.org/documentation/api) and create the API key you'll need soon. Further, you're welcome to use my [minimal-node-environment](https://github.com/aurelkurtula/minimal-node-environment) setup, it doesn't have any code related to what we need to learn here, it just sets the environment. You can read [how that environment is created here](https://dev.to/aurelkurtula/setting-up-a-minimal-node-environment-with-webpack-and-babel--1j04).

### Step One: Linking GraphQL to Express

The following is covered in the [first graphql tutorial](https://dev.to/aurelkurtula/exploring-graphql-api-serve-3abf)

We need to get express to use GraphQL at a specific path. The full code at `./server.js` should look like this

```javascript
import express from 'express'; 
import dotenv from 'dotenv';
//...
import expressGraphQL from 'express-graphql';
import schema from './schema/schema'; // our schema file
dotenv.config()
const app = express();
app.use(webpackMiddleware(webpack(webpackConfig)));

app.use('/graphql', expressGraphQL({
  schema,
  graphiql: true
}))

app.listen(4000, () => {
  console.log('Listening');
});

```

From this point on, everything we'll code is going to be contained within `./schema/schema.js` so create that file.

Finally for this first step create a `.env` file in the root and add your movie API like so 

```
API=api-string-without-quotes
```

All that's left is to install the packages we required above 

```
npm i -S express express-graphql graphql dotenv
```

### Step Two: Create API endpoint for new movies 

Inside `./schema/schema.js` we are able to get data from other sources, be they databases or external APIs and use them to build our own GraphQL API. Here we'll get data from five different JSON API endpoints and architect out GraphQL in a way where other would consume it as if the data comes from one call, or rather without caring where the original data comes from. 

We'll start by pulling data from **themoviedb** concerning the movies that are currently in the cinema

```
https://api.themoviedb.org/3/movie/now_playing?api_key=${YOUR_API}&language=en-US&page=1
```

If you provide your own API and navigate to the above URL, you'll see each movie would be formatted like so:

```javascript
{
    results: [
            {
            vote_count: 3742,
            id: 284054,
            video: false,
            vote_average: 7.3,
            title: "Black Panther",
            popularity: 246.001551,
            poster_path: "/uxzzxijgPIY7slzFvMotPv8wjKA.jpg",
            original_language: "en",
            original_title: "Black Panther",
            genre_ids: [28, 12, 14, 878],
            backdrop_path: "/b6ZJZHUdMEFECvGiDpJjlfUWela.jpg",
            adult: false,
            overview: "King T'Challa returns ....",
            release_date: "2018-02-13"
        }
    ]
}
``` 

As the architects of our GraphQL API we decide which of that information is useful and which is not. We're also able to rename any of the key names. 

Lets work on `./schema/schema.js`

The above JSON result helps us get into GraphQL reasoning. 

1. We know that each movie is an object
2. We'll need an ID and that's an integer 
3. We'll also take the `poster_path` and `title`, and those are both strings
4. We aren't interested in anything else

Let's tell GraphQL that that's what a newly released movie should be.

```javascript
import {
    GraphQLObjectType,
    GraphQLString,
    GraphQLInt,
    GraphQLSchema, // we'll use them for the RootQuery
    GraphQLList // we'll use them for the RootQuery
} from 'graphql';

const NewMoviesType = new GraphQLObjectType({
    name: 'NewMovies',
    fields:{
        id: {type: GraphQLInt},
        poster_path: {type: GraphQLString},
        title: {type: GraphQLString},

    }
})
```

As you can imagine that's just one half of the complete code. That's just saying we need an Object with an integer and two strings.

Finally we need to feed it the data inside the Root Query. As I said in the tutorial where I [introduced GraphQL](https://dev.to/aurelkurtula/exploring-graphql-api-serve-3abf), a "root query is the entry point to GraphQL, it is the thing that fires first and in turn exposes other resources".

```javascript
import axios from 'axios';
...
const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields:{
        newMovies: {
            type: new GraphQLList(NewMoviesType),
            resolve() {
              return axios.get(`https://api.themoviedb.org/3/movie/now_playing?api_key=${process.env.API}&language=en-US&page=1`)
              .then(res => {
                  const movies = res.data.results;
                  movies.map(movie=> movie.poster_path = "https://image.tmdb.org/t/p/w500"+movie.poster_path
                 )
                  return movies
              })
            }
          }          
    }
})
```

As we'll see, inside the `RootQuery` we'll have everything that the GraphQL API is going to make accessible. So far the Root Query has one field called `newMovies`. That field is going to be an object, the blueprint of which we already created. In reality, it's not going to be one object, but an array of movie objects, hence `GraphQLList(NewMoviesType)`

Finally the resolve method is what feeds data to our object. 

We are using axios to fetch the data from the external API (equally, if the data was stored in a database it would be retrieved within the resolve method). 

Because the movie API we are fetching from is json, it works so well that simply returning the results would have worked fine: 

```javascript
resolve() {
    return axios.get(`https://api.themoviedb.org/3/movie/now_playing?api_key=${process.env.API}&language=en-US&page=1`)
    .then(res => res.data.results )
}
```

But of course we needed to modify the value of the `poster_path` since the relative path would not have been of any use. 

Finally we export the root query and run the project (`npm start`)

```javascript
export default new GraphQLSchema({
    query: RootQuery
})
```

If you've been following along, navigating to `http://localhost:4000/graphql` and adding the query below, you should have gotten all the latest movies. 

```json
{
	newMovies{
    id
    title
    poster_path
  }
}
```



One got, four to come!

### Step Three: Get information for a single movie

Think about it, the above query gives us all the new movies. Next, one might want more information for any one of those movies. 

The existing API endpoint for that is 

```
https://api.themoviedb.org/3/movie/${id}?api_key=${YOUR_API}&language=en-US
```

The result is one object with all the information for a specific movie. Let's create the GraphQL Object as we did before.

```Javascript
const MovieInfoType = new GraphQLObjectType({
    name: 'MovieInfo',
    fields: {
        id: {type: GraphQLInit},
        overview: {type: GraphQLString},
        title: {type: GraphQLString},
        poster_path: {type: GraphQLString},
        genres: {type: GraphQLString},
        release_date: {type: GraphQLString},
        vote_average: {type: GraphQLString},
        production_companies: {type: GraphQLString},
        vote_average: {type: GraphQLString},
        runtime: {type: GraphQLString}
    }
})
```

As you can see, once you get your head around it, it's all repetitive. 

We then need to add it inside the Root Query.

```javascript
const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields:{
        topMovies: {...},
        movieInfo: {
            type: MovieInfoType,
            args: {id: {type: GraphQLString}},
            resolve(parentValue, args) {
                return axios.get(`https://api.themoviedb.org/3/movie/${args.id}?api_key=${process.env.API}&language=en-US&page=1`)
                .then(res => {
                    const movie = res.data;
                    movie.genres = movie.genres.map(g => g.name).join(', ')
                    movie.production_companies = movie.production_companies.map(c => c.name).join(', ')
                    movie.runtime+= ' min.'
                    return movie
                })
            }
        }
    }
})
```

This is a lot more complex then the `topMovies` query but not hard to understand. 

First we specify that for a query to `movieInfo` an `id` should be provided as a string. That's of course because the original API requires it. Note how we don't make use of `parentValue` but the required `id` get's added into the `args` object. 

In the original API, the `genres` and `production_companies` and represented as arrays: 

```json
genres: [
    {
        id: 18,
        name: "Drama"
    },
    {
        id: 10751,
        name: "Family"
    }
],
production_companies: [
    {
        id: 9195,
        logo_path: "/ou5BUbtulr6tIt699q6xJiEQTR9.png",
        name: "Touchstone Pictures",
        origin_country: ""
    }
],
```

The GraphQL Object which we created requires both the genres and  production companies to be string (not that we couldn't specify them as arrays). Hence joining only their names.

Similarly, the movie length is given in minutes so I decided to append `min.` to the original string - and argument can be made that that's not a good idea. When we create the interface we might want to convert those minutes into human readable time and so by adding the `min.` we are giving react developers more work, but heck this is fun!

### Step Four: Get Movie trailers 

I'll tell you, this [themoviedb.org API](https://www.themoviedb.org/documentation/api) is fantastic. Amongst other things that we'll not cover here at all, it also gives us movie trailers. They can be accessed at 

```
https://api.themoviedb.org/3/movie/${id}/videos?api_key=${YOUR_API}&language=en-US
```

The result being a reference to an array of youtube videos 

```json
{
    id: 43535,
    results: [
        {
            id: "533ec6a1c3a3685448004f82",
            iso_639_1: "en",
            iso_3166_1: "US",
            key: "e7bD5BNqfwY",
            name: "A Simple Twist of Fate - Hallmark Channel",
            site: "YouTube",
            size: 360,
            type: "Trailer"
        }
    ]
}
```

I have to say when I saw that I was slightly over excited! Anyway, let's create the video GraphQL object. We know that the only useful information there is the `key`, but simply to make developing a react App easy, we know that the `id` is handy, hence:

```javascript
const VideoType = new GraphQLObjectType({
    name: 'Video',
    fields:{
        id: {type: GraphQLString},
        key: {type: GraphQLString}

    }
})
```

Now, if you haven't seen the brightness of GraphQL already, be prepared to be blinded because this is where I think GraphQL shines even brighter!

If we follow the pattern we've used so far, `movieVideo` would be another query in the root. But no, in reality, we know, or we're going to decide, that developers that will consume our GraphQL API are going to want the trailers when they request a single movie. 

So we want the trailers to be accessible from within the `movieInfo` query, hence the `VideoType` array will be part of the `MovieInfoType` object!

```javascript
const MovieInfoType = new GraphQLObjectType({
    name: 'MovieInfo',
    fields: {
        id: {type: GraphQLString},
        ...
         videos: {
             type: new GraphQLList(VideoType),
             args: {id: { type: GraphQLString } },
             resolve(parentValue, args) {
                return axios.get(`https://api.themoviedb.org/3/movie/${parentValue.id}/videos?api_key=${process.env.API}&language=en-US`)
                .then(res => res.data.results)
             }
         }
    }
})
```

Note, we do nothing to the `RootQuery`, we instead add a `videos` field inside the `MovieInfoType` and we resolve it there. The movie `id` required to fetch the specific videos is retrieved from the siblings of `video` hence `fields.id` is referred to as `parentValue.id`.

If we navigate to `http://localhost:4000/graphq` and add the following query:

```json
{
    movieInfo(id:"284054"){
        title 
        videos{
            id
            key
        }
    }
}
```

You get a bunch of Black Panther videos.

Now, that is cool! (both GraphQL and the movie)

## Step five: Credits and reviews

Finally let's add movie reviews and movie cast into our GraphQL API.

```javascript
const MovieCreditsType = new GraphQLObjectType({
    name: 'MovieCredits',
    fields:{
        id: {type: GraphQLString},
        character: {type: GraphQLString},
        name: {type: GraphQLString},
        profile_path: {type: GraphQLString},
        order: {type: GraphQLString}
    }
})
const MovieReviewsType = new GraphQLObjectType({
    name: 'MovieReviews',
    fields:{
        id: {type: GraphQLString},
        content: {type: GraphQLString},
        author: {type: GraphQLString},
    }
})
```

Similar to the video trailers, the movie credits and movie reviews are only meaningful in the context of the movie they are related to. So we'll add them inside the `MovieInfoType` Object.

```javascript
const MovieInfoType = new GraphQLObjectType({
    name: 'MovieInfo',
    fields: {
        id: {type: GraphQLString},
        ...
         videos: { ... },
         movieReviews: {
            type: new GraphQLList(MovieReviewsType),
            args: {id: {type: GraphQLString}},
            resolve(parentValue, args) {
              return axios.get(`https://api.themoviedb.org/3/movie/${parentValue.id}/reviews?api_key=${process.env.API}&language=en-US&page=1`)
              .then(res =>  res.data.results)
            }
          },
          movieCredits: {
            type: new GraphQLList(MovieCreditsType),
            args: {id: {type: GraphQLString}},
            resolve(parentValue, args) {
              return axios.get(`https://api.themoviedb.org/3/movie/${parentValue.id}/credits?api_key=${process.env.API}&language=en-US&page=1`)
              .then(res =>  res.data.cast.filter(cast => cast.profile_path ) )
            }
          }
    }
})
```

## Final Words 

As you have gathered, just because we used, say the `VideoType` inside the `MovieInfoType` object, we can easily duplicate the code and add it as the child `RootQuery` `fields`

```javascript
const MovieInfoType = new GraphQLObjectType({
    name: 'MovieInfo',
    fields: {
        id: {type: GraphQLString},
        ...
         videos: { ... },
         ...
    }
})
const RootQuery = new GraphQLObjectType({
    name: 'RootQueryType',
    fields:{
        videos: {
            type: new GraphQLList(VideoType),
            args: {id: { type: GraphQLString } },
            resolve(parentValue, args) {
               return axios.get(`https://api.themoviedb.org/3/movie/${args.id}/videos?api_key=${process.env.API}&language=en-US`)
               .then(res => res.data.results)
            }
        },
        newMovies: { ... } ,
        movieInfo: { ... }         
    }
})
```

Hence, videos can then be queried in two ways 

```json
{
	videos(id:"284054") {
	  id
    key
	}
}
```

Or through a movie

```json
{
   movieInfo(id:"284054"){
    videos {
      id
      key
    }
  }
}
```

That's all there's is. That makes up our GraphQL API. The full code is on [github](https://github.com/aurelkurtula/IMDBgraphql/tree/Graphql-api), the branch `Graphql-api` holds the code explored here, where as the master branch is subject to change based on the next tutorial - where well consume this new API in a react app.

**You can play with a live demo [here](https://calm-bayou-22920.herokuapp.com/graphql)**