anujeet.swain
Mon 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 (
Following cache invalidation techniques are used to set the data as "stale" in react query:
• Implicitly setting up the
• Adding triggers to react-query:
• Manual invalidation for specific queries using
To have more control over specific query invalidation, we can utilise the queryKey property:
#react-query #cache-invalidation #data-synchronization
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
aman.suhag
Mon Sep 30 2024
Understanding
1.
In Next.js, dynamic routes are created by using brackets in the file names inside the
Example:
2.
Query strings are parameters passed through the URL, typically after a
In Next.js, with the App Router or when using API routes, you can use
Example:
Key Differences:
• Dynamic Routes (
• Query Strings (
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
context.params
and req.nextUrl.searchParams()
in Next.js1.
context.params
for Dynamic RoutesIn 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 StringsQuery 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
nived.hari
Mon 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
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.
In the Helpers (e.g.
Wrap your collections with pagy in your actions :
Optionally set your defaults in the pagy initializer
In the
1.
2.
Some additional helpers:
1.
2.
3.
Example Usage:
If we are on page 1 and displaying 8 items per page and total count is 20, This would display
#pagy #pagination #RubyOnRails
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.etc3. "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.
: 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
aman.suhag
Fri 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
◦ 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
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
ayasha.pandey
Thu 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.
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.
#swr #hook #react #next
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('', 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('', 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
aman.suhag
Thu 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
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
Validating Incoming Data
In your API route, you can use
Key Benefits -
• Type Safety: With
• 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
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
amber.srivastava
Thu Sep 26 2024
How to test an API endpoint.
Lets say we have to test an API endpoint api/projects
1.
2.
• User is redirected with a
• A
• The project is stored in the database when valid data is provided.
3. Mocking:
•
• Mock Implementation: With
4.
•
•
•
5.
6. Assertions:
•
•
#CCT1JMA0Z #promises #C041BBLJ57G #jest #appRouter
Lets say we have to test an API endpoint api/projects
describe("POST /api/projects", () => {
test("redirect with 401 status if the user is not logged in", async () => {
jest
.spyOn(nextAuth, "getServerSession")
.mockImplementation(() => Promise.resolve());
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: projectData,
});
const res = await postProjectHandler(req as any);
expect(res.status).toEqual(401);
});
test("return 400 if the request body is invalid", async () => {
jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
Promise.resolve({
user: {
id: user.id,
},
})
);
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: {},
});
req.json = jest.fn().mockResolvedValue(req.body);
const res = await postProjectHandler(req as any);
expect(res.status).toEqual(400);
});
test("store project in database", async () => {
jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
Promise.resolve({
user: {
id: user.id,
},
})
);
const mockResponse = {
ok: true,
team: {
id: "T08DABCD",
name: "Prisma",
},
};
const mockList = jest.fn().mockResolvedValue(mockResponse);
= mockList;
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: {
...projectData,
},
});
req.json = jest.fn().mockResolvedValue(req.body);
const res = await postProjectHandler(req as any);
const json = await res.json();
expect(res.status).toEqual(201);
expect(json.project.name).toEqual("Test name");
expect(json.project.description).toEqual(
"Test description"
);
});
});
1.
describe
: This is a block in Jest used to group related tests together. In our case, it groups tests for the POST /api/projects
endpoint. It helps organize tests logically.2.
test
: Each test
block defines an individual test case. It contains a name (description) of what it’s testing and a function with the test logic. The three tests are:• User is redirected with a
401
if they are not logged in.• A
400
status is returned if the request body is invalid.• The project is stored in the database when valid data is provided.
3. Mocking:
•
jest.spyOn()
: This creates a mock (or "spy") for a specific function. In this case, we are mocking the getServerSession
function from nextAuth
to control its output (whether the user is logged in or not). This avoids actually calling the real function and allows you to simulate different conditions.• Mock Implementation: With
.mockImplementation()
, we’re replacing the real function with a custom one. For example, in the first test, Promise.resolve()
is returned to simulate a missing session (not logged in).4.
createRequest<APIRequest>()
: This is likely a helper function that creates a mock request object for testing. We use this to simulate an HTTP request (like sending a POST request to your /api/projects
endpoint). It includes:•
method
: Specifies that it’s a POST request.•
url
: The endpoint being tested.•
body
: The request data sent with the POST request.5.
postProjectHandler(req as any)
: This function is our handler for the POST /api/projects
endpoint. It processes the incoming request (in req
), validates the data, and performs actions like saving to the database or returning errors. We’re testing the results of this function.6. Assertions:
•
expect(res.status).toEqual(401)
: This is checking if the response status matches the expected value. For example, in the first test, we expect a 401
status if the user isn’t logged in.•
expect(json.project.name).toEqual("Test name")
: Here, you're checking if the response JSON contains the correct project name.#CCT1JMA0Z #promises #C041BBLJ57G #jest #appRouter
amber.srivastava
Thu Sep 26 2024
How to test an API endpoint.
Let's say we have to test an endpoint /api/projects.
Let's say we have to test an endpoint /api/projects.
describe("POST /api/projects", () => {
test("redirect with 401 status if the user is not logged in", async () => {
jest
.spyOn(nextAuth, "getServerSession")
.mockImplementation(() => Promise.resolve());
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: projectData,
});
const res = await postProjectHandler(req as any);
expect(res.status).toEqual(401);
});
test("return 400 if the request body is invalid", async () => {
jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
Promise.resolve({
user: {
id: user.id,
},
})
);
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: {},
});
req.json = jest.fn().mockResolvedValue(req.body);
const res = await postProjectHandler(req as any);
expect(res.status).toEqual(400);
});
test("store project in database", async () => {
jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
Promise.resolve({
user: {
id: user.id,
},
})
);
const mockResponse = {
ok: true,
team: {
id: "T08DABCD",
name: "Prisma",
},
};
const mockList = jest.fn().mockResolvedValue(mockResponse);
= mockList;
const req = createRequest<APIRequest>({
method: "POST",
url: "/api/projects",
body: {
...projectData,
},
});
req.json = jest.fn().mockResolvedValue(req.body);
const res = await postProjectHandler(req as any);
const json = await res.json();
expect(res.status).toEqual(201);
expect(json.project.name).toEqual("Test name");
expect(json.project.description).toEqual(
"Test description"
);
});
});
ashwanikumarjha
Thu Sep 26 2024
Using the
#hapi.js #server
server.table()
method in Hapi.js
we can retrieve all the registered routes in a server instance. It returns an array of route groups, containing details like the HTTP method, path, settings and other relevant information.#hapi.js #server
aman.suhag
Wed Sep 25 2024
Sending Body Data as
The
In your test case:
This simulates the request body. Since you're testing a
Thus, the test needs to mock this body data to simulate a real request. It's done asynchronously here because in actual implementations, reading the body data is often asynchronous (e.g., when streaming).
Passing Context (with
The
In your test case:
You're simulating the dynamic parameter
This allows your API handler to know which standup you're targeting for the update.
#app-router #dynamic-routes #json
json
:The
json
function in the request is simulating the body of the request. In a real-world scenario, when you send a request with data (like when you're submitting a form or sending some payload), it is typically part of the body of the HTTP request.In your test case:
json: async () => ({
standupData,
}),
This simulates the request body. Since you're testing a
PATCH
method (which is often used to update resources), the server expects some data to update the resource. This json
function acts like a mock for what would be req.body
in a real request. In your API handler, you'd probably be extracting the body data using something like:
const data = await req.json();
Thus, the test needs to mock this body data to simulate a real request. It's done asynchronously here because in actual implementations, reading the body data is often asynchronous (e.g., when streaming).
Passing Context (with
params
):The
context
object typically holds parameters that are passed via the route in a Next.js API request, especially when using dynamic routes (e.g., /api/standups/[id]/toggle-status
). In Next.js, when you define a dynamic API route like /api/standups/[id]/toggle-status
, the id
is part of the route path, and Next.js passes it in the context.params
object.In your test case:
const context = {
params: {
id: standup.id,
},
};
You're simulating the dynamic parameter
id
as part of the request. This is necessary because your API handler likely expects id
to come from the route context (not just from the query string or body). In the handler, you're accessing context.params.id
:
const { id } = context.params as { id: string };
This allows your API handler to know which standup you're targeting for the update.
#app-router #dynamic-routes #json
Showing 5 to 7 of 71 results