Record a process from boring learning GraphQL to discovering project Gitter, imitating project Github-Trending-API, and finally making my own learning project Github-Trending-GraphQL.

This is what I thought at the beginning, and this is what I ended up doing, replaying the whole learning process.

Ready to learn

What is GraphQL? In the previous project, we mainly used GraphQL to merge existing interface data. In this case, we mainly dealt with existing REST-related service interfaces, and we made an intermediate data processing layer. Recently I was thinking about team service project development, because there are a lot of routes defined in development based on REST interfaces. I decided to use GraphQL in my project (actually just to install B, I used the latest XX technology in my project) in order to be lazy about defining routes, and some other considerations in between.

Several concepts

The Graphql model has three types of operations.

Query

Query data (R).

# standard
query {
    field
}

# shorthand
{
    fields
}
Copy the code

Mutation

Add, update, or delete data (CUD).

mutation {
    do( arguments ) {
        fields
    }
}
Copy the code

Objects

Represents accessible resources.

# Repository contains the contents of the project
    # Implements
    # Connections
    # Fields

Copy the code

Implements

Learn not to move, omit….

Be inspired by other projects

In the boring document learning process, I saw a blog recommending gitter, a small program of my own. Out of habit, I caught the request of the small program and found that the trend ranking was obtained by github-trending-api.now.sh. Then I found the corresponding project github-trending API. I have also looked at the GitHub GraphQL API a few times before this, but it tends to be time and other factors (lazy) that have not been used to implement into actual projects. It is found that Trending API is not provided by the official. The Trending API in V3 is added to github-Trending API project. Can I provide a GraphQL API by imitating this project? Start a new project with two goals:

  • Learning GraphQL
  • Do an open source project

Initialize the project

The simplest way to do this is to provide a GraphQL Server and request Github-trending api.now.sh directly. For teams whose projects already have microservices, an intermediate service layer can be used to consolidate data requests, nested data queries, and so on. GraphQL Server uses Apollo Server to create a Node service, define a Schema, and add resolver parsing functions.

How to define Type

At the beginning, the foundation of learning is just for use. GitHub Trending mainly provides two aspects, one is Repository and the other is Developer.

type Repository {
    author: String
    contributors: [Contributor]
    currentPeriodStars: Int
    description: String
    forks: Int
    language: Lang
    name: String
    stars: Int
    url: String
}
Copy the code

In addition to the basic Scalar Type, there are two additional Contributors and languages in the Repository, one for array data and one for objects, which will be obtained by further subdividing the types

type Contributor {
    avatar: String
    url: String
    username: String
}
type Lang {
    name: String
    color: String
}
Copy the code

Developer also gets a data structure after analyzing the data

type Developer {
    avatar: String
    name: String
    repository: RepositoryMini
    username: String
    url: String
}
Copy the code

The project repository is an object data, which can be subdivided into one

type RepositoryMini {
    description: String
    name: String
    url: String
}
Copy the code

How to define Query

Once you have defined the base data types Repository and Developer, you need to provide a unified Query, resulting in a new root data type

type Query {
    repositories: [Repository],
    developers: [Developer]
}
Copy the code

In the actual trend query process, we will add parameters, one parameter is language, and the other parameter is since, where since can only take daily, weekly, monthly, but can also take other values, but the default is still daily. After modification, the result is as follows

type Query {
    repositories(language: String, since: String): [Repository],
    developers(language: String, since: String): [Developer]
}
Copy the code

If you want to verify that since can only take all of three values, you need to add an enumeration type

type Query {
    repositories(language: String, since: Since): [Repository],
    developers(language: String, since: Since): [Developer]
}

enum Since {
    daily
    weekly
    monthly
}
Copy the code

How to optimize Query

There may also be a problem in the practical process of the above writing method. If the data of Repository and Developer are queried at the same time, the parameters need to be passed repeatedly according to the suitability of the filtering condition query, and the two types actually belong to type Trending. Add a new type

type Trending {
    repositories: [Repository]
    developers: [Developer]
}
Copy the code

The root Query Query can also be modified

type Query {
    trending(language: String, since: String): Trending
}
Copy the code

The client initiates a query request

With our final data structure defined, we can initiate such a query

{ Trending(language: "javascript", since: "daily") { repositories { name author description language { name color } forks stars contributors { avatar url username  } currentPeriodStars url } developers { avatar name repository { url name description } username url } } }Copy the code

If you define language and since in variables, it will look like this

# query query getTrending($language: String, $since: String) $since) { repositories { name } } } # variables { "language": "javascript", "since": "daily" }Copy the code

Query and variables are placed in the body as request Payload, where the custom operation method operationName is set to getTrending

fetch("https://trending.now.sh", {
    "credentials": "omit"."headers": {
        "accept": "* / *"."accept-language": "zh-CN,zh; Q = 0.9, en. Q = 0.8"."content-type": "application/json"
    },
    "referrer": "http://localhost:4000/"."referrerPolicy": "no-referrer-when-downgrade"."body": "{\"operationName\":\"getTrending\",\"variables\":{\"language\":\"javascript\",\"since\":\"daily\"},\"query\":\"query getTrending($language: String, $since: String) {\\n trending(language: $language, since: $since) {\\n repositories {\\n name\\n }\\n }\\n}\\n\"}"."method": "POST"."mode": "cors"
});
Copy the code

The server parses the request

We’re using Apollo Server, and when the service receives the request, it parses the body parameter. The business logic will be processed by the nested calls to resolver, entering Trending first and then executing repository and developer at the same time.

Based on the data structure defined by the root query, the tending parser receives two parameters, language and since. Repository and developer also use these two parameters.

// resolver
{
    Query: {
        trending(parent, args, context, info) {
            // args => { language: '', since: '' }
            The parent parameter is the result that can be received by the upper parser, and we can pass the data received in Trending to the child parser
            return { language, since }
        }
    },
    Trending: {
        repositories(parent, args, context, info) {
            // parent => { language: '', since: '' }
        },
        developer(parent, args, context, info) {
            // parent => { language: '', since: '' }}}},Copy the code

What do I need to do in the parser?

According to the data analyzed above by the parser, we can directly request the github-Trending -api.now.sh data interface to get the data. Here, we take learning as the purpose. After capturing THE HTML page, analyze the page structure to get the data you need.

export async function fetchRepository() {
    HTML / / analysis
}

export async function fetchDeveloper() {
    HTML / / analysis
}

export async function fetchLanguage() {
    HTML / / analysis
}
Copy the code

The specific HTML analysis process is not analyzed, but cheerio is used, which is similar to JQuery. There are some caveats

  • The request process is slow. Github Trending’s page is requested again for each request, and then the page has to be analyzed, which is actually a time-consuming process. If we cache the parsed data in the request according to the query criteria, the next request will fetch the data directly from the cache, which is much faster. (Warehouse and developer trends are updated every once in a while, we cache for an hour; Language changes are small, we cache the time of the day)

  • Language package cache. Request warehouse and developer suitable, detect whether the language cache exists, does not exist first cache, subsequent request warehouse and developer or direct request language package will directly hit the cache

We added a new method to refresh the cache, which can update the cache according to the specified key name, or clean the whole cache without passing parameters.

How do I clear the cache?

The GraphQL root processing method has a Mutation in addition to Query. Query (R) and CUD (CUD); The new refresh operation we are going to add is to remove the cache, mainly for the warehouse and developer caches. After the refresh, we only care about success or failure, so we can return a Boolean value here

type Mutation {
    refresh(key: String, language: String, since: String): Boolean
}
Copy the code

The corresponding processing method needs to be added to the parser

{
    Mutation: {
        refresh(parent, args, context, info) {
            // do something}}}Copy the code

Review the

From the beginning of the demand analysis, we need to develop a Github Trending GraphQL API. We made use of the basic knowledge of GraphQL we had learned before and were familiar with GraphQL tool Apollo Server, which made it easy to develop the corresponding API. Later, in order to optimize the request, we added cache policy and clear cache policy.

At this point our project github-Trending graphQL is ready to be submitted to the Github repository. There’s a lot more to do for a perfect open source project, but a sample of GraphQL is almost ready to use.

It’s boring to look at the code in the first place, so we need to deploy a Demo to make it easier to understand. How to easily deploy Demo again becomes a problem?

How to deploy examples

The deployment of trending. Now. sh should be guessed by the domain name as now’s non-service deployment. The usage documentation is quite clear. But there are a few details to pay attention to

For project deployment, we first need to create a now.json file in the project root directory

{
    "version": 2."alias": ["trending.now.sh"]."builds": [{
        "src": "src/server.js"."use": "@now/node-server"}]."routes": [{
        "src": "/"."dest": "/src/server.js"}}]Copy the code

Alias the alias of now.sh will not take effect directly. Server.js is a file that needs to be executed so we set version to 2 and we can add builds to the configuration with @now/node for regular JS customisable files where server.js is used to enable a Node service. So use @now/ Node-server.

After the successful deployment, we obtained a project access address of Github -trending- GraphQL -[hash].now.sh. If we want to access the actual function of the project, We also need to click on it twice to get the project function address github-trending- GraphQL -[hash].now.sh/ SRC /server.js. If we want to directly use the domain name direct access function, we need to add the above configuration route here.

Each deployment will generate a new image, and a new secondary domain name will be obtained. If we want to share the image, we can set an alias for our project, which is trending.now.sh.

What we need to do every time we deploynow && now alias, now Alias needs to specify the project domain name obtained by the current deployment, and the alias to be set.(now) trending.now.shTo add package.json, you only need to do it once per deploymentNPM run now `.

Results show

github trending graphql api

online demo

Related projects

github trending rest api