I'm using Apollo Client for my nextJs project. I'm trying to create a storefront Shopify app but i probably not understand how apollo query works.
This is the error:
Server Error
Error: Variable $handle of type String! was provided invalid value.
[product].jsx
import { useQuery } from '#apollo/client'
import GET_PRODUCT_BY_HANDLE from '../../lib/graphql/queries/getProductByHandle'
// import GET_PRODUCT from '../../lib/graphql/queries/getProduct'
import { initApollo } from '../../lib/apollo'
// const VARIABLE = 'struccante-oleoso'
export default function Product({ queryByHandle }) {
const { data, loading, error } = useQuery(GET_PRODUCT_BY_HANDLE, {
variables: { handle: queryByHandle },
})
if (loading) return <h1>Loading...</h1>
if (error || !data) return <h2>Error</h2>
if (data.productByHandle.length === 0) return <h2>404 | Not Found</h2>
return (
<div>
<h1>Home</h1>
<h2>{data.productByHandle.handle}</h2>
</div>
)
}
export const getServerSideProps = async ({ query }) => {
const queryByHandle = query.productByHandle // if i replace with "struccante-oleoso" works well
const apolloClient = initApollo()
await apolloClient.query({
query: GET_PRODUCT_BY_HANDLE,
variables: { handle: queryByHandle },
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
queryByHandle,
},
}
}
getProductByHandle.jsx
import { gql } from '#apollo/client'
const GET_PRODUCT_BY_HANDLE = gql`
query getProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
handle
description
images(first: 5) {
edges {
node {
url
altText
}
}
}
options {
name
values
id
}
variants(first: 25) {
edges {
node {
selectedOptions {
name
value
}
image {
url
altText
}
title
id
price {
amount
}
}
}
}
}
}
`
export default GET_PRODUCT_BY_HANDLE
apollo.jsx
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client'
import { useMemo } from 'react'
const domain = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
const storefrontAccessToken =
process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
let uri = `https://${domain}/api/2022-10/graphql.json`
let apolloClient
function createApolloClient() {
return new ApolloClient({
cache: new InMemoryCache({}),
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: `${uri}`,
headers: {
'X-Shopify-Storefront-Access-Token': storefrontAccessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
},
}),
})
}
export function initApollo(initialState = null) {
const client = apolloClient || createApolloClient()
if (initialState) {
client.cache.restore({
...client.extract(),
...initialState,
})
}
if (typeof window === 'undefined') {
return client
}
if (!apolloClient) {
apolloClient = client
}
return client
}
export function useApollo(initialState) {
return useMemo(() => initApollo(initialState), [initialState])
}
I expect to retrieve handle as a string from gql query
Related
I am currently trying to get data from a Firebase Realtime Database using RTK Query. However, the code here is giving me an error because the value returned in return is not correct. If anyone has knowledge of this, I would appreciate it if you could correct the code to the correct one.
import { createApi, fakeBaseQuery } from "#reduxjs/toolkit/query/react";
import { onValue, ref } from "firebase/database";
import { db } from "libs/firebase";
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: builder => ({
getUser: builder.query({
queryFn(uid) {
try {
onValue(ref(db, `users/user${uid}`), snapshot => {
return { data: snapshot.val() };
});
} catch (e) {
return { error: e };
}
},
}),
}),
});
export const { useGetUserQuery } = userApi;
import { configureStore } from "#reduxjs/toolkit";
import { userApi } from "./apiSlice";
export const store = configureStore({
reducer: {
[userApi.reducerPath]: userApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(userApi.middleware),
});
const { data: user, error, isLoading, isSuccess } = useGetUserQuery(1);
console.log(user);
error message
Try something along the lines of
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
getUser: builder.query({
async queryFn(uid) {
try {
const data = await new Promise((resolve, reject) =>
onValue(
ref(db, `users/user${uid}`),
(snapshot) => resolve(snapshot.toJSON()),
reject
)
);
return { data };
} catch (e) {
return { error: e };
}
},
}),
}),
});
There is a unit test that's failing on the following react-query hook.
The hook works fine otherwise, it uploads a binary file to an endpoint.
Here is the hook:
import { useMutation, useQueryClient } from 'react-query';
import { IUploadFieldRequest } from '../constants/types';
import { licenseServiceAPI } from '../services';
export const URI = () => '/licenses/upload';
export const useUploadLicense = () => {
const queryClient = useQueryClient();
const [apiService] = licenseServiceAPI<IUploadFieldRequest>({
headers: {
'Content-Type': 'multipart/form-data'
}
});
const {
data: response,
mutateAsync,
isLoading,
isSuccess
} = useMutation(
(formData: IUploadFieldRequest) => {
const form = new FormData();
Array.from(formData.files)?.forEach((file) => {
form.append('license-file', file);
});
return apiService.post(URI(), form);
},
{
onError: () => {
return queryClient.invalidateQueries('licenseUpload');
}
}
);
return {
data: response?.data,
uploadFiles: mutateAsync,
isLoading,
isSuccess
};
};
export default useUploadLicense;
And this is the test for it:
import { act, renderHook } from '#testing-library/react-hooks';
import { queryClientInstance } from '#oam/shared/test-utils';
import useUploadLicense from './use-upload-license';
const file = new File(['file'], 'license-file.json', { type: 'multipart/form-data' });
it('upload files', async () => {
const { result, waitFor } = renderHook(() => useUploadLicense(), {
wrapper: queryClientInstance
});
act(() => {
result.current.uploadFiles({ files: [file] });
});
await waitFor(() => expect(result.current.isLoading).toBeTruthy());
await waitFor(() => expect(result.current.isSuccess).toBeTruthy());
});
The error says it has Timed out in waitFor after 1000ms. and that's a Network error
Any ideas how to fix this?
Also, it could be useful to know the interface:
type IUploadFieldRequest = {
files: File[];
};
And the service:
import { AxiosRequestConfig } from 'axios';
import { requestConfig } from '#oam/shared/services';
import init, { Response } from '#oam/shared/utils/request-wrapper';
export const BASE_PATH =
process.env.NODE_ENV === 'development'
? '/myurl/v1'
: 'http://localhost:7999/v1';
export const licenseServiceAPI = <TRequest, TResponse = TRequest>(
config?: AxiosRequestConfig
): [Response<TRequest, TResponse>, AxiosRequestConfig] => {
const newConfig = requestConfig({
baseURL: BASE_PATH,
headers: {
Accept: 'application/json',
...config?.headers
},
responseType: config?.responseType ? config?.responseType : undefined
});
return [init(newConfig), newConfig];
};
export default licenseServiceAPI;
I am making a simple next js blog type of application using graphql as a data fetching backend to render text. Using getStaticPaths, I'm running into the following error when I try to fetch data for my page.
ReferenceError: Cannot access 'getAllPostIds' before initialization
Here is my code:
pages/posts/[id].tsx
import { getAllPostIds } from '../../../lib/posts'
const Post = ({ postData }) => {
... code.....
}
export const getStaticPaths = async () => {
const paths = getAllPostIds('aws');
return {
paths,
fallback: false
}
}
export default Post;
And here is my posts.ts where I use graphql to fetch data.
import { useQuery } from "react-query";
import { GraphQLClient } from "graphql-request";
const GET_POST_IDS = gql`
query($folder: String!) {
repository(owner: "assembleinc", name: "documentation") {
object(expression: $folder) {
... on Tree {
entries {
name
}
}
}
}
}`
;
const graphQLClient = new GraphQLClient('https://api.github.com/graphql', {
headers: {
Authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`
}
});
export const getAllPostIds = (folder: String) => {
return useQuery(folder, async () => {
... fetch data ...
});
}
Essentially, before I can even get the data through graphql, next js is complaining that getAllPostIds can't be initialized even though I import it at the top. Is there some next.js magic that I am not seeing?
I'm trying to implement meilisearch api in React native and it is working fine with my simulator and after I publish the app some of the users cannot see the data returning from meilisearch, the error is
{"name":"Invariant Violation","framesToPop":1}
This is my code
Meilisearch.js
import axios from 'axios';
import { meilisearchConfig } from '../Config';
const MeilisearchApi = async (payload, success, failed) => {
try {
const response = await axios({
method: 'post',
url: `${meilisearchConfig?.host}indexes/activities/search`,
data: payload,
headers: {
'X-Meili-API-Key': meilisearchConfig?.apiKey,
},
});
success?.(response?.data);
} catch (err) {
failed?.(err);
}
};
export default MeilisearchApi;
This is the normalizer for returning data
import moment from 'moment';
import { IActivity, IByDateGroupFilter } from 'reducers/types';
export const activityNormalizer = (state, { hits, offset }) => {
const {
melisearchActivityData: { byDate, dates, all },
} = state;
const isRefreshing = offset === 0;
const newAll = isRefreshing ? hits : [...all, ...hits];
const datesNew: string[] = isRefreshing ? [] : dates;
const byDateNew: any = isRefreshing ? {} : byDate;
const byDateGroup: IByDateGroupFilter[] = [];
hits.forEach((activity: IActivity) => {
const date = getFormattedDate(activity.created_at);
if (byDateNew[date]) byDateNew[date].push({ ...activity });
else {
byDateNew[date] = [{ ...activity }];
datesNew.push(date);
}
});
Object.keys(byDateNew).forEach((key) => {
byDateGroup.push({
title: key,
data: byDateNew[key],
});
});
return {
dates: datesNew,
byDate: byDateNew,
byDateGroup,
all: newAll,
};
};
This is how i call my Meilisearch API method
MeilisearchApi(
{
q: search,
filters: filters,
offset: newOffset,
limit: PAGE_SIZE,
},
({ hits }: { hits: any[] }) => {
setDataLoaded(true);
setMelisearchActivitiesToRedux({ hits, offset: newOffset });
if (newOffset === 0) {
sectionList?.current?.scrollToLocation({
itemIndex: 1,
});
}
},
(err: any) => {
setDataLoaded(true);
log(err)
},
);
No Idea how this error happens, when users kill the app and logging again this works fine
Hope you could help me out with the following. I am trying to upload an excel file of ≈3MB from the client side to the API by first converting the file to a DataURL where after I send it as a string. This is working for smaller files, but it somehow seems to be blocking my larger files.
When I upload the file, I get the following error.
POST body missing. Did you forget use body-parser middleware?
I have done my own research and found more people with the same problem, though I could not find a solution.
https://github.com/apollographql/apollo-server/issues/792
This is the code I am using on the server side.
import { ApolloServer, gql } from 'apollo-server-micro'
type Props = {
_id: string
file: string[]
}
const typeDefs = gql`
type Mutation {
uploadFile(file: [String!]!): Boolean!
}
type Query {
readUpload(_id: String!): Boolean!
}
`
const resolvers = {
Mutation: {
async uploadFile(_: any, { file }: Props) {
console.log(file)
return true
}
},
Query: {
async readUpload(_: any, { _id }: Props) {
}
}
}
const apolloServer = new ApolloServer({
typeDefs,
resolvers
})
export const config = {
api: {
bodyParser: false
}
}
// Ensure to put a slash as the first character to prevent errors.
export default apolloServer.createHandler({ path: '/api/uploads' })
This is the code I am using on the client side.
import { useRef } from 'react'
import { uploadFile } from '../graphql/fetchers/uploads'
import { UPLOAD_FILE_QUERY } from '../graphql/queries/uploads'
export default function Upload() {
const inputElement = useRef<HTMLInputElement>(null)
const submitForm = (event: any) => {
event.preventDefault()
const files = inputElement.current?.files
if (files) {
const fileReader = new FileReader()
fileReader.onload = async () => {
try {
const result = fileReader.result as string
try {
console.log(result)
await uploadFile(UPLOAD_FILE_QUERY, { file: result })
} catch(error) {
console.log(error)
}
} catch(error) {
console.log(error)
}
}
fileReader.readAsDataURL(files[0])
}
}
return (
<form>
<input ref={inputElement} type='file'></input>
<button onClick={(event) => submitForm(event)}>Submit</button>
</form>
)
}
export const config = {
api: {
bodyParser: false
}
}
set bodyParser to true
Set bodyParser size limit
export const config = {
api: {
bodyParser: {
sizeLimit: '4mb' // Set desired value here
}
}
}
you try to send file as string in json? I think you should use multipart/form data on client side and parse them with special middleware on server side
On client special link converts request to multipart/formdata
full example https://github.com/jaydenseric/apollo-upload-examples
import { useMemo } from "react"
import { ApolloClient, createHttpLink, InMemoryCache } from "#apollo/client"
import { setContext } from "#apollo/client/link/context"
import { getUserTokenFromLocalStorage } from "../utils/utils"
import { createUploadLink } from "apollo-upload-client"
let apolloClient
const httpLink = createUploadLink({
uri: "/api/graphql",
headers: {
"keep-alive": "true",
},
})
const authLink = setContext((_, { headers }) => {
let token = getUserTokenFromLocalStorage()
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
}
})
function createIsomorphLink() {
if (typeof window === "undefined") {
const { SchemaLink } = require("#apollo/client/link/schema")
const { schema } = require("./schema")
return new SchemaLink({ schema })
} else {
return authLink.concat(httpLink)
}
}
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === "undefined",
link: createIsomorphLink(),
cache: new InMemoryCache(),
})
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState)
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === "undefined") return _apolloClient
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}