Fullstack Alibaba Clone with GraphQL, React, and React Native

5/5 - (1 vote)

In this article, we will develop a fullstack e-commerce application called Alibaba Clone that allows users to browse products, shop online and place orders from both web and mobile interfaces.

The app will be built using the GraphQL data query language on the backend, React for building the web UI and React Native for developing the mobile app. By sharing data layer and UI logic, we will see how GraphQL enables creation of unified and scalable fullstack applications.

Setting Up the Project

To get started, we will first initialize our project directories and install the necessary dependencies. We’ll create a root project folder called ‘alibaba-clone’ and inside it two sub-folders – ‘server’ for our GraphQL API and ‘client’ for the React frontend:

mkdir alibaba-clone
cd alibaba-clone
mkdir server client

Inside the server folder, we run npm init to initialize a package.json file:

cd server 
npm init

We’ll install Apollo Server to build our GraphQL API:

npm install apollo-server graphql

Inside the client folder, we initialize a new React app using Create React App:

cd ../client
npx create-react-app .

This sets up the initial folder structure and dependencies we need to get started. Our GraphQL backend is now ready to define the schema, and our React app is setup to develop the frontend interfaces.

Creating the GraphQL Schema

With our dev environment initialized, let’s define the data model for our e-commerce store using GraphQL schema language. We’ll focus on key entity types like Products, Categories, Users etc.

We create a new file called schema.js inside server folder and define the core Product type:

const { gql } = require('apollo-server');

const typeDefs = gql`
  type Product {
    id: ID! 
    name: String!
    description: String
    price: Float!
    image: String
    category: Category! 
  }
`

We add further types like Category, User etc. Products have a required name, price and belonging category. Users have profiles, carts etc.

This schema defines the basic data model and queries/mutations that can be performed. It acts as a contract between frontend and backend.

Setting Up the GraphQL Server

We instantiate Apollo Server and pass our GraphQL schema:

const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');

const server = new ApolloServer({
  typeDefs
});

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});

By default it uses mock resolvers. We connect to MongoDB later using graphql-tools and mongoose.

Import and connect to database:

const mongoose = require('mongoose');

mongoose.connect(MONGO_URL)
  .then(() => console.log('MongoDB Connected'))
  .catch(err => console.log(err));

Define data models:

const productSchema = new mongoose.Schema({
  name: String,
  //...
});

const Product = mongoose.model('Product', productSchema);

Provide resolvers to fetch/modify data:

const resolvers = {
  Query: {
    products: () => Product.find({}) 
  }
}

Our GraphQL server is now configured to translate API requests into MongoDB queries!

Building the React Web App

We initialize Apollo Client inside src/index.js to enable GraphQL requests:

import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloProvider, useQuery } from '@apollo/react-hooks';

const client = new ApolloClient({
  link: ApolloLink.from([
    onError(({ graphQLErrors, networkError }) => {
      if (graphQLErrors)
        graphQLErrors.forEach(({ message, locations, path }) =>
          console.log(
            `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
          )
        );
      if (networkError) console.log(`[Network error]: ${networkError}`);
    }),
    new HttpLink({ uri: 'http://localhost:4000/graphql' })
  ]),
  cache: new InMemoryCache()
});

This allows us to connect to our GraphQL backend and perform queries in React components.

We create a Products component to fetch products data and display listings:

function Products() {
  const { loading, error, data } = useQuery(GET_PRODUCTS);

  if (loading) return <p>Loading...p>;
  if (error) return <p>Error :(p>;

  return data.products.map((product) => (
    <div key={product.id}>
      <img src={product.image} alt={product.name} />
      <p>{product.name}p>
    div> 
  ));
}

This forms the basic product display functionality!

Creating React Components

We build out additional components like ProductDetails, Cart, CategoryFilters etc and integrate them in App.js.

For Product Details, we extract id from params and pass as variable:

function ProductDetails({ match }) {
  const { id } = match.params;

  const { loading, error, data } = useQuery(GET_PRODUCT, {
    variables: { id }
  });

  // rest of component
}

For Cart, we maintain local state and integrate mutations:

function Cart() {
  const [cart, setCart] = useState([]);

  const addToCart = (product) => {
    // add to cart 
  }

  return (
    <div>
      {cart.map(item => (
        <CartItem
          key={item.id}
          item={item}
          removeFromCart={removeFromCart} 
        />  
      ))}
    div>
  )
}

This establishes the basic app layout and UI using React components.

Implementing Authentication

We integrate user authentication with JSON web tokens (JWT).

First install dependencies:

npm install jsonwebtoken bcryptjs express-jwt

Create User model and authentication functions:

// signup 
const signup = async user => {
  // hash password
  // save user
  // return token
}

// login
const login = async credentials => {
  // validate 
  // return token  
}

Protect routes with jwt middleware:

app.get('/profile', jwt({
  secret: process.env.SECRET
}), (req, res) => {
  res.json({ user: req.user })
})

Now only authenticated users can access routes like profile, cart etc. We add login/signup screens in React.

Adding Product CRUD

Only admins should modify products data. We create an Admin screen with form to:

  • Create products
    (mutation with input fields)

  • Update products
    (populate form with EditProduct component)

  • Delete products
    (mutation to remove product)

We define mutations:

mutation CreateProduct($input: CreateProductInput) {
  createProduct(input: $input) {
    id
  }
}

And call them on form submit with variables. Now admins fully manage product catalog.

Integrating Payments

To allow real transactions, we integrate Braintree payments SDK.

First install sdk:

npm install braintree

Create Dropin UI:

const clientToken = await fetchClientToken();

braintree.dropin.create({
  authorization: clientToken
}, container);

On checkout:

const { nonce } = await braintree.dropin.requestPaymentMethod();

const result = await processPayment({
  amount, 
  paymentMethodNonce: nonce
});

We save successful payments, generate orders. Now users can complete purchases through our site!

Building the Mobile App

We create a new React Native project:

npx react-native init Mobile

Import core dependencies:

import { ApolloClient, InMemoryCache } from '@apollo/client';
import { ReactNativeApolloClient } from 'apollo-client-react-native';

Configure Apollo Client identical to web:

const client = new ReactNativeApolloClient({
  uri: API_URL,
  // other config
});

Now we can share queries, mutations and UI logic by extracting them into custom hooks and components.

We create basic screens like Home, Product, Cart etc and render web components on mobile. By sharing GraphQL implementation, web and mobile are now truly unified!

Styling the Mobile UI

To give our app a native look, we add platform-specific styling.

For iOS styling, we install and configure React Native Elements:

npm install react-native-elements

Import common components and styles:

import { Card, ListItem } from 'react-native-elements';

function HomeScreen() {
  return (
    <Card>
      <ListItem title="iPhone Xs"/>
    Card> 
  )
}

For Android styling, we use Material UI components:

import { Card, ListItem } from '@material-ui/core';

function configureTheme() {
  //..
}

function HomeScreen() {
  return (
    <CardThemeProvider theme={theme}>  
      <Card>
        <ListItem button>iPhone XsListItem>  
      Card>
    CardThemeProvider>
  )
}

We also customize colors, fonts globally using react-native-paper/primitives. This makes our app feel truly native on mobile platforms.

Adding Location Features

Mobile e-commerce apps commonly have location services. We integrate the following:

  • Geocoding: Convert addresses to lat/long using Google Maps API:

const location = await googleMaps.geocode({address: '1600 Amphitheatre Parkway, Mountain View, CA'});
```

Nearby Search: Find stores around user’s location

const stores = await googleMaps.nearbySearch({
  location: userLocation, 
  radius: 1000  
});
```

Dynamic Delivery Fees: Calculate delivery cost based on distance

const deliveryFee = getDeliveryFee(
  userLocation, 
  storeLocation  
);
```

Store Locations: Display stores on maps

    latitude: store.latitude,
    longitude: store.longitude,
    latitudeDelta: 0.0922,
    longitudeDelta: 0.0421,
  }}
/>
```

This greatly enhances user experience on mobile.

Testing and Deployment

No project is complete without testing and deployment. For testing, we setup:

  • Unit Tests: Test components in isolation using Jest

test('renders learn react link', () => {
  //..
}); 
```

Integration Tests: Test API and UI interactions using Cypress

it('adds product to cart', () => {
 // select product
 // click add to cart  
 // assert cart updated
});
```

For continuous integration, we configure Travis CI to run tests on commits.

We deploy the GraphQL API to Heroku and React app to Netlify.

To ensure high availability, we use services like AWS for API hosting, Cloudfront CDN and DynamoDB.

Finally, proper error handling, analytics, caching are also important for productionization.

Conclusion and Future Work

In conclusion, we built a fully functioning e-commerce application with all major features – products, cart, payments, auth, admin – accessible through web and mobile interfaces thanks to GraphQL + React stack.

Some ideas for future work could be:

  • Seller functionality – onboarding, inventory management
  • Shipping/Logistics integrations
  • Loyalty programs
  • Order tracking/support
  • Notifications, push, email marketing
  • Related/recommended products
  • Product reviews/ratings
  • Checkout improvements – one click, saved details
  • Internationalization – multiple languages

This demonstrates how GraphQL enables rapid development of complex Fullstack applications that can scale. I hope this article provided a good overview of the technical development process.

Prasad Venkatachalam

Prasad Venkatachalam is a professional writer with over 10 years of expertise in web and mobile app development. With a solid background in the field, Prasad has accumulated a decade of experience, honing his skills and staying up-to-date with the latest trends and technologies. His extensive experience in software development allows him to navigate the intricacies of the process, ensuring efficient and high-quality solutions. Currently, Prasad is a valuable member of the Zipprr team, where he continues to contribute his 10 years of expertise to develop innovative on-demand solutions.