I am trying to use getServerSideProps to fetch a query every time this component is rendered using apollo and next.js
export async function getServerSideProps(context) {
const { data } = await client.query({
query: GET_AUTHED_USER
})
return {
props: { user: data.getAuthedUser },
}
}
const Profile = ({ user }) => {
const router = useRouter();
// const [state, setState] = useState(JSON.parse(router.query.currentUser));
const [state, setState] = useState(user);
console.log(state)
...
APOLLO CONFIG
import { ApolloClient, InMemoryCache, ApolloLink } from '#apollo/client';
import { getCookie } from './utils/functions';
import { setContext } from '#apollo/client/link/context';
import { createUploadLink } from 'apollo-upload-client';
const authLink = setContext((_, { headers }) => {
// get the authentication token from storage if it exists
const token = getCookie('JWT');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? token : '',
}
}
});
const httpLink = createUploadLink({
uri: 'http://localhost:5000/graphql',
});
const client = new ApolloClient({
uri: 'http://localhost:5000/graphql',
cache: new InMemoryCache(),
link: ApolloLink.from([authLink, httpLink])
});
export default client;
currently, when visiting the /profile page I receive the error:
Server Error
Error: document is not defined
Does anyone have any insight on how to resolve this or suggest a valid work around?
appolo/client is a client side library. getServersideProps execute in server side. document doesn't exist in server side. It will only work within client side.
Workaround 1:
You can use swr. swr is for client side data fetch. so you don't need getServerSideProps.
Workaround 2:
You can use apollo-server. Then you can call apollo-server function in getServerSideProps as apollo-server is for server side calling.
Related
So I'm trying to do a custom header for the requests to my graphql backend from my nextJS frontend. The code of the graphql client is as follows:
import { ApolloClient, ApolloLink, InMemoryCache, createHttpLink } from '#apollo/client'
import { setContext } from '#apollo/client/link/context'
import getConfig from 'next/config'
import nookies, { parseCookies } from 'nookies'
const { publicRuntimeConfig } = getConfig()
const httpLink = createHttpLink({
uri: publicRuntimeConfig.uri,
credentials: 'same-origin'
})
const authLink = setContext((_, { headers }) => {
let token = ''
if (typeof window !== 'undefined') {
const { 'toDo-token': newToken } = parseCookies()
token = newToken
} else {
// error is right here. I cant find a way to access the ctx
const { 'toDo-token': newToken } = nookies.get(ctx)
token = newToken
}
return {
headers: {
...headers,
Authorization: token ?? undefined
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
ssrMode: typeof window === 'undefined'
})
export default client
On the client side it works just fine. However, since I cant access client cookies on the server side, I'm trying to use the get function from nookies to grab the auth cookies and pass to the headers with the setContext function. The problem I'm having is that I cant find a way to grab the app context from next so I can look at the request info and grab the ctx from there so I can see the cookies.
I do something similar in my _app.tsx in the following way:
MyApp.getInitialProps = async ({ ctx }: AppContext) => {
const { locale } = ctx
const { 'toDo-token': token } = nookies.get(ctx)
let user = null
try {
user = await useUser()
} catch {
user = null
}
return {
locale: locale === 'default' ? 'en' : locale as LocaleEnum,
token: token ?? null,
user
}
}
Does anyone know how I can access the request context in the graphql setContext function?
First time using Hot Chocolate and Apollo and I have an issue where the Apollo client created always returns 404 for the Hot Chocolate API. This API is running on localhost and can be queried fine.
Whats weird is, if I use the Apollo Client getServerSideProps, the query returns fine.
EG This is working
export async function getServerSideProps() {
const { data } = await client.query({
query: gql`
query {
rolesQuery {
roles {
id
name
}
}
}
`,
});
return {
props: { roles: data.rolesQuery.roles, },
};
}
This does not
export function createApolloClient() {
const httpLink = createHttpLink({
uri: process.env.NEXT_PUBLIC_GRAPHQL_API,
fetchOptions: {
mode: 'no-cors',
},
});
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
return client;
}
// Usage of the client
const apolloClient = createApolloClient();
<ApolloProvider client={apolloClient}>...</ApolloProvider>
At first I had a CORS issue which is fixed but I'm still getting a 404 when calling this from a hook inside a component (using graphql-codegen version 2.6.1).
Any ideas?
Edit
Front end component calling mutation
import React from "react";
import { gql, useMutation } from '#apollo/client';
const REGISTER = gql`
mutation RegisterUser ($email: String!, $password: String!) {
usersMutation {
registerUser (request: { email: $email, password: $password}) {
id
firstName
email
}
}
}
`;
const SignUp = () => {
const [registerUser, { data, loading, error }] = useMutation(REGISTER);
console.log(registerUser())
return <div>code removed for brevity </div>;
};
export default SignUp;
This is my apollo client code.
import {
ApolloClient,
ApolloLink,
createHttpLink,
InMemoryCache
} from "#apollo/client"
import { setContext } from "#apollo/client/link/context"
let uri = `${process.env.NEXT_PUBLIC_API_URL}/wp/graphql`
const httpLink = createHttpLink({ uri, credentials: "include" })
const authLink = setContext((_, { headers }) => {
headers
})
let client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: "no-cache"
}
}
})
export { client }
This is my page routing in next where i am trying a simple GraphQL Query.
export default function PrivatePath(props: any) {
console.log("props:", props)
const { data, loading, error } = useQuery(gql`
query MyQuery {
page(id: "/min-sida", idType: URI) {
status
title
}
}
`)
console.log("data:", data)
return (
<ApolloProvider client={client}>
<AuthProvider>
<div></div>
</AuthProvider>
</ApolloProvider>
)
}
export async function getServerSideProps(context: any) {
const slugs = context.query.path[0]
const query = gql`
query MyQuery {
page(id: "/min-sida", idType: URI) {
status
title
}
}
`
const data = await client.query({ query: query })
return {
props: data
}
}
What is interesting to me is that the hook useQuery, does what is expected and when logged in delivers the page title and status.
The client.query, however, does not ever return page title and status, even when logged in it simple returns page: { null }.
My initial thought was that it was because getStatiProps in next won't be able to get the data no matter what, but it seems getServerSideProps is unable to do so as well?
Does anyone have a decent idea for solving this?
Decided to switch from normal React to NextJS after watching various videos and reading articles. I'm currently trying to implement Apollo Client but am getting this (title) error and was hoping to get some help. The way my withData is currently set is
import { ApolloClient } from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { hasSubscription } from '#jumpn/utils-graphql';
import * as AbsintheSocket from '#absinthe/socket';
import withApollo from 'next-with-apollo';
import { createAbsintheSocketLink } from '#absinthe/socket-apollo-link';
import { Socket as PhoenixSocket } from 'phoenix';
let apolloClient = null;
const HTTP_ENDPOINT = 'http://localhost:4000/api/v1/graphiql';
const WS_ENDPOINT = 'ws://localhost:4000/api/v1/socket';
const httpLink = createHttpLink({
url: HTTP_ENDPOINT
});
const socketLink = createAbsintheSocketLink(AbsintheSocket.create(new PhoenixSocket(WS_ENDPOINT)));
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('auth-item');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
};
});
const link = new ApolloLink.split(
(operation) => hasSubscription(operation.query),
socketLink,
authLink.concat(httpLink)
);
const create = (initialState) => {
return new ApolloClient({
link: link,
cache: new InMemoryCache().restore(initialState || {})
});
};
const initApollo = (initialState) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return create(initialState);
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState);
}
return apolloClient;
};
export default withApollo(initApollo);
All help is appreciated to understand what I did wrong and what is a better approach should there be one.
The issue is because nextJs will run the code above in the server and WebSocket is a property that exists only in the browser, to get this fixed you can do:
const socketLink =
process.browser &&
createAbsintheSocketLink(
AbsintheSocket.create(
new PhoenixSocket(WS_URI)
)
)
By checking for process.browser you make sure that this function is executed only on client-side.
I would like to set up a graphql client with React for both uploading file and handle subscriptions from a graphql server.
The file upload and the other queries work well. The problem is with subscriptions. I get in the browser console the following error:
WebSocket connection to 'ws://localhost:3001/subscriptions' failed: Connection closed before receiving a handshake response
I have used apollo-upload-client for file upload and apollo-link-ws for subscriptions.
I can see that subscriptions-transport-ws suggests using createNetworkInterface and addGraphQLSubscriptions but this approach is not compatible with apollo-upload-client that only supports createUploadLink.
I'm stuck. Please help.
I setup my client like this:
import React from 'react';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { createUploadLink } from 'apollo-upload-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink, Observable, split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
const cache = new InMemoryCache();
const request = async (operation) => {
const token = localStorage.getItem('token');
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : '',
},
});
};
const httpLink = createUploadLink({ uri: 'http://localhost:3001/graphql' });
// Create a WebSocket link:
const wsLink = new WebSocketLink({
uri: 'ws://localhost:3001/subscriptions',
options: {
reconnect: true
},
});
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink,
);
const requestLink = new ApolloLink((operation, forward) =>
new Observable((observer) => {
let handle;
Promise.resolve(operation)
.then(oper => request(oper))
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
}));
const apolloClient = new ApolloClient({
link: ApolloLink.from([
requestLink,
link,
]),
cache,
});
export const withApolloClient = App => (
<ApolloProvider client={apolloClient}>
<App client={apolloClient} />
</ApolloProvider>
);
export default apolloClient;
I am using a similar config but instead of importing WebSocketLink from apollo-link-ws I imported it from #apollo/client.
With that setup i had both the subscription and upload working.
import { WebSocketLink } from "#apollo/client/link/ws";
I would suggest to use graphql-server-express like this