Angular doesn't retrieve information from Apollo GraphQL Server

788 views Asked by At

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);
    }
}

1

There are 1 answers

0
Santiago Vallejo On

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