0

I've been struggling for hours in order to get my Angular component work with retrieving data from an Apollo GraphQL server by a simple Query (PingGQLService). I'm using apollo-angular 4.1.0 and @apollo/client 3.0.0, even tried with @graphql-codegen/typescript-apollo-angular

This is my App Module:

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GraphQLModule } from './graphql.module';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AppMaterialModule } from './app.material.module';

import { DummyComponent } from './components/dummy/dummy.component';
import { PingGQLService } from './services/ping.service';

@NgModule({
    declarations: [
        AppComponent,
        DummyComponent,
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        AppMaterialModule,
        GraphQLModule,
        HttpClientModule,
    ],
    providers: [PingGQLService],
    bootstrap: [AppComponent],
})
export class AppModule {}

This is my GraphQLModule:

import { HttpClientModule, HttpHeaders } from '@angular/common/http';
import { NgModule } from '@angular/core';
import {
    ApolloClientOptions,
    ApolloLink,
    InMemoryCache,
} from '@apollo/client/core';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';

import { PingGQLService } from './services/ping.service';

const uri = 'http://localhost:3000'; // <-- add the URL of the GraphQL server here
export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
    const http = httpLink.create({ uri, withCredentials: true });

    const contentType = setContext((operation, context) => ({
        headers: new HttpHeaders().set('Content-Type', 'application/json'),
    }));

    const proto = setContext((operation, context) => ({
        headers: new HttpHeaders().set('x-forwarded-proto', 'https'),
    }));

    const errorLink = onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors)
            graphQLErrors.map(({ message, locations, path }) =>
                console.log(
                    `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
                )
            );
        if (networkError) console.log(`[Network error]: ${networkError}`);
    });

    return {
        link: ApolloLink.from([contentType, proto, errorLink, http]),
        cache: new InMemoryCache(),
    };
}

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

This is my PingGQLService:

import { Injectable } from '@angular/core';
import { gql, Query } from 'apollo-angular';

export interface Response {
    pong: string;
}

@Injectable({
    providedIn: 'root',
})
export class PingGQLService extends Query<Response> {
    override document = gql`
        query Ping {
            ping
        }
    `;
}

This is my Dummy Component:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { PingGQLService } from 'src/app/services/ping.service';

@Component({
    selector: 'app-dummy',
    templateUrl: './dummy.component.html',
    styleUrls: ['./dummy.component.css'],
})
export class DummyComponent implements OnInit {
    ping!: Observable<string>;
    pingJson!: string;

    constructor(private pingGQL: PingGQLService) {}

    ngOnInit() {
        this.ping = this.pingGQL.watch().valueChanges.pipe(
            map((result) => {
                console.log(result.data);
                return result.data.pong;
            })
        );
        this.pingJson = JSON.stringify(this.ping);
    }
}

And this is the HTML:

<p>dummy works!</p>
<h1>Account:</h1>
<ul>
    <li>{{ pingJson }}</li>
    <li>{{ ping }}</li>
</ul>

I don't know what is going wrong with my code, it could be something about CORS, but this is my GraphQL server config:

import "module-alias/register";
import "reflect-metadata";

import express from "express";
import session from "express-session";

import { startServer } from "@/app";
import { HOST, NODE_ENV, PORT } from "@/configs/index";
import { loadConsumers } from "@/mq/consumers";

declare module "express-session" {
  interface Session {
    token: string | undefined;
  }
}

loadConsumers()
  .then(() => {
    console.log("MQ consumers loaded");
  })
  .catch((error) => {
    console.error("MQ consumers failed to load", error);
  });

const app = express();

app.use(
  session({
    secret: "secret",
    resave: false,
    saveUninitialized: false,
    cookie: { secure: true, sameSite: "none", maxAge: 2 * 60 * 60 * 1000 },
  })
);

app.set("trust proxy", 1);

// Setup graphql
startServer()
  .then((server) => {
    server.applyMiddleware({
      app,
      path: "/graphql",
      cors: {
        credentials: true,
        origin: [
          "https://studio.apollographql.com",
          "http://localhost:3000/graphql",
          "http://localhost:4200",
          "http://localhost:4200/",
          "*",
        ],
      },
    });

    app.listen(PORT, () => {
      console.log(
        `Server running on ${NODE_ENV} mode at http://${HOST}:${PORT}/graphql`
      );
    });
  })
  .catch((err) => {
    console.error("Error starting server", err);
  });

I expect that the HTML gives me this kind of answer:

{
  "data": {
    "ping": "pong"
  }
}

Or even an error, but nothing displays more than this:

I don't get any new log in my console, so I don't know what is happening behind or even if my headers are being sent.

UPDATE: I've simplified the GraphQLModule and changed the DummyComponent according this example, now it doesn't use the service as external, still don't work. These are the changes:

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';

import { ApolloClientOptions, InMemoryCache } from '@apollo/client/core';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';

const uri = 'http://localhost:3000/graphql';
export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
    const http = httpLink.create({ uri });

    return {
        link: http,
        cache: new InMemoryCache(),
    };
}

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

import { Component, OnInit } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export type Query = {
    ping: string;
};

@Component({
    selector: 'app-dummy',
    templateUrl: './dummy.component.html',
    styleUrls: ['./dummy.component.css'],
})
export class DummyComponent implements OnInit {
    ping!: Observable<string>;
    pingJson!: string;

    constructor(private apollo: Apollo) {}

    ngOnInit() {
        this.ping = this.apollo
            .watchQuery<Query>({
                query: gql`
                    query Ping {
                        ping
                    }
                `,
            })
            .valueChanges.pipe(map((result) => result.data.ping));
        this.pingJson = JSON.stringify(this.ping);
    }
}

  • Have you tried to strip down your `GraphQLModule` as far as possible? You don't need all the additional links (except http) and the `useCredentials` flag for testing. I would start by removing all those additions first. – puelo Oct 22 '22 at 20:56

1 Answers1

0

I can't believe it, but after all, the solution was to use {{ ping | async }} in the HTML template.

I didn't even know the use of the async pipe on Angular but of course it gave me like 20 hours of pure stress.

Also, thanks to @puelo 's comment, I left my GraphQLModule cleaner and it perfectly works