Advanced
Web Applications


GraphQL

Get ready

This is an OPTIONAL practical.
Your are not required not finish, or submit, any of the assignments.

GraphQL Endpoint

We can use GraphQL endpoint directly as an HTTP endpoint. Alternatively we can use tools providing user-interface like GraphiQL. NKOD GraphQL API is a reasonable example.

GraphQL Schema

  • Basic example
  • Types
  • Required (!)

type Query {
  users: [User]
}

type User {
  id: ID!
  name: String
}
    

GraphQL Schema

  • Arguments

type Query {
  users: [User]
  user(identifier: String): User
  groups: [Group]
}

type User {
  id: ID
  name: String
}

type Group {
  id: ID
  users: [User]
  homepage: String
}
  

Assignment: GraphQL (1/5)

./practical-11/graphql

Employ express, graphql, graphql-http, and ruru or GraphiQL (GraphQL clients), libraries to implement GraphQL endpoint. Follow the steps on this and following slides to complete the assignment.

Start from a simple GraphQL Server with Express. Just install the libraries, copy (and read) the source code into an file and start the file using node command.

Assignment: GraphQL (2/5)

Next use GraphQLSchema to implement schema (bellow), create your own test data. See GraphQL.js page for example of using GraphQLSchema. Notice the resolve function.

Mind CommonJS (require) and ES Modules (import ... from "") style of imports. You can use .mjs extension for ESModule import and .cjs extension for CommonJS type of imports.


type Query {
  users: [User]
}

type User {
  id: ID!
  name: String
}
    

Note: Use GraphQLList to represent a list of types.

Assignment: GraphQL (3/5)

Make sure you understand all the concepts before you continue.

Update the previous schema, add `user`. See second example at Constructing Types for information on how to work with arguments.


type Query {
  users: [User]
  user(identifier: String): User
}

type User {
  id: ID
  name: String
}
  

Assignment: GraphQL (4/5)

Update the previous schema, add `groups`.

You may notice that we share the user object in here. It would be great (performance) not to construct the whole group object but to re-use some functionality. A solution is to declare and reuse multiple resolve functions. In fact, you can declare a resolve function in every object with type. The first argument of a resolve function is source object, i.e. parent.

Add a resolve function to Group.users and use the parent argument to get list of all users.


type Query {
  users: [User]
  user(identifier: String): User
  groups: [Group]
}

type User {
  id: ID
  name: String
}

type Group {
  id: ID
  users: [User]
  homepage: String
}
  

Assignment: GraphQL (5/5)

We should not put business logic or model into the resolve functions (remember MVC)? Extract the functionality into another layer.

Introduce a service/object UserService, with methods (TypeScript-like notation):

  • getUsers(): Promise<{ id: string, name: string }> - return all users as objects
  • getUsersIdentifiers() : Promise<string[]> - return identifiers of all users
  • getUser(identifier:string): Promise< { id: string, name: string}> - return data for user with given identifier.

In a similar manner introduce a user GroupService:

  • getGroups() : Promise<{id:string, homepage: string, users: string[]}> - return all groups as objects. Users are represented using identifiers.

This is the end of the first assignment.

Assignment: GraphQL HTTP (1/2)

./practical-11/http

Expand implementation from previous assignment by fetching the data from an HTTP endpoints described by following specifications.

As the servers are not available to the public we need to mock the servers as well.

Assignment: GraphQL HTTP (2/2)

Instead of implementing the servers from cratch we can:

  • Utilize AI to generate the servers.
  • Look for a tool to generate a mock server.

Once you have the servers ready test your implementation with them.

Assignment: GraphQL MySQL (1/1)

./practical-11/mysql

Employ mysql (documentation) package to fetch data from a MySQL server with following schema.


CREATE TABLE `users` (
  `id` int NOT NULL,
  `name` varchar(256) NOT NULL
);

CREATE TABLE `groups` (
  `id` int NOT NULL,
  `homepage` varchar(256) NOT NULL
);

CREATE TABLE `membership` (
  `user_id` int NOT NULL,
  `group_id` int NOT NULL
);
    

Code quality (1/2)

Make sure your code is of reasonable quality and follow best practices!

Code quality (2/2)

Your solutions should

  • load configuration from .env file,
  • have a license,
  • have a README.md,
  • have a Dockerfile or even docker-compose.yaml file,
  • use ESLint.

Additional resources

Questions, ideas, or any other feedback?

Please feel free to use the anonymous feedback form.