Building a Scalable Full-Stack GraphQL App with Remix and Apollo

Building a Full-Stack GraphQL Application with Remix

What is Remix?

Remix is a full-stack web framework that prioritizes the user interface and delivers a fast, sleek, and resilient user experience. Built on top of React, Remix includes features like server-side rendering, TypeScript support, production server, and backend optimization.

Understanding Remix API Routes

In Remix, routes are their own APIs, allowing components to fetch data on the server side when a user requests a route. This approach differs from other frameworks like Next.js, where the client makes requests to API/server routes to perform CRUD operations.

Introduction to Apollo GraphQL

Apollo is a suite of tools for creating GraphQL servers and consuming GraphQL APIs. We’ll use Apollo’s Schema Link to perform GraphQL operations on a provided schema instead of making network calls to a GraphQL API.

Setting up a Remix Project

To create a new Remix project, run the following command in your terminal:

npx remix-create my-remix-app

Follow the prompts to set up your project.

Apollo GraphQL in Remix

Install the required packages:

npm install @apollo/client graphql-tag

Create a new file app/lib/apollo/index.ts to configure your Apollo client:


import { ApolloClient } from '@apollo/client';
import { makeExecutableSchema } from 'graphql-tools';
import { typeDefs } from './schema';

const graphQLClient = new ApolloClient({
  schema: makeExecutableSchema({ typeDefs }),
});

Setting up Utility Functions

Create a JSON file at app/data/books.json to store your data. Create a new file app/utils/readWrite.ts to set up utility functions for reading and writing to the JSON file:


import fs from 'fs';
import path from 'path';

const filePath = path.join(__dirname, '..', 'data', 'books.json');

export const read = () => {
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};

export const write = (data) => {
  fs.writeFileSync(filePath, JSON.stringify(data));
};

Running Queries

Create a new file app/routes/books.tsx to define your query and execute it using the Apollo client:


import { useLoaderData } from 'emix';
import { graphQLClient } from '~/lib/apollo';

const Books = () => {
  const data = useLoaderData();
  return (
    <div>
      <h1>Books</h1>
      <ul>
        {data.books.map((book) => (
          <li>
            <p>{book.title}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export const loader = async () => {
  const query = `
    query GetBooks {
      books {
        id
        title
      }
    }
  `;
  const result = await graphQLClient.query({ query });
  return result.data;
};

Setting up Mutations

Define your mutation and resolver in app/lib/apollo/schema.ts:


import { gql } from 'graphql-tag';

const typeDefs = gql`
  type Mutation {
    addBook(book: BookInput!): [Book]
  }

  input BookInput {
    title: String!
    author: String!
  }
`;

const resolvers = {
  Mutation: {
    addBook: async (parent, args) => {
      const book = args.book;
      const data = await read();
      data.push(book);
      await write(data);
      return data;
    },
  },
};

Create a new file app/routes/books/addbook.tsx to define your mutation and execute it using the Apollo client:


import { useActionData } from 'emix';
import { graphQLClient } from '~/lib/apollo';

const AddBook = () => {
  const data = useActionData();
  return (
    <div>
      <h1>Add Book</h1>
      <form>
        <label>Title:</label>
        <input type="text" name="title" />
        <br>
        <label>Author:</label>
        <input type="text" name="author" />
        <br>
        <button type="submit">Add</button>
      </form>
      {data && (
        <p>Book added successfully!</p>
      )}
    </div>
  );
};

export const action = async ({ request }) => {
  const formData = await request.formData();
  const title = formData.get('title');
  const author = formData.get('author');
  const query = `
    mutation AddBook($book: BookInput!) {
      addBook(book: $book) {
        id
        title
      }
    }
  `;
  const variables = { book: { title, author } };
  const result = await graphQLClient.mutate({ query, variables });
  return result.data;
};

This is just the beginning of what you can achieve with Remix and GraphQL. You can also create a resource route in Remix to provide a GraphQL endpoint using Apollo Server.

Leave a Reply