adjoe Engineers’ Blog
 /  Frontend  /  RTK Query & Mock Service Worker
decorative green and navy animation of swirls with code embedded
Frontend

RTK Query and Leveraging New Libraries

At adjoe, we use RTK Query for several reasons. It can simplify state management, reduce boilerplate code, improve type safety and middleware integration, and enhance the overall development experience.

Besides RTK Query, our team uses Mock Service Worker (mswjs), which offers significant benefits by simplifying and enhancing the testing and development process for web applications that interact with APIs. It has several benefits including mocking API responses and isolating FE and BE development.These are my best practices for the RTK Query and Mock Service Worker libraries.

No Writing Data-Fetching and Caching Logic Yourself

RTK Query provides me with different hooks that can easily be reused for the logic of my component and fetching its data. And besides that, with RTK Query, I don’t need to

  • write caching mechanisms from scratch
  • spend time and energy writing the code myself – we don’t need to write some logic from scratch, as this library provides us with some custom hooks

Since implementing this advanced data-fetching and caching tool, I have so far been able to reduce time spent writing code by around 70 percent. That’s 70 percent less time writing or worrying about caching or fetching – just from implementing and running RTK Query on production for around three months. 

Cleaner Code and Cache Mutations

Let’s say I want to display a list of todos in adjoe’s application. I need to fetch them from the API. To do this, I can use a React app using TypeScript and start a new project by running the command below in terminal.

npx create-react-app todos --template typescript

1. Once the demo project is ready, I need to install some dependencies.

npm install redux react-redux
npm install @reduxjs/toolkit

2. I define the “Todo” type (if I’m using TypeScript in the application): src/types.d.ts.

type Todo = {
 userId: number;
 id: number;
 title: string;
 completed: boolean;
};

3. Now, I’m ready to create an API slice: src/api/apiSlice.ts.

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
export const apiSlice = createApi({
 reducerPath: "api",
 baseQuery: fetchBaseQuery({
   baseUrl: "https://jsonplaceholder.typicode.com",
 }),
 endpoints: (builder) => ({
   getTodos: builder.query<Todo[], void>({
     query: () => "/todos",
     transformResponse: (response: Todo[]) => {
       let filtered = response.filter((r) => r.id <= 10);
       return filtered.sort((a, b) => b.id - a.id);
     },
   }),
 }),
});
 
// custom hooks based on the methods that we provide
export const {
 useGetTodosQuery,
} = apiSlice;

4. After this, I need to wrap the application component with “ApiProvider”: src/index.tsx.

import { ApiProvider } from "@reduxjs/toolkit/query/react";
import { apiSlice } from "./api/apiSlice";
root.render(
 <React.StrictMode>
   <ApiProvider api={apiSlice}>
     <App />
   </ApiProvider>
 </React.StrictMode>
);

5. Then I can use the custom hook created by RTK Query in Todos.tsx component src/Todos.tsx.

import { useGetTodosQuery } from "../api/apiSlice";


export const Todos = () => {
 const { data: todos, isError, isFetching, error } = useGetTodosQuery();


 if (isFetching) return <p>Loading...</p>;
 if (isError) return <p>{JSON.stringify(error)}</p>;
 return (
   <>
     {todos?.map((todo) => (
       <p key={todo.id}>
         {todo.title}
       </p>
     ))}
   </>
 );
};

Utilize Custom Hooks for CRUD Operations

I can also add API calls for add, delete, and update operations and utilize custom hooks available from RTK Query. I can add code below to the API slice endpoints object src/api/apiSlice.ts.

  addTodo: builder.mutation({
     query: (todo: Todo) => ({
       url: "/todos",
       method: "POST",
       body: todo,
     }),
   }),
   updateTodo: builder.mutation({
     query: (todo: Todo) => ({
       url: `todos/${todo.id}`,
       method: "PUT",
       body: todo,
     }),
   }),
   deleteTodo: builder.mutation({
     query: (id: number) => ({
       url: `/todos/${id}`,
       method: "DELETE",
     }),
   }),
export const {
 useAddTodoMutation,
 useDeleteTodoMutation,
 useUpdateTodoMutation
} = apiSlice;

src/Todo.tsx

 const [addTodo] = useAddTodoMutation();
 const [deleteTodo] = useDeleteTodoMutation();
   const [updateTodo] = useUpdateTodoMutation();
 deleteTodo(todo.id);
 addTodo({id: 201, userId: 10, title: 'new todo is added', completed: false});
   updateTodo({ id: 193, userId: 10, title: "my first todo item", completed: true });

Assigning a Tag to the Cache

In this process, results get cached, and I don‘t invalidate the previous cache. This means the component doesn’t display the new changes upon adding, deleting, or updating a todo item. To resolve this issue, I can assign a tag to the cache to let it know which mutation invalidates the cache to ensure it automatically refetches that data.

src/api/apiSlice.ts

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
export const apiSlice = createApi({
 reducerPath: "api",
 baseQuery: fetchBaseQuery({
   baseUrl: "https://jsonplaceholder.typicode.com",,
 }),
 tagTypes: ["Todos"], //defining tags' type
 endpoints: (builder) => ({
   getTodos: builder.query<Todo[], void>({
     query: () => "/todos",
     transformResponse: (response: Todo[]) => {
       let copy = response.filter((r) => r.id <= 10);
       return copy.sort((a, b) => b.id - a.id);
     },
     providesTags: ["Todos"], // defining a tag for this call
   }),
   addTodo: builder.mutation({
     query: (todo: Todo) => ({
       url: "/todos",
       method: "POST",
       body: todo,
     }),
     invalidatesTags: ["Todos"], // invalidate this tag for this mutation so that data can automatically re-fetched
   }),
   updateTodo: builder.mutation({
     query: (todo: Todo) => ({
       url: `todos/${todo.id}`,
       method: "PUT",
       body: todo,
     }),
     invalidatesTags: ["Todos"], // invalidate this tag for this mutation so that data can automatically re-fetched
   }),
   deleteTodo: builder.mutation({
     query: (id: number) => ({
       url: `/todos/${id}`,
       method: "DELETE",
     }),
     invalidatesTags: ["Todos"], // invalidate this tag for this mutation so that data can automatically re-fetched
   }),
 }),
});
 
// custom hooks based on the methods that we provide
export const {
 useGetTodosQuery,
 useAddTodoMutation,
 useDeleteTodoMutation,
 useUpdateTodoMutation,
} = apiSlice;

Besides eliminating the need to hand-write code yourself, the tool has more tricks up its sleeve to further optimize development processes. 

I have ideas on how to further leverage RTK Query by use case. When the frequency of data change is high (for example, one second or less than 30 seconds), and the application should show data instantly; it should not use the cached data. However, when the data frequency is less and the response object is big (for example, more than 1MB), the team can enable caching. This enables a better user experience and also reduces the burden on adjoe’s servers.

mswjs: API Mocking for Testing, Development, and Debugging

In an ecosystem where there is little documentation on testing RTK Query hooks, our frontend team found the mswjs library while researching what other React developers are utilizing.

We decided to use mswjs for two reasons. 1) To use mock data and develop new features in the frontend when the endpoint is not ready on the server side and 2) to write unit tests for components using RTK to fetch data since it intercepts requests on the network level. When it comes to Mock Service Worker, you should know the following:

  • The library is designed to intercept requests on the network level, and mock data helps write the test for RTK Query to test the API and components.
  • This way, we don’t need to call the API on the server, and mocking is completely seamless.

Test Your Component in mswjs 

When I want to test a component that fetches data from an adjoe API with the mswjs library and mock the API response, I need to install the necessary dependencies.

npm install msw --save-dev

2. Then I can define the mocks: src/test/mocks/todoMocks.ts.

export const todoMocks: Todo[] = [
 {
   id: 1,
   userId: 10,
   title:
     "temporibus atque distinctio omnis eius impedit tempore molestias pariatur",
   completed: true,
 },
 {
   id: 2,
   userId: 10,
   title: "ut quas possimus exercitationem sint voluptates",
   completed: false,
 },
 {
   id: 3,
   userId: 10,
   title: "rerum debitis voluptatem qui eveniet tempora distinctio a",
   completed: false,
 },
 {
   id: 4,
   userId: 10,
   title: "sed ut vero sit molestiae",
   completed: true,
 },
];

src/test/mocks/handlers.ts

import { rest } from "msw";
import { todoMocks } from "./todoMocks";
 
export const handlers = [
 rest.get("https://jsonplaceholder.typicode.com/todos", (_, res, ctx) =>
   res(ctx.status(200), ctx.json<Todo[]>(todoMocks))
 ),
];

3. Afterward, I set up the server: src/test/server.ts.

import { setupServer } from "msw/node";
import { handlers } from "./handlers";
 
export const server = setupServer(...handlers);

4. Finally, I can implement the unit test: src/Todos.spec.tsx.

import { screen, waitFor, render } from "@testing-library/react";
import { ApiProvider } from "@reduxjs/toolkit/query/react";
import { server } from "../test/server";
import { Todos } from "./Todos";
import { apiSlice } from "../api/apiSlice";
 
describe("Todos", () => {
 beforeAll(() => {
   server.listen();
 });
 
 afterEach(() => {
   server.resetHandlers();
 });
 
 afterAll(() => {
   server.close();
 });
 
 it("should display todos", async () => {
   render(
     <ApiProvider api={apiSlice}>
       <Todos />
     </ApiProvider>
   );
 
   await waitFor(() => {
     expect(screen.getByText("sed ut vero sit molestiae")).toBeInTheDocument();
   });
 });
});

At adjoe, we will be able to develop mswjs incrementally in order to improve the unit tests of components that make API calls by defining mocks at the network level. We can also seamlessly reuse the same mock definition for testing, development, and debugging.

Quality Code and Faster Frontend Processes

Using RTK and mswjs in combination with each other streamlines development, we enhance code quality and promote efficient testing. This results in a more robust and faster development process ​​without having to hand-write code ourselves.

With the RTK caching mechanism, you can improve behind-the-scenes performance when dealing with large amounts of daily user data and decrease costs when requesting data from the BE. With mswjs it is possible to isolate FE and BE development, ensuring consistent testing and reducing test execution time.

Technical Account Manager (Boston) (f/m/d)

  • adjoe
  • Playtime Supply
  • Full-time
Every great app out there deserves to be connected with the right users and the right revenue streams. And adjoe will give this to them. adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions that take its app partners’ business to the next level. Our unique ad unit Playtime has made us one of the fastest-growing ad platforms and top-ranking user acquisition sources for app publishers worldwide (ranked #1 for growth in the AppsFlyer Index).

Home to an advanced tech stack, powerful financial backing from Bertelsmann, and a long-term growth mindset, adjoe is part of the AppLike Group ecosystem. A hub of disruption and thought leadership in the app economy, with a driven and dynamic workforce to be reckoned with. 

Our US HQ launched in the beginning of 2023. Since its inception the US office has played a critical role in adjoe’s massive year-over-year growth, and we’ve continued to grow our investment in the US as a key source of future growth. Be the next tech-driven, ambitious talent to join our growing US team!

Meet Your Team: Playtime Supply

Playtime Supply is building a unique software solution for app and game developers that puts users’ needs first. Instead of traditional monetization mechanisms, Playtime builds an engaging ad experience in which users can play and earn rewards for various new mobile games in Playtime. To predict which mobile game will best suit a user, the team has introduced ML algorithms.

To build this rewarded experience, adjoe’s backend reliably handles more than 200 million users – remaining stable despite impressive growth in the user base. And imagine this: adjoe’s Golang event consumer applications assign rewards equivalent to 700 months of time spent in Playtime every day to its users. 

The software depends on two main components.
1) adjoe’s Android mobile SDK – written in a combination of Java and Kotlin – which can be bundled by developers in their apps. 
2) The team’s highly scalable Golang backend, which is hosted on AWS and is otherwise responsible for developing a modern React (TypeScript) dashboard that provides adjoe’s clients and Business team colleagues with fast-loading insights.
What You Will Do:
  • Manage the technical onboarding of our newest app partners and ensure a smooth implementation process. 
  • Become the link between various stakeholders, teams, and clients to provide clients with solutions to maximize their business goals. 
  • Assist the supply business team by providing them with data-driven insights into how they can reach their KPIs based on the analysis, aggregation, and visualization of adjoe’s data. 
  • Put on your detective hat and support publishers with issues they may face when integrating adjoe, ranging all the way from basic issues in their mobile app code to fine-tuning configurations.
  • Proactively identify integration issues that publishers may face and work with the product team to create solutions for these issues.
  • Contribute to maintaining easy-to-understand documentation of the integration process.
  • Be part of an international English-speaking team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • You have a skill for communicating complex technical information to business teams and partners. 
  • You are able to turn data into clear and actionable insights and document these in understandable spreadsheets or wiki pages to drive business growth. 
  • You thrive in a team environment and are an excellent cross-team communicator.
  • You have excellent analytical and communication skills and can use SQL to query databases.
  • Basic Knowledge of HTML and JavaScript & you know you don’t eat cookies in IT.
  • You have a working knowledge of how different technical systems communicate with each other, such as being able to test HTTP REST endpoints and understand what could go wrong when they don’t work.
  • Plus: You are familiar with Java or have experience building your own apps.
  • Build our signature product

    See vacancies

    Technical Account Manager (Boston) (f/m/d)

    • adjoe
    • Playtime Supply
    • Full-time
    Every great app out there deserves to be connected with the right users and the right revenue streams. And adjoe will give this to them. adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions that take its app partners’ business to the next level. Our unique ad unit Playtime has made us one of the fastest-growing ad platforms and top-ranking user acquisition sources for app publishers worldwide (ranked #1 for growth in the AppsFlyer Index).

    Home to an advanced tech stack, powerful financial backing from Bertelsmann, and a long-term growth mindset, adjoe is part of the AppLike Group ecosystem. A hub of disruption and thought leadership in the app economy, with a driven and dynamic workforce to be reckoned with. 

    Our US HQ launched in the beginning of 2023. Since its inception the US office has played a critical role in adjoe’s massive year-over-year growth, and we’ve continued to grow our investment in the US as a key source of future growth. Be the next tech-driven, ambitious talent to join our growing US team!

    Meet Your Team: Playtime Supply

    Playtime Supply is building a unique software solution for app and game developers that puts users’ needs first. Instead of traditional monetization mechanisms, Playtime builds an engaging ad experience in which users can play and earn rewards for various new mobile games in Playtime. To predict which mobile game will best suit a user, the team has introduced ML algorithms.

    To build this rewarded experience, adjoe’s backend reliably handles more than 200 million users – remaining stable despite impressive growth in the user base. And imagine this: adjoe’s Golang event consumer applications assign rewards equivalent to 700 months of time spent in Playtime every day to its users. 

    The software depends on two main components.
    1) adjoe’s Android mobile SDK – written in a combination of Java and Kotlin – which can be bundled by developers in their apps. 
    2) The team’s highly scalable Golang backend, which is hosted on AWS and is otherwise responsible for developing a modern React (TypeScript) dashboard that provides adjoe’s clients and Business team colleagues with fast-loading insights.
    What You Will Do:
  • Manage the technical onboarding of our newest app partners and ensure a smooth implementation process. 
  • Become the link between various stakeholders, teams, and clients to provide clients with solutions to maximize their business goals. 
  • Assist the supply business team by providing them with data-driven insights into how they can reach their KPIs based on the analysis, aggregation, and visualization of adjoe’s data. 
  • Put on your detective hat and support publishers with issues they may face when integrating adjoe, ranging all the way from basic issues in their mobile app code to fine-tuning configurations.
  • Proactively identify integration issues that publishers may face and work with the product team to create solutions for these issues.
  • Contribute to maintaining easy-to-understand documentation of the integration process.
  • Be part of an international English-speaking team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • You have a skill for communicating complex technical information to business teams and partners. 
  • You are able to turn data into clear and actionable insights and document these in understandable spreadsheets or wiki pages to drive business growth. 
  • You thrive in a team environment and are an excellent cross-team communicator.
  • You have excellent analytical and communication skills and can use SQL to query databases.
  • Basic Knowledge of HTML and JavaScript & you know you don’t eat cookies in IT.
  • You have a working knowledge of how different technical systems communicate with each other, such as being able to test HTTP REST endpoints and understand what could go wrong when they don’t work.
  • Plus: You are familiar with Java or have experience building your own apps.
  • Build our signature product

    See vacancies