Skip to main content

GraphQL Integration with Apollo Client

Setup and Configuration

1. Installation

First, install the required dependencies:

npm install @apollo/client graphql

2. Apollo Client Setup

// app/lib/apollo.ts
import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

// HTTP connection to the API
const httpLink = createHttpLink({
uri: process.env.GRAPHQL_API_URL,
});

// Auth middleware
const authLink = setContext((_, { headers }) => {
const token = getAuthToken(); // Implement this function
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});

// Apollo Client instance
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
machinery: {
merge: true,
},
},
},
},
}),
});

Query Management

1. Query Definitions

// app/graphql/queries/machinery.ts
import { gql } from "@apollo/client";

export const GET_MACHINERY = gql`
query GetMachinery($id: ID!) {
machinery(id: $id) {
id
name
type
hourlyRate
availability
features {
id
name
}
owner {
id
name
}
}
}
`;

export const SEARCH_MACHINERY = gql`
query SearchMachinery($input: MachinerySearchInput!) {
searchMachinery(input: $input) {
id
name
type
hourlyRate
}
}
`;

2. Using Queries in Components

// app/components/machinery/MachineryList.tsx
import { useQuery } from "@apollo/client";
import { SEARCH_MACHINERY } from "~/graphql/queries/machinery";

export function MachineryList({
searchParams,
}: {
searchParams: SearchParams;
}) {
const { loading, error, data } = useQuery(SEARCH_MACHINERY, {
variables: { input: searchParams },
});

if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;

return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{data.searchMachinery.map((machinery) => (
<MachineryCard key={machinery.id} machinery={machinery} />
))}
</div>
);
}

Mutation Handling

1. Mutation Definitions

// app/graphql/mutations/booking.ts
import { gql } from "@apollo/client";

export const CREATE_BOOKING = gql`
mutation CreateBooking($input: CreateBookingInput!) {
createBooking(input: $input) {
booking {
id
startTime
endTime
totalCost
machinery {
id
name
}
}
success
message
}
}
`;

2. Using Mutations

// app/components/booking/BookingForm.tsx
import { useMutation } from "@apollo/client";
import { CREATE_BOOKING } from "~/graphql/mutations/booking";

export function BookingForm({ machineryId }: { machineryId: string }) {
const [createBooking, { loading, error }] = useMutation(CREATE_BOOKING);

const handleSubmit = async (formData: FormData) => {
try {
const result = await createBooking({
variables: {
input: {
machineryId,
startTime: formData.get("startTime"),
endTime: formData.get("endTime"),
},
},
});

if (result.data.createBooking.success) {
// Handle success
}
} catch (err) {
// Handle error
}
};

return (
<Form method="post" onSubmit={handleSubmit}>
{/* Form fields */}
</Form>
);
}

Cache Management

1. Cache Configuration

const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
searchMachinery: {
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
},
},
},
},
});

2. Cache Updates

const [createBooking] = useMutation(CREATE_BOOKING, {
update(cache, { data: { createBooking } }) {
cache.modify({
fields: {
bookings(existingBookings = []) {
const newBookingRef = cache.writeFragment({
data: createBooking.booking,
fragment: gql`
fragment NewBooking on Booking {
id
startTime
endTime
}
`,
});
return [...existingBookings, newBookingRef];
},
},
});
},
});

Error Handling

1. Error Policies

const { data, error } = useQuery(GET_MACHINERY, {
errorPolicy: "all",
// Options: 'none' | 'ignore' | 'all'
});

2. Error Components

function ErrorMessage({ error }: { error: ApolloError }) {
return (
<div className="error-message">
<h3>Error</h3>
<p>{error.message}</p>
{error.graphQLErrors.map((error, index) => (
<p key={index}>{error.message}</p>
))}
</div>
);
}

Loading States

1. Loading Components

function LoadingState() {