draschke / ui5-sample-apollo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ui5-sample-apollo

UI5 Tooling Ecosystem

Overview

This project was part of the UI5Con on Air 2020 and was presented as part of a lightning talk. The recording can be found on YouTube

This project show-cases the usage of Apollo GraphQL for UI5 applications. The first step is the integration of OSS libraries directly from npm into UI5 by using the new CLI tooling. With Rollup the Apollo GraphQL library can be transpiled into a UI5 AMD-like module. The other part of the project is the integration of Apollo GraphQL into the UI5 programming model.

The content of the repository is structured like that:

packages
├── ui5-apollo-lib     // the UI5 library containing the Apollo client and a base controller for UI5 MVC applications
├── ui5-apollo-server  // the Apollo GraphQL server
└── ui5-todoapp        // the UI5 todo applications

Getting Started

The ui5-sample-apollo repository is a monorepo based on yarn workspaces. Instead of npm you need yarn to run the project. You can use the yarn installation page or install yarn via npm

# Install yarn (if not done already)
npm i -g yarn

To get started with the project, please ensure to run yarn once to install all required dependencies in your node_modules folder.

# install dependencies
yarn

# trigger a build
yarn build

# start the watch mode for development
# (hint: initially a refresh is needed after the build is completed!)
yarn watch

# start the productive mode
# (hint: takes a while since the todoapp builds all related UI5 libs)
yarn start

Using npm packages in UI5

This repository showcases with the ui5-apollo-lib package how npm packages can be consumed in UI5 libraries or applications. The ui5-apollo-lib package is a UI5 library providing a thirdparty module which exports the Apollo GraphQL client. The Apollo GraphQL client is consumed as npm dependency in the package.json. The relevant modules of Apollo and GraphQL are consumed via ES6 module imports and finally exported. A reduced module defining those exports can be seen here:

import { ApolloClient } from "apollo-client";
import { default as gql } from "graphql-tag";

export default {
    ApolloClient,
    gql
};

This module is transpiled into a UI5 AMD-like module by using Rollup. Therefore, the library implements a custom task ui5-task-rollup4ui5 which uses Rollup within the UI5 Tooling build lifecycle to transpile this module and integrate the result into the build output. The following snippet shows how the task is configured in the apollo.client library:

specVersion: '2.0'
metadata:
  name: apollo.client
type: library
builder:
  customTasks:
  - name: ui5-task-rollup4ui5
    beforeTask: replaceVersion
    configuration:
      configFile: "build-src/rollup.apollo.js"
---
# https://sap.github.io/ui5-tooling/pages/extensibility/CustomTasks/
specVersion: "1.0"
metadata:
  name: ui5-task-rollup4ui5
kind: extension
type: task
task:
  path: lib/rollup4ui5.js

The Rollup tasks executes the config file rollup.apollo.js which is a standard Rollup configuration file. This configuration file uses several plugins to resolve dependencies from node_modules, to convert commonjs modules to transpile the code using babel and to minify the code using terser. Looking into the configuration, there are two special things:

{
    input: path.resolve(__dirname, "apollo/index.js"),
    output: {
        ui5ModuleName: `/resources/apollo/client/thirdparty/apollo${minify ? "" : "-dbg"}.js`,
        format: "amd",
        amd: {
            define: "sap.ui.define"
        }
    },
    plugins: [...]
}

The output configuration uses an alternative define API: sap.ui.define and it also contains the ui5ModuleName which is the runtime path of the generated UI5 module. The later information are used for the task to store the generated file in the proper place in the UI5 builder workspace.

This now generates a UI5 AMD-like module which can be simply consumed at runtime for your UI5 applications and in libraries (in this case using babel to transpile the code to allow the usage of the latest JS language features, like Object destructuring, ...):

sap.ui.define(["apollo/client/thirdparty/apollo"], function(UI5Apollo) {

    const {
        ApolloClient,
        gql
    } = UI5Apollo;

});

Using Apollo GraphQL in UI5

To improve the usage of Apollo GraphQL within UI5 applications, the ui5-apollo-lib provides a Controller implementation which is adding some syntactic sugar to work easily with GraphQL and manages the GraphQL data in a JSONModel. This allows the usage of simple databinding in the View to connect the GraphQL data with the UI5 controls. This Controller is the apollo/client/controller/ApolloBaseController.

Let's take a look into the functionality provided by the ApolloBaseController:

    /**
     * Initializes the ApolloBaseController
     */
    onInit: function () {
        // retrieve the Apollo client
        var apolloClient = this.getOwnerComponent().apolloClient;

        // some syntactic sugar for the consumers
        this.$query = apolloClient.query.bind(apolloClient);
        this.$mutate = apolloClient.mutate.bind(apolloClient);
        this.$subscribe = apolloClient.subscribe.bind(apolloClient);

        // create a JSONModel for the data
        this.getView().setModel(new JSONModel());

        // enrich the Apollo root object
        if (this.apollo) {
            Object.keys(this.apollo).forEach(entity => {
                this.apollo[entity].invoke = () => {
                    this.invoke(entity)
                }
                if (!this.apollo[entity].skip) {
                    this.invoke(entity);
                }
            });
        }
    },

Due to the complexity of creating the ApolloClient, the ApolloBaseController retrieves the ApolloClient instance from the Component.

An example can be found in our todo app Component.js or a simple version in the sample app guide on "How to connect your application". For the ApolloBaseController it is only relevant where you define the client, but after all you can use all the options available for the ApolloClient

return UIComponent.extend("sap.ui.demo.todo.Component", {
  ...
  createContent: function () {
     this.apolloClient = new ApolloClient({
        cache,
        link
     });
  }
}

In addition, the ApolloBaseController adds some shortcuts to $query, $mutate and $subscribe (the syntactic sugar). Afterwards it creates a standard JSONModel to manage the GraphQL data. The last step during the initialization is to look over the so called Apollo root object and enrich the defined entities with an invoke function as well as calling the invoke directly if the property skip is not true:

ApolloBaseController.extend("sap.ui.demo.todo.controller.App", {

    apollo: {
        todos: {
            binding: "{/todos}",
            query: gql`
                query GetToDos {
                    todos {
                        id
                        title
                        completed
                    }
                }
            `,
        },
    },

});

This declaration will trigger a query to request the todos with the attributes id, title and completed and put them into the JSONModel with the path /todos. This allows to access and bind the todos within the View with the databinding syntax:

<List items="{/todos}">
    <CustomListItem>
        <HBox>
            <CheckBox name="{id}" selected="{completed}" select=".updateTodo"/>
            <VBox justifyContent="Center">
                <Text text="{title}"/>
            </VBox>
        </HBox>
    </CustomListItem>
</List>

OpenUI5 todo application running on Apollo GraphQL

The ui5-todoapp package is a fork of the openui5-sample-app. The sample application has been extended to use the Apollo GraphQL client to communicate with a GraphQL server. It show-cases the creation of the ApolloClient in the Component.js, the usage of the ApolloBaseController for the App.controller.js and the consumption of the data in the App.view.xml.

To communicate with the Apollo GraphQL server, the ui5-todoapp is using the ui5-middleware-simpleproxy. To enable the usage of the latest JS language features, the custom task ui5-task-transpile i s used which is transpiling the JS files by using babel. The custom tasks and middlewares are defined in the ui5.yaml of the ui5-todoapp.

License

This work is dual-licensed under Apache 2.0 and the Derived Beer-ware License. The official license will be Apache 2.0 but finally you can choose between one of them if you use this work.

When you like this stuff, buy @DamianMaring a beer or buy @pmuessig a coke.

About

License:Other


Languages

Language:JavaScript 90.7%Language:HTML 8.9%Language:CSS 0.4%