In a lot of teams, there is a frontend developer and a backend developer working separately on their tasks. Frontend developer diligently working on the UI implementation (perhaps using AI tools to create the components and screens as well) perfecting it in StoryBook and then polishing it with animations and making things seamless to use in general.
In the modern tech landscape, creating APIs generally boils down to implementing the business logic and edge cases. Most general APIs can be generated from your data models and permission schema and things like authentication are provided by libraries. That being said, your backend modules are generally re-usable.
But when it comes to integrating APIs the traditional approach is to get the API schema either in Postman or Swagger and integrate screen by screen.
But what if you could “pre-integrate” APIs and then plug and play into the frontend of your choice?
This approach works best with MobX based state management architecture but can also be implemented with Redux architectures (caveat is the placement of dispatch actions, slicing and middleware code)
Pre-integration using JSON objects
To “pre-integrate” APIs, you need to first ensure that when implementing UIs any data-related texts should be from a JSON object in your UI and utilize dummy functions to simulate method calls.
This approach is very beneficial as it keeps your code data driven while not bogging you down from your other frontend tasks.
//screens/UserScreen.js import React, { useState, useEffect } from 'react'; // Simulated JSON data with methods const userStore = { userList: [ { id: 1, name: "John Doe", age: 30, greet: function () { return `Hello, I'm ${this.name} and I am ${this.age} years old.`); } }, { id: 2, name: "Jane Smith", age: 25, greet: function () { console.log(`Hi, I'm ${this.name} and I am ${this.age} years old.`); } } ], //simulate API Call fetchUsers: () => { return new Promise((resolve) => { setTimeout(() => { resolve(userStore.userList); // Simulate fetching JSON data }, 1000); // Simulate delay }); } } // Main React component const UserComponent = () => { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { // Fetch data from simulated JSON userStore.fetchUsers().then((data) => { setLoading(false); }); }, []); if (loading) { return <div>Loading...</div>; } return ( <div> <h1>User List</h1> <ul> {userStore.userList.map((user) => ( <li key={user.id}> {user.name} ({user.age} years old) <button onClick={() => user.greet()}>Greet</button> </li> ))} </ul> </div> ); }; export default UserComponent;
The structure of JSON should be ideally be defined as per the model schema created in the architecture phase, even if the structure changes backend and frontend can independently update their relevant code base.
Now for the actual API “pre-integration” part, we will follow the approach used by Ignite CLI for React Native (can easily be implemented for React as well).
First, some boilerplating:
/* Project Structure /app /models /user user-model.js // User model with actions and flow user-store.js // UserStore with fetch logic /screens UserScreen.js // React Native screen that uses the MobX store /stores root-store.js // Root store combining all stores setup-root-store.js // Function to set up the root store */ //app/stores/root-store.js import { types } from "mobx-state-tree"; import UserStore from "../models/user/user-store"; // RootStore combining all stores (for now only UserStore) const RootStore = types.model({ userStore: UserStore, }); //app/stores/setup-root-store.js export default RootStore; import { RootStore } from "./root-store"; export const setupRootStore = () => { const rootStore = RootStore.create({ userStore: { users: [], loading: false, error: null, }, }); return rootStore; }; //app.js or app .tsx import React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { setupRootStore } from './stores/setup-root-store'; import { Provider } from 'mobx-react-lite'; import UserScreen from './screens/UserScreen'; const App = () => { const rootStore = setupRootStore(); return ( <Provider value={rootStore}> <NavigationContainer> <UserScreen /> </NavigationContainer> </Provider> ); }; export default App;
Store Module
Now that's setup we can create our store, let's first define our data model.
//app/models/user/user-model.js import { types } from "mobx-state-tree"; // Define the User model with a greet action const User = types .model({ id: types.identifierNumber, name: types.string, age: types.number, }) .actions((self) => ({ greet() { console.log(`Hello, I'm ${self.name} and I am ${self.age} years old.`); }, })); export default User;
Now define the actual store which we will be replacing the dummy userStore defined earlier.
//app/models/user/user-store.js import { types, flow } from "mobx-state-tree"; import User from "./user-model"; // UserStore to handle the state and data fetching const UserStore = types .model({ users: types.array(User), // List of User models loading: types.boolean, error: types.maybeNull(types.string), }) .actions((self) => ({ // flow for handling async API calls fetchUsers: flow(function* () { self.loading = true; try { // Fetch real data from an external API const response = yield fetch('https://your-server-here/users'); const data = yield response.data; self.users= User.create(data) } catch (error) { self.error = "Failed to fetch users."; } finally { self.loading = false; } }), })); export default UserStore;
Plug and Play!
Now simply plug and play your pre-integrated module by replacing the userStore JSON with the actual userStore.
//screens/UserScreen.js import React, { useState, useEffect } from 'react'; //JSON data deleted // Main React component const UserComponent = () => { const {userStore}=useStores() const [loading, setLoading] = useState(true); useEffect(() => { // Fetch data from simulated JSON userStore.fetchUsers().then((data) => { setLoading(false); }); }, []); if (loading) { return <div>Loading...</div>; } return ( <div> <h1>User List</h1> <ul> {userStore.userList.map((user) => ( <li key={user.id}> {user.name} ({user.age} years old) <button onClick={() => user.greet()}>Greet</button> </li> ))} </ul> </div> ); }; export default UserComponent;
The userStore can be developed independently as well and even easily by a backend developer (often backend leads frontend) improving your efficiency.
Enjoy efficient coding!