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.

Data Analyst (f/m/d)

  • adjoe
  • Playtime Supply
  • Full-time
adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions to meet the diverse needs of app publishers. Being part of the applike group ecosystem plus the significant financial backing from Bertelsmann empowers us to do this. At adjoe, we are proud of our advanced tech stack and dynamic and innovative workforce.

Meet Your Team: Playtime Supply

The Playtime Supply team focuses on creating great experiences for the 200+ million users who interact with Playtime. This includes everything from the gamified experiences within the mobile apps to the dashboards used to configure and customise these experiences. From the unique product features and ML algorithms to the analytical systems used to analyse their effects. Our cross-functional team has all the expertise needed to realise our product goals.
What you will do
  • You will use data-insights to find opportunities for our product to improve and grow.
  • You will be part of an international, English-speaking team that builds our disruptive Ad Tech platform
  • You create, analyze and communicate reports and statistics by using queries and dashboards that our BI team builds.
  • You will participate in building and maintaining models that will be used in order to provide business insights through our dashboard
  • You drill into data from various sources (Redshift, Spark, SQL, Athena, S3, Druid event streams, AWS data pipeline) and write scripts to improve the processes
  • You are a strong communicator,can explain complex things in simple words,and know how to deal with stakeholders
  • You are able to express technical issues comprehensively in business language
  • You have to understand all the different parts of our product to help our product to achieve better results and provide incredible experience to our publishers and end-users.
  • Who you are
  • You have excellent analytical & communication skills and like to use these on Python, Scala or Julia as well as on Spreadsheets
  • You are fluent in English
  • Strong knowledge in at least one programming language that can be used for BI
  • You’re interested in big data and have outstanding experience in SQL
  • Experienced in using Power BI/Tableau/Quicksight or other tools for creating a dashboard for quick analysis.
  • You are self-confident and competent in communicating with clients
  • Plus: You got in touch with java or you build already some basic android apps in your free time
  • Plus: Knowledge about Mobile App analytics (e.g. adjust, appsflyer etc.)
  • Plus: Knowledge about SDK and basic understanding of the SDK integration process.
  • Heard of Our Perks?
  • Work-Life Package: 2 remote days per week, 30 vacation days, 3 weeks per year of remote work, flexible working hours, dog-friendly kick-ass office in the center of the city.
  • Relocation Package: Visa & legal support, relocation bonus, reimbursement of German Classes costs, and more.
  • Happy Belly Package: Monthly company lunch, tons of free snacks and drinks, free breakfast & fresh delicious pastries every Monday
  • Physical & Mental Health Package: In-house gym with a personal trainer, various classes like Yoga with expert teachers & free of charge access to our EAP (Employee Assistance Program) to support your mental health and well-being
  • Activity Package: Regular team and company events, and hackathons.
  • Education Package: Opportunities to boost your professional development with courses and training directly connected to your career goals 
  • Wealth building: virtual stock options for all our regular employees
  • Skip writing cover letters. Tell us about your most passionate personal project, your desired salary and your earliest possible start date. We are looking forward to your application!

    We welcome applications from people who will contribute to the diversity of our company.

    Build our signature product

    See vacancies

    Data Analyst (f/m/d)

    • adjoe
    • Playtime Supply
    • Full-time
    adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions to meet the diverse needs of app publishers. Being part of the applike group ecosystem plus the significant financial backing from Bertelsmann empowers us to do this. At adjoe, we are proud of our advanced tech stack and dynamic and innovative workforce.

    Meet Your Team: Playtime Supply

    The Playtime Supply team focuses on creating great experiences for the 200+ million users who interact with Playtime. This includes everything from the gamified experiences within the mobile apps to the dashboards used to configure and customise these experiences. From the unique product features and ML algorithms to the analytical systems used to analyse their effects. Our cross-functional team has all the expertise needed to realise our product goals.
    What you will do
  • You will use data-insights to find opportunities for our product to improve and grow.
  • You will be part of an international, English-speaking team that builds our disruptive Ad Tech platform
  • You create, analyze and communicate reports and statistics by using queries and dashboards that our BI team builds.
  • You will participate in building and maintaining models that will be used in order to provide business insights through our dashboard
  • You drill into data from various sources (Redshift, Spark, SQL, Athena, S3, Druid event streams, AWS data pipeline) and write scripts to improve the processes
  • You are a strong communicator,can explain complex things in simple words,and know how to deal with stakeholders
  • You are able to express technical issues comprehensively in business language
  • You have to understand all the different parts of our product to help our product to achieve better results and provide incredible experience to our publishers and end-users.
  • Who you are
  • You have excellent analytical & communication skills and like to use these on Python, Scala or Julia as well as on Spreadsheets
  • You are fluent in English
  • Strong knowledge in at least one programming language that can be used for BI
  • You’re interested in big data and have outstanding experience in SQL
  • Experienced in using Power BI/Tableau/Quicksight or other tools for creating a dashboard for quick analysis.
  • You are self-confident and competent in communicating with clients
  • Plus: You got in touch with java or you build already some basic android apps in your free time
  • Plus: Knowledge about Mobile App analytics (e.g. adjust, appsflyer etc.)
  • Plus: Knowledge about SDK and basic understanding of the SDK integration process.
  • Heard of Our Perks?
  • Work-Life Package: 2 remote days per week, 30 vacation days, 3 weeks per year of remote work, flexible working hours, dog-friendly kick-ass office in the center of the city.
  • Relocation Package: Visa & legal support, relocation bonus, reimbursement of German Classes costs, and more.
  • Happy Belly Package: Monthly company lunch, tons of free snacks and drinks, free breakfast & fresh delicious pastries every Monday
  • Physical & Mental Health Package: In-house gym with a personal trainer, various classes like Yoga with expert teachers & free of charge access to our EAP (Employee Assistance Program) to support your mental health and well-being
  • Activity Package: Regular team and company events, and hackathons.
  • Education Package: Opportunities to boost your professional development with courses and training directly connected to your career goals 
  • Wealth building: virtual stock options for all our regular employees
  • Skip writing cover letters. Tell us about your most passionate personal project, your desired salary and your earliest possible start date. We are looking forward to your application!

    We welcome applications from people who will contribute to the diversity of our company.

    Build our signature product

    See vacancies