Apollo Server - GET query missing - reactjs

I am trying to use this template to learn how to use graphql/apollo/prisma etc.
When I try to start the server, I get a confirmation in the console, but an error in the browser that says: GET query missing.
import "reflect-metadata"
import "dotenv/config"
import { ApolloServerPluginCacheControl, ApolloServerPluginLandingPageDisabled } from "apollo-server-core"
import { ApolloServer } from "apollo-server-express"
import jwt from "express-jwt"
import { buildSchema } from "type-graphql"
import { Container } from "typedi"
import { JWT_AUTH } from "./lib/config"
import { ExpressContext } from "./lib/express"
import { formatResponse } from "./lib/formatResponse"
import { ErrorInterceptor } from "./lib/globalMiddleware"
import { loadPrismaHooks } from "./lib/hooks"
import { loadCurrentUser } from "./lib/loadCurrentUser"
import { loadResolvers } from "./lib/loadResolvers"
import { prisma } from "./lib/prisma"
import { Server } from "./lib/server"
class App extends Server {
constructor() {
super()
this.init().catch((error) => {
this.logger.error(error)
process.exit(1)
})
}
async init() {
await this.setUpDb()
await this.setUpAuth()
await this.setupApollo()
this.start()
}
async setUpDb() {
await prisma.$connect()
loadPrismaHooks()
this.logger.info("DB ready")
}
async setUpAuth() {
this.app
.use(jwt(JWT_AUTH))
.use((err: any, _: any, __: any, next: any) => {
if (err.name === "UnauthorizedError") next()
})
.use(loadCurrentUser)
this.logger.info("Auth ready")
}
async setupApollo() {
const schema = await buildSchema({
container: Container,
resolvers: loadResolvers(),
globalMiddlewares: [ErrorInterceptor],
})
const apolloServer = new ApolloServer({
context: ({ req, res }: ExpressContext) => ({ req, res, prisma }),
formatResponse,
plugins: [ApolloServerPluginCacheControl(), ApolloServerPluginLandingPageDisabled()],
schema,
// playground: true,
// introspection: true
})
await apolloServer.start()
apolloServer.applyMiddleware({ app: this.app })
// I deleted cors: true from the above line
this.logger.info("Apollo setup hello")
}
}
new App()
I have seen other posts describing that cors is no longer needed (not sure what the basis for that is) and also suggesting that I add the playground and introspection arguments to the new ApolloServer. I tried this (as shown in the commented lines), but the playground is not recognised as a valid argument.
Server is defined in the lib folder as:
import "reflect-metadata"
import "dotenv/config"
import * as Sentry from "#sentry/node"
import * as Tracing from "#sentry/tracing"
import chalk from "chalk"
import express from "express"
import morgan from "morgan"
import { IS_PRODUCTION, PORT, SENTRY_DSN } from "./config"
export class Server {
private readonly _app: express.Application
readonly logger: {
info: (message: string) => void
error: (message: string) => void
}
constructor() {
this._app = express()
.use(Sentry.Handlers.requestHandler())
.use(Sentry.Handlers.tracingHandler())
.enable("trust proxy")
.use(
morgan("dev", {
skip: (req) => req.method === "OPTIONS",
stream: { write: (message) => console.log(message + "\n\n") },
}),
)
if (IS_PRODUCTION) {
Sentry.init({
dsn: SENTRY_DSN,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app: this._app }),
],
enabled: IS_PRODUCTION,
tracesSampleRate: 1.0,
})
}
this.logger = {
info: this.info,
error: this.error,
}
}
protected error(message: string) {
console.log(`[${chalk.red("ERROR")}] `, message)
}
protected info(message: string) {
console.log(`[${chalk.blue("INFO")}] `, message)
}
protected get app(): express.Application {
return this._app
}
start(): void {
this._app
.use(Sentry.Handlers.errorHandler())
.listen(PORT, () => this.logger.info(`Server started at http://localhost:${PORT}/graphql 🚀` + "\n"))
}
}
The console logs in the terminal print the server started confirmation, but the browser just generates the cannot GET message. I don't know what this message means, to be able to begin to try and figure out how to get to the playground.
Can anyone recommend current instructions for how to configure the server?

the reason for this problem seems to be related to the move to the graphql sandbox environment. If you add localhost555 to the sandbox address bar, the page loads without an error.

Related

431: Request Header Fields Too Large error

I'm creating a project that uses NestJs for the backend part and React for the frontend part. I would like to add users but there is an error that prevented it. When I make the request using Postman it works fine, but when I try it through the front, it sends a 431 error. I'm using redux/toolkit for sending requests. I'm using http://localhost:3001/. I tried to add a "start": "nest start -- --max-http-header-size=80000" for backend and frontend, but it didn't help me. Attached a request-response screen:
Frontend request
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import { CreateUserRequest } from "../dto/create-user-request.dto";
import { User } from "../models/User";
export const usersApi = createApi({
reducerPath: "usersApi",
baseQuery: fetchBaseQuery({
baseUrl: "/users",
}),
endpoints: (build) => ({
createUser: build.mutation<User, CreateUserRequest>({
query: (createUserRequest) => ({
url: "/",
method: "POST",
body: createUserRequest,
}),
}),
}),
});
export const { useCreateUserMutation } = usersApi;
backend main.ts
import { ValidationPipe } from '#nestjs/common';
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
app.useGlobalPipes(new ValidationPipe())
await app.listen(3000);
}
bootstrap();
users.controller.ts
import { Body, Controller, Post } from '#nestjs/common';
import { CreateUserRequest } from './dto/request/create-user-request.dto';
import { UserResponse } from './dto/response/user-response.dto';
import { UsersService } from './users.service';
#Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) { }
#Post()
async createUser(
#Body() createUserRequest: CreateUserRequest
): Promise<UserResponse> {
return this.usersService.createUser(createUserRequest)
}
}

Next.js imported function errors out with 'ReferenceError' in getServerSideProps

I am using Firebase for auth in my project. After user authenticates, I set his/her id token in cookies, so that next time any request is made to auth-only page, I can verify the token server-side for SSR.
However, the wrapper function I wrote for this errors out as 'ReferenceError' when used in getServerSideProps.
lib/firebase-admin.ts
import { initializeApp, App, AppOptions } from 'firebase-admin/app'
import { getAuth, Auth } from 'firebase-admin/auth'
import { credential } from 'firebase-admin'
import serviceAccount from '../secrets/firebase-admin-sdk.json'
// Firebase Admin app configs
const firebaseAdminConfig: AppOptions = {
credential: credential.cert(JSON.stringify(serviceAccount))
}
// Get app admin instance and export it
const app: App = initializeApp(firebaseAdminConfig)
export default app
// Get auth admin and export
export const auth: Auth = getAuth(app)
utils/auth-server.ts
import { auth } from '../lib/firebase-admin'
import { DecodedIdToken } from 'firebase-admin/auth'
import AuthErrorMessages from '../constants/auth'
// Export function to verify id token in server side
interface IVerifyIdToken {
status: boolean
message?: string
token?: DecodedIdToken
}
export const verifyIdToken = async (idToken: string): Promise<IVerifyIdToken> => {
try {
const decodedIdtoken = await auth.verifyIdToken(idToken, true)
console.log(decodedIdtoken)
return { status: true, token: decodedIdtoken }
} catch (e) {
return { status: false, message: e }
}
}
components/test.tsx
import { GetServerSideProps, GetServerSidePropsContext, InferGetServerSidePropsType } from 'next'
import nookies from 'nookies'
import { verifyIdToken } from '../utils/auth-server'
export const getServerSideProps: GetServerSideProps = async (ctx: GetServerSidePropsContext) => {
const cookies = nookies.get(ctx)
if (cookies.token) {
const idToken = await verifyIdToken(cookies.token) // ERROR HERE
console.log(idToken)
return {
props: {
email: 'DUMMY'
}
}
} else {
return {
props: {
email: "NO EMAIL (not logged in)"
}
}
}
}
export default function Test({ email }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<p>Your email: {email}</p>
)
}
Error while opening /test
ReferenceError: Cannot access 'auth' before initialization
at Module.auth (webpack-internal:///./lib/firebase-admin.ts:5:53)
at verifyIdToken (webpack-internal:///./utils/auth-server.ts:12:87)
at getServerSideProps (webpack-internal:///./pages/test.tsx:20:96)
at Object.renderToHTML (/home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/render.js:479:26)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async doRender (/home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/next-server.js:1392:38)
at async /home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/next-server.js:1487:28
at async /home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/response-cache.js:63:36
I fixed the problem! (thanks #ArneHugo the hint)
So, what happened was not really a cyclic dependency, but files getting compiled asynchronously, because of which there was no actual control over what got compiled first.
I fixed this by making a small change:
lib/firebase-admin.ts
.
.
.
const serviceAccount = require('../secrets/firebase-admin-sdk.json') // Earlier -> import serviceAccount from '../secrets/firebase-admin-sdk.json'
.
.
.
credential: credential.cert(serviceAccount) // Earlier -> credential: credential.cert(JSON.stringify(serviceAccount))
.
.
.
// REPLACE ENTIRE BELOW PORTION WITH THIS
// Get app admin instance and export it
if (getApps().length === 0) { // To make sure only one instance is created and referred to at a time
initializeApp(firebaseAdminConfig)
}
// Get auth admin and export
export const auth: Auth = getAuth(getApp()) // To make sure auth from only the one app instance we have is exported

Using ra-data-graphql with AppSync GraphQL API

I'm trying to use react-admin with AWS Amplify library and AWS AppSync SDK.
I can't wrap my head around how to use use ra-data-graphql/ra-data-graphql-simple with AWS AppSync API for querying/mutating data. Trying to do a very basic test with master/examples/demo from https://github.com/marmelab/react-admin/.
Any guidance will be appreciated.
Currently I'm trying to use dataProvider similar to below:
src/dataProvider/appsync.js:
import gql from 'graphql-tag';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import buildGraphQLProvider, { buildQuery } from 'ra-data-graphql-simple';
import { __schema as schema } from './schema';
const client = new AWSAppSyncClient({
url: "https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql",
region: "us-east-1,
auth: {
type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(),
}
const myBuildQuery = introspection => (fetchType, resource, params) => {
const builtQuery = buildQuery(introspection)(fetchType, resource, params);
if (resource === 'listings' && fetchType === 'GET_LIST') {
return {
...builtQuery,
query: gql`
query getListings {
data: getListings {
items {
listingId
}
}
}`,
};
}
return builtQuery;
}
export default buildGraphQLProvider({ client: client, introspection: { schema }, buildQuery: myBuildQuery })
src/dataProvider/index.js:
export default type => {
switch (type) {
case 'graphql':
return import('./graphql').then(factory => factory.default());
case 'appsync':
return import('./appsync');
default:
return import('./rest').then(provider => provider.default);
}
};
src/App.js:
...
import dataProviderFactory from './dataProvider';
...
class App extends Component {
state = { dataProvider: null };
async componentDidMount() {
const dataProvider = await dataProviderFactory(
process.env.REACT_APP_DATA_PROVIDER
);
this.setState({ dataProvider });
}
...
src/dashboard/Dashboard.js:
...
fetchData() {
this.fetchListings();
}
async fetchListings() {
const { dataProvider } = this.props;
const { data: reviews } = await dataProvider(GET_LIST, 'listings');
console.log(listings)
}
...
Currently no data is returned from the API and the exception is thrown on await dataProvider(GET_LIST, 'listings'); saying call: [object Model] is not a function, however I see that buildGraphQLProvider promise was resolved succesfully to a function.
Can anyone suggest what I am doing wrong and what is the right way to approach the task?

"this.getClient(...).watchQuery is not a function" - remote schema stitching with Apollo 2 / Next.js

So I'm attempting to stitch multiple remote GraphCMS endpoints together on the clientside of a Next.js app, and after trying/combining about every example on the face of the internet, I've gotten it to a place that's worth asking about. My error:
TypeError: this.getClient(...).watchQuery is not a function at GraphQL.createQuery
github repo here, where you can see this initApollo.js in context:
import { ApolloClient } from 'apollo-client'
import {
makeRemoteExecutableSchema,
mergeSchemas,
introspectSchema
} from 'graphql-tools'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import fetch from 'node-fetch'
import { Observable, ApolloLink } from 'apollo-link'
import { graphql, print } from 'graphql'
import { createApolloFetch } from 'apollo-fetch'
let apolloClient = null
if (!process.browser) {
global.fetch = fetch
}
const PRIMARY_API = 'https://api.graphcms.com/simple/v1/cjfipt3m23x9i0190pgetwf8c'
const SECONDARY_API = 'https://api.graphcms.com/simple/v1/cjfipwwve7vl901427mf2vkog'
const ALL_ENDPOINTS = [PRIMARY_API, SECONDARY_API]
async function createClient (initialState) {
const AllLinks = ALL_ENDPOINTS.map(endpoint => {
return new HttpLink({
uri: endpoint,
fetch
})
})
const allSchemas = []
for (let link of AllLinks) {
try {
allSchemas.push(
makeRemoteExecutableSchema({
schema: await introspectSchema(link),
link
})
)
} catch (e) {
console.log(e)
}
}
const mergedSchema = mergeSchemas({
schemas: allSchemas
})
const mergedLink = operation => {
return new Observable(observer => {
const { query, variables, operationName } = operation
graphql(mergedSchema, print(query), {}, {}, variables, operationName)
.then(result => {
observer.next(result)
observer.complete()
})
.catch(e => observer.error(e))
})
}
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser,
link: mergedLink,
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo (initialState) {
if (!process.browser) {
return createClient(initialState)
}
if (!apolloClient) {
apolloClient = createClient(initialState)
}
console.log('\x1b[37m%s\x1b[0m', apolloClient)
return apolloClient
}
I'm getting useful data all the way up into the .then() inside the Observable, where I can log the result
This is a shot in the dark, but initApollo isn't async so it returns a promise (not an ApolloClient object) which is then being passed into client prop of the ApolloProvider. watchQuery doesn't exist as a function on the Promise type, hence the error.
I think if you make initApollo async and then await those calls or find a way to make client creation synchronous, you should be able to address this issue.

Apollo subscription issue - updateQuery not firing

I have a functioning web socket created with Apollo's WebSocketLink interface. I managed to subscribe to an event using subscribeToMore and a message is pushed by the server (can see it in the network tab). Unfortunately updateQuery function is never triggered. I wonder whether it's the message structure that is incorrect (therefore a wrong server implementation) or is it something wrong in my client code.
For reference I added the message sent from server:
and here the graphql config for my component:
import { graphql } from "react-apollo/index";
import Insights from 'components/insights/Insights';
import gql from "graphql-tag";
import { withRouter } from "react-router-dom";
import get from 'lodash/get';
const query = gql`
query CampaignInsights($campaignId: ID) {
campaigns (id: $campaignId) {
edges {
node {
insights {
campaignPlanningInsight {
campaign
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
}
}
}
}
`;
const insightsSubscription = gql`
subscription onInsightsUpdated($campaignId: ID) {
campaignPlanningInsightUpdated(id: $campaignId) {
id
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
`;
const InsightsWithData = graphql(query, {
options: (props) => {
return {
variables: {
campaignId: props.match.params.campaignId
}
}
},
props: ({ data: { campaigns, subscribeToMore }, ownProps: { match }
}) => {
return {
insights: get(campaigns,
'edges[0].node.insights[0].campaignPlanningInsight', null),
subscribeToInsightsUpdate: () => {
return subscribeToMore({
document: insightsSubscription,
variables: {
campaignId: match.params.campaignId
},
updateQuery: (prev, { subscriptionData }) => {
debugger; // never gets here
if (!subscriptionData.data) {
return prev;
}
}
})
}
}
}
})(Insights);
export default withRouter(InsightsWithData);
I believe the issue might be the id of the graphql-ws websocket protocol.
That id needs to match the one sent by the frontend in the GQL_START message. Otherwise, the component won't re-render on a new message.
For more details, look into the subscription-transport-ws protocol

Resources