Skip to content

Instantly share code, notes, and snippets.

@timbophillips
Last active April 4, 2020 01:56
Show Gist options
  • Save timbophillips/b430c8241cdd6fe62a683e79b4845a9f to your computer and use it in GitHub Desktop.
Save timbophillips/b430c8241cdd6fe62a683e79b4845a9f to your computer and use it in GitHub Desktop.
codegen angular apollog hasura graphql
https://github.com/dotansimha/graphql-code-generator/issues/2043
https://medium.com/angular-in-depth/configuring-a-angular-cli-project-with-graphql-37217f66d419
https://github.com/dotansimha/graphql-code-generator/issues/2043#issuecomment-508540316
@timbophillips
Copy link
Author

CLI commands:

ng new graphql3
cd graphql3
ng add apollo-angular
npm install -s apollo-link-ws subscriptions-transport-ws

Modify angular.json to add pointer to proxy config file

"serve": {`
    "options": {
        "proxyConfig": "src/proxy.conf.json"

add a new file src/proxy.conf.json with proxy settings:

{
  "/api": {
    "target": "http://localhost:8080/v1/graphql",
    "pathRewrite": { "^/api": "" },
    "secure": false
  },
  "/ws": {
    "target": "ws://localhost:8080/v1/graphql",
    "pathRewrite": { "^/ws": "" },
    "secure": false,
    "ws": true,
    "changeOrigin": true
  },
  "logLevel": "debug"
}

modify src/app/graphql.nodule.ts to differentiate websocket requests, modify the url, and access via proxy:

import { NgModule } from '@angular/core';
import { ApolloModule, APOLLO_OPTIONS, Apollo } from 'apollo-angular';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';

import { WebSocketLink } from 'apollo-link-ws';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';

// see src/proxyconf.json
const uri = '/api';
// need to get the current webapp address and change it from http to ws.... solution helped out by
// https://stackoverflow.com/questions/48793151/angular-cli-proxy-websocket-with-proxy-conf-json
const wsURI = `${ window.location.protocol.replace('http', 'ws')}//${window.location.host}/ws`;

export function createApollo(httpLink: HttpLink) {
  // bastardised from https://www.apollographql.com/docs/angular/features/subscriptions/
  return {
    link:
      // Apollo function that returns different ApolloLink handlers for different requests
      split(
        ({ query }) => {
          const definition = getMainDefinition(query);
          return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
        },
        // if above test is true then its a subscription so it needs a WS
        new WebSocketLink({
          uri: wsURI,
          options: {
            reconnect: true,
          }
        }),
        // otherwise boring old HTTP will suffice
        httpLink.create({ uri }),
      ),
    cache: new InMemoryCache(),
  };
}

@NgModule({
  exports: [ApolloModule, HttpLinkModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink],
    },
  ],
})
export class GraphQLModule { }

back to the command line

npm install -D graphql-cli
npx graphql init
? Enter project name (Enter to skip):
? Local schema file path: schema.graphql
? Endpoint URL (Enter to skip): http://localhost:8080/v1/graphql
? Name of this endpoint, for e.g. default, dev, prod: default
? Subscription URL (Enter to skip): ws://localhost:8080/v1/graphql
? Do you want to add other endpoints? No
? What format do you want to save your config in? (Use arrow keys)
❯ JSON
  YAML

add / modify package scripts in package.json:

  "scripts": {
    "gql:update-schema": "graphql get-schema -e default",
    "gql:codegen": "graphql-codegen --config codegen.yml",
    "gql:codegen:watch": "graphql-codegen --watch",
    "start:ng": "ng serve",
    "start": "run-p start:ng gql:codegen:watch",

run the update-schema script:

npm run gql:update-schema

install GraphQL Codegen CLI

npm install @graphql-codegen/cli -D

run it:

npx graphql-codegen init

    Welcome to GraphQL Code Generator!
    Answer few questions and we will setup everything for you.

? What type of application are you building? Application built with Angular
? Where is your schema?: (path or url) schema.graphql
? Where are your operations and fragments?: src/**/*.graphql
? Pick plugins: TypeScript (required by other typescript plugins), TypeScript Operations (operations and fragments), Ty
peScript Apollo Angular (typed GQL services)
? Where to write the output: src/generated/types.graphql-gen.ts
? Do you want to generate an introspection file? No
? How to name the config file? codegen.yml
? What script in package.json should run the codegen? gql:codegen
npm install

add an ignore line to .gitignore

*.graphql-gen.ts

install a npm app that lets us run more than one watch script

npm install npm-run-all -D

create a GraphQL file and put it in the src/app folder

query Names {
  names {
    id
    name
    number
    color
  }
}

subscription NamesSubscription {
  names {
    id
    name
    number
    color
  }
}

mutation addName($name: String, $number: Int = 0, $color: String = "black") {
  __typename
  insert_names(objects: {color: $color, name: $name, number: $number}) {
    returning {
      id
    }
  }
}

mutation delName($id: Int) {
  __typename
  delete_names(where: {id: {_eq: $id}}) {
    returning {
      id
    }
  }
}

modify the src/app/app.component.ts file to create a "testing page":

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { NamesGQL, NamesSubscriptionGQL, AddNameGQL, DelNameGQL } from '../generated/types.graphql-gen';

@Component({
  selector: 'app-names',
  template: `<h3>apollo-hasura-learning</h3>
<p>
  <input #newName />
  <input #newColor />
  <input #newNumber />
  <button (click)="addName(newName.value, newColor.value, newNumber.value)">Add new name</button>
</p>
<ul *ngFor="let item of namesSub$ | async | select: 'names'">
  ID:{{
    item.id
  }}
  Name:
  {{
    item.name
  }}
  Color:
  {{
    item.color
  }}
  Number:
  {{
    item.number
  }}
  <button (click)="delName(item.id)">delete</button>
</ul>
`,
  styles: [
    `
      h3 {
        font-family: monospace;
        color: blue;
        margin-left: 40px;
      }
      p {
        font-family: monospace;
        margin-left: 40px;
      }
      ul,
      button,
      input {
        font-family: monospace;
        margin-right: 5px;
      }
    `
  ],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {

  names$: Observable<string[]>;
  namesSub$: Observable<{}>;

  title = 'graphql-angular-learning';

  constructor(
    private namesGQL: NamesGQL,
    private namesSubscriptionGQL: NamesSubscriptionGQL,
    private addNameGQL: AddNameGQL,
    private delNameGQL: DelNameGQL) {
  }

  ngOnInit() {
    this.names$ = this.namesGQL.fetch({}).pipe(
      map(result => result.data.names.map(x => x.name))
    );
    this.namesSub$ = this.namesSubscriptionGQL.subscribe();
  }

  addName(newName: string, newColor: string, newNumber: number) {
    this.addNameGQL.mutate({ name: newName, number: newNumber, color: newColor }).subscribe(x => console.log(JSON.stringify(x)));
  }

  delName(id: number | string) {
    this.delNameGQL.mutate({ id: id as number }).subscribe(x => console.log(JSON.stringify(x)));
  }
}

command line

npm run gql:codegen
npm run start

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment