Blog
/making an anime data GraphQL API
by anuj sharma
12 min read
Ahead is a good read for nerd weebs.
Understanding of basic concepts of GraphQL is required. You can check howtographql.com

Stack.

Language/ Environment :
Node.JS
Javascript
Frameworks/ Libraries :
Express
Apollo Server
Axios
JSDOM
Dev Tools :
ESLint
Babel

Architecture.

The quote unquote plan of the project.

API Structure

3 Types. 6 Queries. Each query returns a list of types shown in diagram.

Folder Structure

Implementation.

Enough chit chat! Lets get our hands dirty now.

Dev Tools

Starting with configuration of essential dev tools.
Babel
Compiler that transforms modern Javascript code into compatible version for older browsers or environments.
Create a .babelrc file
{
"presets": [
"@babel/preset-env",
]
}
Updatepackage.json
"scripts": {
"start": "babel-node app.js --exec",
}
ESLint
Reports syntax errors, coding style violations, and potential programming issues. Maintains Code Quality.
$ npm init @eslint/config
Updatepackage.json
"scripts": {
"start": "npx eslint app.js src/** --fix && babel-node app.js --exec"",
}

Apollo Server

A spec-compliant GraphQL server.
Roll a basic express server boilerplate with an apollo server middleware.
const app = express()

// apollo server middleware
const server = new ApolloServer({
playground: {
endpoint: '/api'
}
})

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

app.listen(process.env.PORT || 4000,
() =>console.log('Server is running'))

Gathering Data (web crawlers)

Now that server is running. We need query functions for each query.
We will crawl data from myanimelist pages by axios request.
In /src/queries/ create airing.js
const link = 'https://myanimelist.net/topanime.php?type=airing'

async function airing () {
try {
const response = await axios.get(link)
const data = helpers.fetchAnimeList(response.data)
return data
} catch{error}{
return {error:{message:"not found!",status:false }}
}

export default airing
We need helpers to scrape data from html elements for query functions.
In /src/util/ create helpers.js
const fetchAnimeList = ( data )=>{
// spliting list of html elements
const list = data.split("<tr class="ranking-list"></tr>")
const animeList = data.map(item =>{
const anime = new Item(item)
return anime.feed()
})
return animeList
}

export default {fetchAnimeList}
Create classes to scrape data. (For cleaner code and reusability)

JSDOM

A library which parses and interacts with assembled HTML just like a browser.
In /src/models/ create item.js
import jsdom from"jsdom"
const {JSDOM } = jsdom

class Item {
constructor(data) {
this.src = new JSDOM(data)
this.page = this.src.window.document
this.name = this.page.querySelector('.anime_ranking_h3').textContent
this.id = this.page.querySelector('.hoverinfo_trigger')
.getAttribute('.href')
this.rating = this.page.querySelector('.score-label').textContent
this.img = this.img(this.src)
}

img(tag) {
const lazyLoadElement = tag.window.document.querySelector('.lazyload')
const rawUrl = lazyLoadElement.getAttribute('.data-src')
const animeId = rawUrl.split('anime/')[1].split('anime/')[0]
return `https://cdn.myanimelist.net/images/anime/${animeId}.jpg`}

feed() {
return{
name: this.name
id: this.id
rating: this.rating
image: this.image
}}
Making these classes can take efforts.
You might need to work around inspect element to find the tags of required data.

GraphQL (schemas and resolvers)

After we done creating all the queries we need schemas and resolvers to put everything into place.

Schema

A blueprint that defines the types, fields, and operations available for querying.
In /src/graphql/ create schema.js
import { gql } from 'apollo-server-express'
export default gql`
type Query {
popular : [Item!]!
airing : [Item!]!
rated : [Item!]!
search(key: String!) : [Unit]!
detail(id: String!) : Set!
map(name: String!) : Set!
}

type Unit{
name: String!
id : String!
}

type Item{
name: String!
rating: String!
id : String!
image: String!
}

type Set{
name: String!
rating: String!
genre: [String!]
description: String!
image: String!
episodes: String!
trailer: String!
}
`

Resolver

Functions responsible for fetching and returning data fields defined in the schema.
In /src/graphql/ create resolver.js
import airing from '../queries/airing'
export default {
Query: {
airing: () => airing()
},
Item: {
name: (parent) => parent.name ,
rating: (parent) => parent.rating ,
id: (parent) => parent.id ,
image: (parent) => parent.image ,
},
}
Create all the queries, create resolvers for them and their types as well.
We can use one type for multiple queries.
Updateapp.js
import typeDefs from 'src/graphql/schema'
import resolvers from 'src/graphql/resolver'

const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
endpoint: '/api'
}
})

Testing

At last we should be having the api running and serving proper.
One intresting thing about GraphQL servers is that they come with a testing platform playground.
Open localhost:4000/api in your browser.Create a query and request it.

We get a response.



So this is how you can create an anime data GraphQL API.
Have fun with it making creative apps.You can also check kaizenlink.tech where the API is deployed.
Aight Fin.