From a WordPress Site to a Blazing-Fast Flutter App : REST API vs GraphQL

WordPress Screenshot

We started with a goal that many e-commerce store owners share: to take our successful WordPress and WooCommerce website and build a beautiful, native mobile app for our customers. We chose Flutter, a powerful framework for building apps on both iOS and Android from a single codebase.

Our initial version was a success—it worked! But we quickly ran into a common challenge: how do we make the app feel fast and smooth, and, most importantly, how do we stop it from breaking every time we update a plugin on our WordPress site?

This is the story of our journey—analysing our app's architecture and discovering the modern, future-proof approach to make it faster, more reliable, and a joy to maintain.


Our Starting Point: A Look at Our Project's DNA

Before we dive into the changes, let's look at the foundation we built. Our app, caliber_app, has a very professional and well-organized structure. Understanding this is key to seeing why we're making these upgrades.

  • lib/blocs/: The "brain" of our app. Each feature has its own Business Logic Component (BLoC): ProductBloc, CartBloc, AuthenticationBloc, and more. This keeps our logic clean and organized.
  • lib/domain/: The heart of our application, containing core business rules.
    • repositories/: Data managers that decide whether to fetch data from the internet (remote) or local database.
    • models/: Blueprints for our data (Product, Store, User), built with freezed to prevent bugs.
    • data/: Our storage rooms — e.g., local_data_source.dart (Hive database) and remote_data_source (CaliberApi).
  • lib/network/: Communications layer. Using Dio and Retrofit in caliber_api.dart to talk to WordPress.
  • lib/screens/: Everything the user sees — UI organized by feature (home, cart, profile, etc.).

The Challenge: The Traditional REST API Approach

Our app was built on the standard WordPress REST API. To load our home screen (home_body.dart), the app had to make many separate network requests:

  1. Banners (BannerBlocBloc)
  2. Promos (PromoBloc)
  3. New arrivals & featured products (ProductBloc)
  4. Stores (StoreBloc)
  5. Blogs (BlogsBloc)

This is a "chatty" API with two major drawbacks:

  • Slow: Multiple round trips make loading screens feel sluggish.
  • Brittle: Changes in plugins (like Dokan or banners) could break the app, making it fragile and high-maintenance.

The Solution: Embracing a Headless Approach with GraphQL 🚀

We upgraded to a modern Headless CMS approach using GraphQL. Instead of many endpoints, GraphQL gives us one powerful endpoint. Our Flutter app can now fetch all data for a screen in a single query.

This solves:

  • Speed: One request is much faster than five.
  • Durability: The app isn’t tied to plugin-specific endpoints. If a plugin changes, only the server logic needs updating.

Our 3-Step Action Plan to Go Headless

Step 1: Prep Our WordPress Backend

Install and activate these free plugins:

  1. WPGraphQL: Creates the /graphql endpoint.
  2. WooGraphQL: Exposes WooCommerce products, variations, and cart data.
  3. Dokan GraphQL Extension: For store data (custom or community-built).

Step 2: Equip Our Flutter App

Update Flutter to speak GraphQL:

  1. Add graphql_flutter in pubspec.yaml.
  2. Configure the client in dependency_injection.dart:

// in dependency_injection.dart
final HttpLink httpLink = HttpLink('https://calibershoes.com/graphql');

final client = GraphQLClient(
  link: httpLink,
  cache: GraphQLCache(store: HiveStore()),
);

inject.registerLazySingleton<GraphQLClient>(() => client);

Step 3: Rewrite Our Home Screen Logic

One GraphQL query replaces 5+ REST calls:


query GetHomeScreenData {
  newArrivals: products(first: 5, where: {orderby: {field: DATE, order: DESC}}) {
    nodes { id, name, price, images { nodes { sourceUrl } } }
  }
  bestOffers: products(first: 5, where: {featured: true}) {
    nodes { id, name, price, images { nodes { sourceUrl } } }
  }
  stores(first: 5) {
    nodes { id, storeName, bannerImage }
  }
  blogs: posts(first: 4) {
    nodes { id, title, date, featuredImage { node { sourceUrl } } }
  }
}

We then replace multiple BLoCs with a single HomeBloc managing this query, simplifying home_body.dart.

BEFORE:

// home_body.dart
NewArrival(),
CaliberOutlets(),
BlogsScreen(),
AFTER:

// home_body.dart
BlocBuilder<HomeBloc, HomeState>(
  builder: (context, state) {
    if (state.isLoading) return LoadingShimmers();
    if (state.hasData) {
      return Column(
        children: [
          NewArrivalWidget(products: state.newArrivals),
          CaliberOutletsWidget(stores: state.stores),
          BlogsWidget(blogs: state.blogs),
        ],
      );
    }
  }
)

Our Path Forward

By embracing a headless architecture with GraphQL, we’ve made our app faster, more durable, and easier to maintain. What was once a fragile bridge is now a modern, future-proof superhighway ready for whatever changes WordPress or WooCommerce bring.

Post a Comment

Previous Post Next Post