/  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.

Client Solutions Lead (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). And that’s just the start: We’ve just launched our programmatic in-app platform in order to disrupt the status quo of mobile ad monetization. Again. 

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. 

Be the next tech-driven, ambitious talent to join our growing teams!

Meet Your Team: Client Solutions

Big ambitions lead to a big impact – and big revenue numbers. 

Your role will be to lead and build a Global Customer Service Organization that services both our business partners as well as our B2C customers. You will be reporting into the Director of Technology – Playtime Product and align with your peers in New Business and Account Management in order to provide the best technical support and service to our publisher partners and advertisers. 
What You Will Do:
  • Manage a team of Technical Account Managers. They ensure the technical onboarding of our newest app partners and a smooth SDK implementation process. 
  • Manage a team of B2C support agents. They are problem solvers for our mobile gamers, who have technical issues related to our partners’ games.
  • Become the link between various stakeholders, teams, and clients to provide solutions to maximize their business goals. 
  • Evolve our service organization based on our geographical expansion as well as service and organizational needs arising from growing business with Enterprise clients. 
  • Ensure a consistent level of knowledge and quality in the execution of your teams.
  • Collaborate with the product and technical teams to ensure our platform meets client needs and provides a positive user experience.
  • Ensure up-to-date product and technical documentation in alignment with our product team. 
  • Be part of an international English-speaking management team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • Minimum 3 years of experience in a leadership role within a service organization, preferably in the (mobile) ad tech industry.
  • Proven track record of building and managing high-performing teams.
  • Strong analytical and problem-solving skills with the ability to work with data –  use SQL to query databases, Excel and other analytics tools
  • Ability to build and maintain strong relationships with clients and colleagues.
  • You thrive in a team environment and are an excellent cross-team collaborator with excellent analytical and communication skills
  • Basic Knowledge of HTML and JavaScript or one basic programming or scripting language 
  • 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/Kotlin/Swift or have experience building your own apps.
  • Plus: Understanding of the mobile ad tech landscape and current best practices.
  • 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.
  • Wealth building: virtual stock options for all our regular employees.
  • 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 personal trainer, various classes like Yoga with expert teachers.
  • Activity Package: Regular team and company events, hackathons.
  • Education Package: Opportunities to boost your professional development with courses and trainings directly connected to your career goals 
  • Free of charge access to our EAP (Employee Assistance Program) which is a counseling service designed to support your mental health and well-being.
  • Build our signature product

    See vacancies

    Client Solutions Lead (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). And that’s just the start: We’ve just launched our programmatic in-app platform in order to disrupt the status quo of mobile ad monetization. Again. 

    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. 

    Be the next tech-driven, ambitious talent to join our growing teams!

    Meet Your Team: Client Solutions

    Big ambitions lead to a big impact – and big revenue numbers. 

    Your role will be to lead and build a Global Customer Service Organization that services both our business partners as well as our B2C customers. You will be reporting into the Director of Technology – Playtime Product and align with your peers in New Business and Account Management in order to provide the best technical support and service to our publisher partners and advertisers. 
    What You Will Do:
  • Manage a team of Technical Account Managers. They ensure the technical onboarding of our newest app partners and a smooth SDK implementation process. 
  • Manage a team of B2C support agents. They are problem solvers for our mobile gamers, who have technical issues related to our partners’ games.
  • Become the link between various stakeholders, teams, and clients to provide solutions to maximize their business goals. 
  • Evolve our service organization based on our geographical expansion as well as service and organizational needs arising from growing business with Enterprise clients. 
  • Ensure a consistent level of knowledge and quality in the execution of your teams.
  • Collaborate with the product and technical teams to ensure our platform meets client needs and provides a positive user experience.
  • Ensure up-to-date product and technical documentation in alignment with our product team. 
  • Be part of an international English-speaking management team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • Minimum 3 years of experience in a leadership role within a service organization, preferably in the (mobile) ad tech industry.
  • Proven track record of building and managing high-performing teams.
  • Strong analytical and problem-solving skills with the ability to work with data –  use SQL to query databases, Excel and other analytics tools
  • Ability to build and maintain strong relationships with clients and colleagues.
  • You thrive in a team environment and are an excellent cross-team collaborator with excellent analytical and communication skills
  • Basic Knowledge of HTML and JavaScript or one basic programming or scripting language 
  • 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/Kotlin/Swift or have experience building your own apps.
  • Plus: Understanding of the mobile ad tech landscape and current best practices.
  • 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.
  • Wealth building: virtual stock options for all our regular employees.
  • 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 personal trainer, various classes like Yoga with expert teachers.
  • Activity Package: Regular team and company events, hackathons.
  • Education Package: Opportunities to boost your professional development with courses and trainings directly connected to your career goals 
  • Free of charge access to our EAP (Employee Assistance Program) which is a counseling service designed to support your mental health and well-being.
  • Build our signature product

    See vacancies