<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ismail PE</title>
    <description>The latest articles on DEV Community by Ismail PE (@ismailpe).</description>
    <link>https://dev.to/ismailpe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F406915%2Fb14e3306-8b4d-4c5a-8359-c271e0a9a9a8.jpg</url>
      <title>DEV Community: Ismail PE</title>
      <link>https://dev.to/ismailpe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ismailpe"/>
    <language>en</language>
    <item>
      <title>Chrome DevTools MCP</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Mon, 05 Jan 2026 08:53:25 +0000</pubDate>
      <link>https://dev.to/ismailpe/chrome-devtools-mcp-3a5c</link>
      <guid>https://dev.to/ismailpe/chrome-devtools-mcp-3a5c</guid>
      <description>&lt;p&gt;Have you ever felt AI was blind when debugging your web app with AI agent? AI couldn't see the shift in UI layout? AI didn't care about the loading performance or issues with styles? AI couldnt see the errors on browser console? Yes these things are not visible to your AI agent. But there is a way.&lt;/p&gt;

&lt;p&gt;With the power of using real browser, the AI agent can do better. ChromeDevtools MCP enables the ai agents to use a real browser, load the app, click buttons or fill form on the app, collect logs, take snapshots, and fix things with a real 'vision'. This guide is for &lt;code&gt;VS Code&lt;/code&gt; but similar steps can be found for any IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the extension
&lt;/h2&gt;

&lt;p&gt;Open extensions (Ctrl + Shift + x) in VS Code&lt;br&gt;
Search &lt;code&gt;@mcp devtools&lt;/code&gt;&lt;br&gt;
Install &lt;code&gt;ChromeDevTools&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensure the MCP server is running
&lt;/h2&gt;

&lt;p&gt;Right click on the installed extension to get actions menu (you can do start/stop/restart/configure here)&lt;br&gt;
Open the project folder in IDE for the AI agent to get the context.&lt;br&gt;
You can also use &lt;code&gt;npx chrome-devtools-mcp@latest --help&lt;/code&gt; to see all available configuration options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it
&lt;/h2&gt;

&lt;p&gt;Open the Copilot chat on the IDE and ask &lt;code&gt;why my form is not centered&lt;/code&gt; or &lt;code&gt;Test this form submission with all inputs&lt;/code&gt; or &lt;code&gt;Check if layout shifts with error messages displaying on the form&lt;/code&gt; or &lt;code&gt;Improve the LCP&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;This MCP provides many tools to the AI agent. You can use '#' in chat to see the list of tools available. Here is an example with direct tools usage and in common words. Both will work. Use tools for a precise prompt and faster response.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#navigate_page home page and #fill_form first name: Adam, last name: Noah, #take_snapshot , #click submit, #wait_for submission, #list_network_requests , #list_console_messages&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Navigate to the home page and fill the form with first name: Adam, last name: Noah. Click the submit button and wait for it. List down the console logs. List down the network requests.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ChromeDevTools/chrome-devtools-mcp?tab=readme-ov-file" rel="noopener noreferrer"&gt;Here is the Github page for more help&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>devtools</category>
      <category>ai</category>
      <category>testing</category>
    </item>
    <item>
      <title>Using RTK with Nextjs</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Fri, 21 Mar 2025 05:57:16 +0000</pubDate>
      <link>https://dev.to/ismailpe/using-rtk-for-nextjs-19jc</link>
      <guid>https://dev.to/ismailpe/using-rtk-for-nextjs-19jc</guid>
      <description>&lt;p&gt;A straight forward example of using Redux Tool Kit with Nextjs app.&lt;/p&gt;

&lt;p&gt;redux/hooks.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useDispatch, useSelector, useStore } from "react-redux";
// Use throughout the app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes();
export const useAppSelector = useSelector.withTypes();
export const useAppStore = useStore.withTypes();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;redux/store.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { configureStore } from "@reduxjs/toolkit";
import userReducer from "../features/user/userSlice";

export const makeStore = () =&amp;gt; {
  return configureStore({
    reducer: {
      user: userReducer,
      //list more if any
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;redux/StoreProvider.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use client'
import { useRef } from 'react'
import { Provider } from 'react-redux'
import { makeStore } from './store'

export default function StoreProvider({ children }) {
  const storeRef = useRef()
  if (!storeRef.current) {
    // Create the store instance the first time this renders
    storeRef.current = makeStore();
  }

  return &amp;lt;Provider store={storeRef.current}&amp;gt;{children}&amp;lt;/Provider&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;features/user/userSlice.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
//import axiosConfig as well

const initialState = {
  userInfo: {},
  isUserLoggedIn: false,
  isUserInfoLoading: true,
  //..add more state variables
};

const reducers = {
  setUserInfoResponse: (state, action) =&amp;gt; {
    state.userInfo = action.payload;
    state.isUserLoggedIn = true;
    state.isUserInfoLoading = false;
  },
  //...add more functions to update state
};

export const userSlice = createSlice({
  name: "user",
  initialState,
  reducers,
});

export const getUserInfo = createAsyncThunk(
  "user/getUserInfo",
  async (params, { dispatch }) =&amp;gt; {
  const { uid } = params;
    const userInfoResponse = await axiosConfig.get(
      `get_user_info/${uid}`,
    );
    if (userInfoResponse.data.status) {
      dispatch(setUserInfoResponse(userInfoResponse.data.data));
    }
  }
);

export const {
  setUserInfoResponse,
  //list other functions defined above if any
} = userSlice.actions;
export default userSlice.reducer;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;_app.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import StoreProvider from "../redux/StoreProvider";
import { SessionProvider } from "next-auth/react";

function MyApp({ Component, pageProps: { session, ...pageProps } }) {
  return (
   &amp;lt;SessionProvider session={session}&amp;gt;
    &amp;lt;StoreProvider&amp;gt;
     &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/StoreProvider&amp;gt;
   &amp;lt;/SessionProvider&amp;gt;
  )
}
export default MyApp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useAppDispatch, useAppSelector } from "../../redux/hooks";
import { getUserInfo } from "../../features/user/userSlice";

...
  const dispatch = useAppDispatch();
  const { isUserLoggedIn, isUserInfoLoading, userInfo } = useAppSelector(state =&amp;gt; state.user);

inside useEffect or any logic:
dispatch(getUserInfo({ uid: 123 }));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>redux</category>
      <category>reduxtoolkit</category>
      <category>store</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Programmatically Whitelist new domains on Firebase app</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Fri, 19 Jan 2024 13:30:08 +0000</pubDate>
      <link>https://dev.to/ismailpe/whitelist-new-domains-to-firebase-from-code-30hd</link>
      <guid>https://dev.to/ismailpe/whitelist-new-domains-to-firebase-from-code-30hd</guid>
      <description>&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt;: You have an app which uses signInWithPhoneNumber function from firebase and your app allow users to create subdomains from your app. Now you have multiple domains which are not added to authorized list of domains on your firebase app. So users wont be able to sign in using phone number from the subdomain. Firebase doesn't allow wildcard to be added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How&lt;/strong&gt;: We will create an api which will add the newly created subdomain into the whitelist. This api will be called before user shoots the request to signin with firebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need service account credentials for getConfig and updateConfig operations&lt;/li&gt;
&lt;li&gt;updateConfig replaces the list of domains. So we need to fetch the existing list and append to it.&lt;/li&gt;
&lt;li&gt;Create and get private_key and service account email address from firebase console. Add them as env variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;br&gt;
When user clicks sign in with phone number button, call this api, which will add the subdomain to list of authorized list. Then call signInWithPhoneNumber.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//add new subdomain to white list
var jwt = require("jsonwebtoken");
export default async function handler(req, res) {
  // Get the current time in UTC seconds
  const currentTimestamp = Math.floor(Date.now() / 1000);
  // Set the expiry time to 60 minutes (60 * 60 seconds) from the current time
  const expiryTimestamp = currentTimestamp + 60 * 60;
  try {
    //create JWT for requesting access token from firebase
    const jwtForToken = jwt.sign(
      {
        iss: process.env.FIREBASE_SA_EMAIL,
        scope: "https://www.googleapis.com/auth/identitytoolkit",
        aud: "https://oauth2.googleapis.com/token",
        iat: currentTimestamp,
        exp: expiryTimestamp,
      },
      `${process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n')}`,
      { algorithm: "RS256" }
    );

    //use the JWT created and request access token from firebase
    const data = new URLSearchParams();
    data.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
    data.append("assertion", jwtForToken);
    const tokenResp = await fetch("https://oauth2.googleapis.com/token", {
      method: "POST",
      headers: {
        "content-type": "application/x-www-form-urlencoded",
      },
      body: data,
    });
    const token = await tokenResp.json();

    //get current config data from firebase
    const configResp = await fetch(
      `https://identitytoolkit.googleapis.com/admin/v2/projects/${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}/config?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`,
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${token.access_token}`,
        },
      }
    );
    const configData = await configResp.json();
    let authorizedDomains = configData.authorizedDomains;

    const { domain } = JSON.parse(req.body);
    if (authorizedDomains.includes(domain)) {
      res.status(200).json({ status: true, result: authorizedDomains });
    } else {
      //add new domain to whitelist
      authorizedDomains.push(domain);
      //patch the config to firebase with updated list of domains
      const updateRequestBody = new URLSearchParams();
      updateRequestBody.append("authorizedDomains", [authorizedDomains]);
      const configUpdateResp = await fetch(
        `https://identitytoolkit.googleapis.com/admin/v2/projects/${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}/config?updateMask=authorizedDomains`,
        {
          method: "PATCH",
          headers: {
            Authorization: `Bearer ${token.access_token}`,
          },
          body: JSON.stringify({ authorizedDomains }),
        }
      );
      res
        .status(200)
        .json({ status: configUpdateResp.status, result: authorizedDomains });
    }
  } catch (err) {
    // console.log("err", err)
    res.status(500).json({ status: false, result: err });
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>firebase</category>
      <category>config</category>
      <category>domain</category>
      <category>whitelist</category>
    </item>
    <item>
      <title>Multiple api calls even for single saga action?</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Fri, 24 Jul 2020 15:49:20 +0000</pubDate>
      <link>https://dev.to/ismailpe/multiple-api-calls-even-for-single-saga-action-2ncf</link>
      <guid>https://dev.to/ismailpe/multiple-api-calls-even-for-single-saga-action-2ncf</guid>
      <description>&lt;p&gt;I was facing this issue in my React app and I could not find the exact reason in internet. There were many hacks and solutions for the same symptom, but nothing worked for me. Finally i solved it by correcting a mistake in code. So thought of posting it here.&lt;/p&gt;

&lt;p&gt;I had an action which fetches a data via api call. There were other actions also defined inside same saga file. And this saga was used in multiple containers. Whenever i navigate between these containers, and call any action from this saga, there were multiple api calls triggered. But saga was called only once.&lt;/p&gt;

&lt;p&gt;Problem:&lt;br&gt;
While injecting the saga, I was using different key names inside different containers for the same saga. Saga was supposed to automatically ignore the duplicate actions. But since the key was different, it considered all the calls as unique.&lt;/p&gt;

&lt;p&gt;Solution:&lt;br&gt;
I used same key for this saga in all the containers where this was injected. So simple.&lt;/p&gt;

&lt;p&gt;It was totally my mistake to use different keys. But i found same question in forums and github issues. So may be this would help someone to solve the issue or rule out this reason for the symptom. &lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;

</description>
      <category>react</category>
      <category>saga</category>
      <category>api</category>
    </item>
    <item>
      <title>Duplicating a github repo with history</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Thu, 23 Jul 2020 16:52:56 +0000</pubDate>
      <link>https://dev.to/ismailpe/duplicating-a-github-repo-with-history-3223</link>
      <guid>https://dev.to/ismailpe/duplicating-a-github-repo-with-history-3223</guid>
      <description>&lt;p&gt;Are you moving your codebase into a different versioning system? It's so easy to duplicate a github repository including every commits and branches.&lt;/p&gt;

&lt;p&gt;1) Create a new repo in github using UI/command as usual. This will be the target repo where duplicate of source repo to be pasted.&lt;/p&gt;

&lt;p&gt;2) Open a command prompt and Clone the source/existing repo using the command:&lt;/p&gt;

&lt;p&gt;git clone --bare &lt;a href="https://sourceRepoURL" rel="noopener noreferrer"&gt;https://sourceRepoURL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new folder will be created containing some files from source repo.&lt;/p&gt;

&lt;p&gt;3) Move to that folder:&lt;br&gt;
cd sourceRepoFolder&lt;/p&gt;

&lt;p&gt;4) Push these files into the target repo using command:&lt;br&gt;
git push --mirror &lt;a href="https://targetRepoURL" rel="noopener noreferrer"&gt;https://targetRepoURL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. Thank you.&lt;/p&gt;

</description>
      <category>github</category>
      <category>branch</category>
      <category>migrate</category>
      <category>repo</category>
    </item>
    <item>
      <title>Handling environment variable: React and AWS Cognito</title>
      <dc:creator>Ismail PE</dc:creator>
      <pubDate>Thu, 11 Jun 2020 15:15:57 +0000</pubDate>
      <link>https://dev.to/ismailpe/handling-environment-variable-react-and-aws-cognito-540h</link>
      <guid>https://dev.to/ismailpe/handling-environment-variable-react-and-aws-cognito-540h</guid>
      <description>&lt;p&gt;This post is for beginners. You can use the same code base for all environments if you have clearly handled the environment specific changes. Those changes should be minimised like the api urls. I have described the process in four steps.&lt;/p&gt;

&lt;p&gt;First, I have created separate files where these differences are defined. For example, my files are named config.prod, config.qa and config.dev. And the contents of file is like&lt;br&gt;
export default {&lt;br&gt;
  api: {&lt;br&gt;
    baseURL: “https://..”,&lt;br&gt;
    fetchUsers: “https://..”,&lt;br&gt;
  }&lt;br&gt;
  cognito: {&lt;br&gt;
    USER_POOL_ID: 1234,&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Prod config file will have prod api url domain and dev config file will have dev api url domain.&lt;br&gt;
If your endpoints are same for all environments, you can export it from a separate common file. &lt;br&gt;
2: And i have written a util function which switches the custom environment variable (we will set in next step) and return corresponding config file.&lt;/p&gt;

&lt;p&gt;export function getEnvConfig(){&lt;br&gt;
let config = null&lt;br&gt;
switch(process.env.REACT_APP_ENV){&lt;br&gt;
  case “dev”:&lt;br&gt;
   config = require(“../config.dev”)&lt;br&gt;
   break;&lt;br&gt;
  case “qa”:&lt;br&gt;
   config = require(“../config.qa”)&lt;br&gt;
  default:&lt;br&gt;
   config = require(“../config.prod”)&lt;br&gt;
  }&lt;br&gt;
  return config&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;3: And inside my saga/thunk, i have imported and used the urls from config file.&lt;/p&gt;

&lt;p&gt;const config = getEnvConfig()&lt;br&gt;
const fetchUserUrl = config.default.baseURL + 'getusers'&lt;/p&gt;

&lt;p&gt;4.On AWS Cognito secret manager, for each environment, I added a new secret with the name REACT_APP_ENV and value as my corresponding environment.&lt;/p&gt;

&lt;p&gt;That's all. You can now access access your custom environment variable in your code using process.env.REACT_APP_ENV&lt;/p&gt;

&lt;p&gt;Thank you!&lt;/p&gt;

</description>
      <category>react</category>
    </item>
  </channel>
</rss>
