Fueling Curiosity, One Insight at a Time

At Codemancers, we believe every day is an opportunity to grow. This section is where our team shares bite-sized discoveries, technical breakthroughs and fascinating nuggets of wisdom we've stumbled upon in our work.

Oct 7, 2024
Mocking in Jest
Jest provides several ways to mock:
jest.fn(): Creates a mock function that you can use instead of a real function.
jest.mock(): Mocks entire modules.
jest.spyOn(): Tracks calls to an existing method while optionally replacing its implementation.
#jest #test #mock
aman.suhag
Aman Suhag
System Analyst
Oct 2, 2024
Async Local Storage in Node.js

• Provides us a way to store and manage context-specific data across asynchronous operations without needing to pass it explicitly through function arguments.
• Built on the async_hooks module, which tracks asynchronous resource lifecycle events.
• We need to use asyncLocalStorage.run(store, callback) to create a new context.
• Asynchronous operations initiated within this callback inherit that context.
• Each context created with asyncLocalStorage.run() is unique and does not interfere with other contexts.
• Common use cases can be maintaining custom context in our web app (e.g., request data, user ID...) across multiple layers (controllers, services, etc.), can help us with tracing how a request propagates through different async functions.
• Automatically cleans up the context after the asynchronous operations are complete.
run(store, callback): Creates a new context and runs the callback with the provided store (like a Map or a primitive value).
• Set a value: asyncLocalStorage.getStore().set('requestId', requestId);
• Get a value: const requestId = asyncLocalStorage.getStore().get('requestId');


const http = require('node:http');
const { AsyncLocalStorage } = require('node:async_hooks');
const { v4: uuid } = require('uuid');

const asyncLocalStorage = new AsyncLocalStorage();

function logWithId(msg) {
  const requestId = asyncLocalStorage.getStore(); 
  console.log(`${requestId} - ${msg}`);
}

function serviceA() {
  logWithId('Service A log');
}

function serviceB() {
  logWithId('Service B log');
}

http.createServer((req, res) => {
  const requestId = uuid();
  asyncLocalStorage.run(requestId, () => { 
    logWithId('Request received');
    serviceA();
    serviceB();
    logWithId('All services called');
    res.end('Response sent');
  });
}).listen(4040);

http.get('https://localhost:4040');

// Output:
<generated-request-id> - Request received
<generated-request-id> - Service A log
<generated-request-id> - Service B log
<generated-request-id> - All services called

ashwanikumarjha
Ashwani Kumar Jha
Senior System Analyst
Sep 30, 2024
## Avoid Mutating Objects Loaded from JSON Files

Today I learned that even if data is loaded from a static JSON file - once it's parsed and stored as a JavaScript object in memory, it behaves like any other object—meaning it's mutable by reference.

This means that modifying a property of an object loaded from a JSON file will mutate the original object in memory, affecting all references to that object across the app.

To avoid accidental mutations, it's best to create a copy of the object (using methods like { ...obj } for shallow copies) before modifying it. This ensures that the original data remains unchanged and helps prevent unexpected side effects throughout the codebase.

Example of creating a copy to avoid mutation:



const content = { ...emails['Signup success'] };


This protects the original emails object from being modified, keeping the rest of the app safe from unintended changes.

---

It's a small but important detail when dealing with mutable JavaScript objects loaded from static sources!

#passByReference #js #json #objects
vaibhav.yadav
Vaibhav Yadav
Senior System Analyst
Sep 30, 2024
The default behavior of a form's submit button in Rails is to disable itself once the form has been submitted. In any situation if you want to submit form multiple times without reloading the page, we can use a simple trick:

1. Move the submit button outside of the form.
2. Create a controller that connects the button and the form.
3. Implement an action in controller to submit the form when the button is clicked.
Form:


<%= form_with(model: @object, data: {
    controller: "form",
    form_target: "form"
}) do |form| %>
  <%= form.label :name %>
  <%= form.text_field :name %>

  <%= form.label :status %>
  <%= form.select :status, ["Active", "Inactive"] %>

<% end %>

<%= button_tag "submit", type: "submit", data: {
      action: "click->form#formSubmit"
    } %>


Controller:


export default class extends Controller {
    static targets = ["form"];

    formSubmit(){
        this.formTarget.submit()
    }
}


Why This Works
By placing the button outside the form, it becomes unlinked from the form submission process, allowing it to remain enabled even after the form is submitted.

#CU6U0R822 #form #stimulus
nitturu.baba
Nitturu Baba
System Analyst
Sep 30, 2024
While using React-Query, Cache invalidation is key for keeping your data up-to-date with server state.
Data becomes "stale" after a set time (staleTime), and stale data gets re-fetched when the query is re-triggered (e.g., on component remount, on focus, or on manual refetch).

Following cache invalidation techniques are used to set the data as "stale" in react query:
• Implicitly setting up the staleTime .
• Adding triggers to react-query: refetchOnWindowFocus , refetchOnReconnect, refetchOnMount , refetchInterval .
• Manual invalidation for specific queries using queryClient.invalidateQueries() .
To have more control over specific query invalidation, we can utilise the queryKey property:


queryClient.invalidateQueries({
  queryKey: ['todos'],
})




queryClient.invalidateQueries({
  queryKey: ['todos', { type: 'done' }],
})


#react-query #cache-invalidation #data-synchronization
anujeet.swain
Anujeet Swain
System Analyst
Sep 30, 2024
Understanding context.params and req.nextUrl.searchParams() in Next.js
1. context.params for Dynamic Routes
In Next.js, dynamic routes are created by using brackets in the file names inside the pages directory (or in the /app directory in the case of the App Router). For example, if you create a file called [id].js, you are creating a dynamic route where id is a parameter.
Example:


const { id } = context.params;


2. req.nextUrl.searchParams() for Query Strings
Query strings are parameters passed through the URL, typically after a ? symbol. These are useful for handling additional data or filters that don’t affect the route structure.
In Next.js, with the App Router or when using API routes, you can use req.nextUrl.searchParams() to access query parameters.
Example:


const searchParams = req.nextUrl.searchParams;
const searchTerm = searchParams.get('query'); // ?query=nextjs


Key Differences:

Dynamic Routes (context.params) are part of the URL path, like /product/123, where 123 is dynamically extracted.
Query Strings (req.nextUrl.searchParams) are optional parameters passed in the URL, like /api/search?query=nextjs.
Both are useful depending on whether you want to make the parameters part of the URL path or pass them as additional optional information.
#nextJS #query #dynamic-params
aman.suhag
Aman Suhag
System Analyst
Sep 30, 2024
Pagy Gem for Efficient Pagination in Rails

A fast, lightweight, and efficient solution for pagination in Ruby on Rails applications.


Why pagy?

1. Faster and less resource heavy when compared to other pagination gems like Kaminari or Will Paginate
2. Highly customizable : We can easily configure pagy through pagy.rb initializer file like default items per page.etc
3. "Helpful" Helpers : Pagy provides various helper methods that make it easy to implement pagination in views with minimal code.
4. Efficiency: It significantly reduces the number of queries, making it ideal for large datasets.
5. Performance-Oriented: Pagy is claiming to perform up to 40x faster than other pagination gems such as Kaminari and Will Paginate
Example Usage:

Code for basic pagination:

In the controllers (e.g. application_controller.rb)



include Pagy::Backend



In the Helpers (e.g. application_helper.rb)



include Pagy::Frontend



Wrap your collections with pagy in your actions :


@pagy, @records = pagy(Product.all)


Optionally set your defaults in the pagy initializer pagy.rb :



# Set default items per page and navigation size
Pagy::DEFAULT[:items] = 10  # items per page
Pagy::DEFAULT[:size]  = [1, 4, 4, 1]  # control how many navigation links are shown




In the view:



<%= pagy_nav(@pagy) %>


1. pagy_nav: Renders the full pagination navigation links (next, previous, and page numbers).
2. pagy_info: Displays pagination information such as the range of items being shown and the total count.
Some additional helpers:

1. pagy.from: This returns the starting index of the current page’s items.
2. https://pagy.to|pagy.to: This returns the ending index of the current page’s items.
3. pagy.count: This returns the total number of items being paginated.

Example Usage:



Showing <%= pagy.from(@pagy) %> to <%= pagy.to(@pagy) %> of <%= @pagy.count %> items.


If we are on page 1 and displaying 8 items per page and total count is 20, This would display Showing 1 to 8 of 20 items Giving more clarity about the pagination, making it user-friendly

#pagy #pagination #RubyOnRails
nived.hari
Nived Hari
System Analyst
Sep 27, 2024
CSRF stands for Cross-Site Request Forgery, which is a type of attack where a malicious actor tricks a user into performing unwanted actions on a web application in which the user is authenticated. The attacker essentially "forges" a request from the user's browser without their consent, taking advantage of the user's active session with the target website.

How CSRF Works:
1. User Authentication: The user logs into a website (e.g., a banking website) and receives a session cookie that keeps them authenticated.
2. User Visits a Malicious Site: While logged in, the user visits a malicious site or clicks on a malicious link.
3. Forged Request: The malicious site generates a hidden request (such as a form submission) to the target site (e.g., bank) on behalf of the user, utilizing the user's active session and browser cookies.
4. Unintended Action: Since the user is authenticated, the target site processes the request as valid, allowing the attacker to perform actions like transferring money, changing account details, etc.

CSRF Prevention Mechanisms:
To protect against CSRF, developers can implement several mechanisms:
1. CSRF Tokens: The most common and effective defense.
◦ Every form submission or sensitive request includes a hidden, random token (CSRF token) that is unique to the user's session.
◦ The server validates the token before processing the request, ensuring the request originated from a legitimate source.
2. SameSite Cookies: A modern defense where cookies are only sent with requests originating from the same site.
◦ Setting SameSite attribute in cookies can prevent browsers from sending cookies in cross-origin requests.
◦ Set-Cookie: sessionID=abc123; SameSite=Strict;
NextAuth.js and CSRF Protection:
In the context of NextAuth.js, CSRF protection is enabled by default when handling authentication requests. It ensures that malicious websites can’t perform unwanted actions on behalf of a logged-in user.
#csrf #security
aman.suhag
Aman Suhag
System Analyst
Sep 26, 2024
Swr:
Stale-While-Revalidate is a popular strategy that first returns the data from stale(cache), then send the fetch request (revalidate), and finally come with the up-to-date data.

Why swr?
Instant Loading (Faster User Experience)
Background Revalidation of the data
Improves Offline/Slow Network Experience

We have two most common hooks from SWR data-fetching library to implement swr in our React/Next application -
1. useSWR - with useSWR we can easily fetch data from an API or any source. The hook automatically handles everything like caching, revalidation, error handling, and cache data updates. For example, it revalidates the data when component mounts, when the user refocuses the browser tab, reconnects to the internet, or on custom revalidation interval.


import useSWR from 'swr';

// Fetcher function
const fetcher = (url) => fetch(url).then((res) => res.json());

function MyComponent() {
  const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);

  if (error) return <div>Error loading data</div>;
  if (isLoading) return <div>Loading...</div>;

  return <div>{JSON.stringify(data)}</div>;
}


2. useSWRMutation - It complements useSWR by providing a way to modify data, such as performing create, update, or delete operations. While useSWR is designed for reading data and displaying it in your app, useSWRMutation focuses on mutating or updating that data on the server side.


import useSWRMutation from 'swr/mutation';

// Function to update data
const updateData = (url, { arg }) =>
  fetch(url, {
    method: 'PUT',
    body: JSON.stringify(arg),
    headers: { 'Content-Type': 'application/json' },
  });

function MyMutationComponent() {
  const { trigger, isMutating } = useSWRMutation('https://api.example.com/data', updateData);

  const handleUpdate = async () => {
    await trigger({ id: 1, name: 'New Name' });
  };

  return (
    <div>
      <button onClick={handleUpdate} disabled={isMutating}>
        Update Item
      </button>
      {isMutating && <span>Updating...</span>}
    </div>
  );
}


#swr #hook #react #next
ayasha.pandey
Ayasha Pandey
System Analyst
Sep 26, 2024
Schema Validation with Zod: Why and How?
When building APIs, validating the structure and content of incoming requests is crucial for security, consistency, and reliability. Using a library like zod allows you to define schemas that describe the expected data format and then easily validate incoming data. Here’s how and why we use it in a Next.js API.
Why Validate?
1. Security: Ensure only well-formed data reaches your application, preventing injection attacks or bad data.
2. Consistency: Guarantees that the data has the correct shape, making it easier to work with.
3. Error Handling: Helps provide meaningful error messages when the validation fails.
Example: Standup Schema Validation
Using zod, we can define a schema for our "standup" object and use it to validate incoming requests in our API route.


import { z } from "zod";

// Define a schema for the standup data
const standupSchema = z.object({
  name: z.string().trim().nonempty(),
  days: z.number().array().min(1).max(7),
  time: z.string().datetime(),
  postStandupTime: z.string().datetime(),
  standupLead: z
    .object({
      name: z.string().trim().nonempty(),
      id: z.string(),
    })
    .required(),
  questions: z
    .array(z.object({ text: z.string().min(1), isActive: z.boolean() }))
    .min(1),
  channel: z.object({
    name: z.string().trim().nonempty(),
    id: z.string(),
  }),
  members: z
    .array(
      z.object({
        name: z.string().trim().nonempty(),
        id: z.string(),
      })
    )
    .min(1),
  isActive: z.union([z.boolean(), z.undefined()]),
  schedulerId: z.string().optional(),
  timezone: z.string().min(1),
  postType: z.string().min(1),
});

export type StandupDetail = z.infer;


Validating Incoming Data
In your API route, you can use safeParse to validate the request body against the schema. This ensures the data is valid before proceeding with further logic.


export async function PATCH(req: NextRequest) {
  const body = await req.json();
  
  // Validate the request body using the schema
  const response = standupSchema.safeParse(body);
  
  if (!response.success) {
    const { errors } = response.error;
    return NextResponse.json(
      { error: { message: "Invalid Payload", errors } },
      { status: 400 }
    );
  }
  
  // If successful, extract the validated data
  const {
    name,
    days,
    time,
    postStandupTime,
    questions,
    channel,
    members,
    isActive,
    timezone,
    standupLead,
    postType,
  }: StandupDetail = response.data;

  // Proceed with your logic using the validated data
}


Key Benefits -
Type Safety: With zod and TypeScript, you get strong typing, ensuring that the validated data has the expected shape.
Automatic Error Handling: If the validation fails, you can easily catch errors and send meaningful feedback.
Data Consistency: Guarantees that the incoming data adheres to a predefined structure, making your API more reliable and robust.
#zod #type-security #json
aman.suhag
Aman Suhag
System Analyst

Showing 12 to 14 of 79 results

Ready to Build Something Amazing?

Codemancers can bring your vision to life and help you achieve your goals