RSS
 

Archive for the ‘Technical’ Category

Data Transfer Objects and NextJS Server Actions

01 Aug

Next.js server actions can be leveraged to implement the DTO (Data Transfer Object) design pattern to structure and manage data flow between the client and server.

What Are Data Transfer Objects (DTO)s?

DTOs are objects that define the structure of data being transferred between layers of an application (e.g., client and server). They are typically used to:

  1. Encapsulate Data: Define a clear contract for the shape of data being sent or received.
  2. Validate Data: Ensure that the data conforms to a specific structure.
  3. Decouple Layers: Abstract the internal structure of the server or database from the client.

DTOs with Next.js Server Actions

Server actions in Next.js provide a way to handle server-side logic directly in the application. By using DTOs as interfaces for server actions, you can:

  1. Standardize Data Contracts
    • Define clear and reusable interfaces for the data sent to and received from server actions.
    • Ensure consistency across the application.
  2. Improve Type Safety
    • Use TypeScript to enforce the structure of data at compile time.
    • Reduce runtime errors caused by unexpected data shapes.
  3. Simplify Validation
    • Validate incoming and outgoing data against the DTO structure.
    • Use libraries like zod or class-validator for runtime validation.
  4. Enhance Maintainability:
    • Centralize data definitions, making it easier to update and refactor the application.

How to Implement DTOs with Server Actions in Next.js

Step 1: Define DTO Interfaces

Create TypeScript interfaces or types to define the structure of the data being transferred.

import { z } from "zod";

// Define the DTO schema using zod for runtime validation
export const SheetUserWeightsDTO = z.object({
  userId: z.string(),
  sheetId: z.string(),
  specWeights: z.record(z.string(), z.number()), // Example: { spec1: 0.5, spec2: 0.8 }
  specsVersion: z.number(),
});

// Infer the TypeScript type from the zod schema
export type SheetUserWeightsDTOType = z.infer<typeof SheetUserWeightsDTO>;

Step 2: Use DTOs in Server Actions

Leverage the DTOs in server actions to validate and enforce the structure of incoming and outgoing data.

import { SheetUserWeightsDTO, SheetUserWeightsDTOType } from "@/lib/dto/sheetUserWeights.dto";

export async function saveSheetUserWeightsAction(data: SheetUserWeightsDTOType): Promise<void> {
  // Validate the incoming data
  const parsedData = SheetUserWeightsDTO.parse(data);

  // Perform the server-side logic (e.g., save to database)
  const { userId, sheetId, specWeights, specsVersion } = parsedData;

  // Example: Save to database
  await db.collection("sheetUserWeights").updateOne(
    { userId, sheetId },
    { $set: { specWeights, specsVersion } },
    { upsert: true }
  );
}

export async function getSheetUserWeightsAction(userId: string, sheetId: string): Promise<SheetUserWeightsDTOType | null> {
  // Fetch data from the database
  const result = await db.collection("sheetUserWeights").findOne({ userId, sheetId });

  if (!result) return null;

  // Validate the fetched data
  return SheetUserWeightsDTO.parse(result);
}

Step 3: Use DTOs in Client-Side Hooks

Use the DTOs in React Query hooks to ensure type safety and consistency.

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { SheetUserWeightsDTOType } from "@/lib/dto/sheetUserWeights.dto";
import { getSheetUserWeightsAction, saveSheetUserWeightsAction } from "@/lib/actions/sheets";

export function useQuerySheetUserWeights(userId: string, sheetId: string) {
  return useQuery<SheetUserWeightsDTOType | null, Error>({
    queryKey: ["sheetUserWeights", userId, sheetId],
    queryFn: () => getSheetUserWeightsAction(userId, sheetId),
    enabled: !!userId && !!sheetId,
    staleTime: 1000 * 60 * 5, // Cache for 5 minutes
  });
}

export function useSaveSheetUserWeights() {
  const queryClient = useQueryClient();

  return useMutation<void, Error, SheetUserWeightsDTOType>({
    mutationFn: saveSheetUserWeightsAction,
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries(["sheetUserWeights", variables.userId, variables.sheetId]);
    },
  });
}

Benefits of This Approach

  1. Type Safety
    • Both the client and server share the same DTO definitions, ensuring consistent data structures.
  2. Validation:
    • DTOs can validate incoming and outgoing data at runtime, catching errors early.
  3. Reusability:
    • DTOs can be reused across server actions, client-side hooks, and even database queries.
  4. Scalability:
    • As the application grows, DTOs provide a clear and maintainable way to manage data contracts.

When to Use DTOs

  • Complex Applications:
    • DTOs are especially useful in applications with complex data flows or multiple layers (e.g., client, server, database).
  • Shared Data Contracts:
    • When the same data structure is used across multiple parts of the application (e.g., client and server).
  • Validation Requirements:
    • When you need to validate data at runtime to ensure correctness.

Conclusion

Using DTOs as interfaces for server actions in Next.js is a powerful design pattern that improves type safety, validation, and maintainability. By defining DTOs with tools like zod or TypeScript interfaces, you can standardize data contracts across your application and ensure consistency between the client and server.

This article was generated mostly via my prompting of AI CoPilot; Image was generated via Claude.ai. I had to edit the result.

 
 

Adding Markdown (mdx) Support to NextJS with App Router

04 Jul

Markdown allows text content to be written with implicit formatting that nearly matches how one would write text without thought of formatting. A lot of web content is also simply text content; a lot of web content is built on the ReactJS library; and NextJS has become a popular framework, extending ReactJS. With that, I was motivated to use the Markdown content of my NextJS web app, but I had trouble simply relying on their documentation, so I thought I would document what I got working.

  1. Use Markdown for a text-heavy portion of a page of my web app, and
  2. To be able to have an entire page of content come from a single Markdown page

The former is a feature of NextJS to allow markdown content to be imported as a “component,” which can be rendered like any component in ReactJs.

The latter would rely NextJS’s App Router that assumes many URL paths for the app parallel the file/directory hierarchy of the app itself. So, merely placing a “page” file in the app’s directory will create a new URL path for the website. That means that you can create a “page.md” or “page.mdx” file with Markdown text content to define the content for that URL. .mdx files also allow JSX syntax so that React/Next components can be embedded within the Markdown. Read the rest of this entry »

 
 

Command line Clipboard for macOS, Cygwin, and Linux

25 Mar

I use the command line a lot, even though I am on a graphical user-interface (GUI) on Windows, macOS, or Linux. And since I’m lazy, I write a lot of scripts to perform repetitive tasks. I stumbled across macOS commands that allow command line programs to copy/paste between the clipboard that we’re so used to using.

macOS pbpaste and pbcopy

macOS has two commands, pbpaste and pbcopy, which “paste” from the pasteboard to stdout and copies from stdin to the pasteboard, respectively. Read the rest of this entry »

 

ChatGPT: What is the common way to parse command line parameters with Rust?

04 Mar

Rust Programming Language logoIn Rust, the most common way to parse command-line parameters is by using the std::env::args function, which returns an iterator over the command-line arguments passed to the program. Read the rest of this entry »

 

Ruby RSpec FactoryBot traits and transient via ChatGPT

15 Dec

The following was edited together from a series of questions to ChatGPT on the topic. Currently, I cannot identify sources of the originating content. My role was to edit ChatGPT into the following.

FactoryBot

RSpec is a testing framework for the Ruby programming language, and FactoryBot (formerly known as Factory Girl) is a library for creating test data in Ruby. Together, these tools can be used to write unit-tests for a Ruby application.

In FactoryBot, a factory is a blueprint for creating test data objects. A factory can define various attributes of the objects it creates, such as the object’s type, the values of its attributes, and any associations it has with other objects.

Read the rest of this entry »
 
 

Frontend Security Recipe Checklist

03 Jan

Even as the number of frontend programming vulnerabilities grows continually, many are not difficult to combat; you simply need to remember to fortify your frontend security against them.

 
 

ReactJS: componentWillReceiveProps() is Dead! (*sniff*)

21 Apr

ReactJSThings evolve. ReactJS evolves. With version 16.3, there were several changes to the component life-cycle methods. In particular, componentWillReceiveProps is disappearing. In its place, they say, you can use the getDerivedStateFromProps static function. I found this a bit challenging, but I did find an interesting pattern when the component-state was dependent on fetched information.

I should mention that I had a specific goal to better encapsulate data within the component. While I could pass in all the needed data as properties, that would require the surrounding component know what to pass and how to get it. That shouldn’t necessarily be necessary; the component knows what it needs and how to retrieve it.

For example, say you have a component which accepts a phone number and displays the phone number and the state that it’s from. Certainly, you could write a simple component that accepts both pieces of information as properties.

<ShowPhoneLocation number="+12065551212" city="Seattle" />

Which might be implemented as:

class ShowPhoneLocation extends React.Component {
  render() {
    return (
      <div>{this.props.number} is from {this.props.city}</div>
    )
  } // render()
} // class ShowPhoneLocation

But, since the component should be able to infer the state from the phone number (by its area code), it shouldn’t be incumbent on its container to know what it is.

class ShowPhoneLocation extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    let location = getCityFromPhone(nextProps.number)
    return {
      city: location
    }
  }
  render() {
    return (
      <div>{this.props.number} is from {this.state.city}</div>
    )
  } // render()
} // class ShowPhoneLocation

That’s all well and good, but what if getCityFromPhone() has to call a web service? We don’t want getDerivedStateFromProps() to stall, waiting for a response. However, it is static and does not have a this reference to the object for which it is returning state; so an asynchronous fetch doesn’t know what object’s state to set. Instead, don’t wait for the result to save in the state, save the request’s Promise in the state and update the state, once the promise resolves.

function getCityFromPhone(number) {
  return fetch('http://saas.com/get/'+number+'/city') // Returns fetch promise
}
class ShowPhoneLocation extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    let location = getCityFromPhone(nextProps.number)
    return {
      city: location
    }
  }
  componentDidUpdate() {
    let location = this.state.city
    if (location instanceof Promise) {
      this.setState({ city: '...waiting...' })
      location.then(city => this.setState({ city: city }) )
        .catch(() => this.setState({ city: 'not found' }) )
    }
  }
  render() {
    return (
      <div>
        {this.props.number} is from {this.state.city instanceof Promise
         ? '...'
        : this.state.city}</div>
    )
  } // render()
} // class ShowPhoneLocation

In componentDidUpdate() you can define the completion handlers to set the object’s state, base on the returned information from the service.

It is a common pattern to perform a fetch in componentDidMount(). The problem is that there may not be enough information to perform the fetch, that early, or the information for the fetch changes after the component has been mounted.

I am going to miss componentWillReceiveProps()… without it, things become a bit more convoluted but it’s going the way of the Dodo.

 

HTTP via Telnet

02 May

Okay, back to basics, the very low level basics. HTTP is text based, telnet enables a network connection via a text-based command line. All good. We can use telnet to talk to a web server by manually typing text to it, if you know the details of the content syntax to send HTTP via telnet.

Though this is not done, often, it is still good to know how it can be done. In fact, since these basics rarely change, I shouldn’t have even had to write this down, since it should be innate.

Sending HTTP Using Telnet

Once you get telnet running (see section, below), it is just a matter of typing HTTP to the server, manually.

There are four pieces the first three are shown, here:

GET / HTTP/1.1
Host: cachecrew.com
Connection: close
  1. This is the start of the HTTP commands you can enter. The first line must specify the HTTP method. There are three parts: Read the rest of this entry »
 
 

Promises, Promises… Timeout!

25 Apr

I was playing with ES6’s new Promises, for the first time, this week. Then, I was looking at the ugliness of using a browser’s setTimeout() function and thought that it would look better as a Promise.

tl;dr summary: A Simple Promise Version of “setTimeout()”

If we do it right, you simply specify the timeout period and implement a handler for then() to invoke:

timeout(5000) // Delay for 5000 ms
   .then(function () {
      // Do, here, whatever should happen when the time has elapsed…
   });

Or, since this is ES6, we might as well use arrow-function shorthand:

timeout(5000).then( () => {
   // do your thing, here…
})

Implementation

The setTimeout() already functions a bit like a Promise, without matching the promise-pattern and syntax, so converting it to a Promise is pretty easy: Read the rest of this entry »

 
 

Simplicity via PHP Frameworks

05 Apr

PHP language and web-technologyA long time ago, I’d endeavored to create my own lightweight PHP framework, ccPHP. It was a good exercise, but never reached prime time. Around that time I began to notice others endeavoring to do the same thing (i.e., create simpler, lighter weight PHP frameworks that didn’t take more time to learn than to implement the project). I’ve been away for a while and things have changed.

  1. The continued refinement and simplification of frameworks.
  2. The greater emphasis on frontend apps to do more of the work that used to be done in the middle tier.
  3. The ubiquity of RESTful interfaces, the dividing line between front-ends and servers.

Micro-PHP Frameworks

I doubt that I will ever finish my own framework, now that others are a lot closer to the goals that I was moving towards. And, right now, I simply want to find a lightweight modular frameworks that truly help me get my core work done. Dragan Gaic did a great survey of the PHP hot frameworks in Top 12 Best PHP RESTful Micro Frameworks (Pro/Con).

A key feature of such frameworks is for them to be modular to allow me to pick and choose only the components I need, rather than have to install, understand, and run a bunch of functionality that I do not need. It makes it harder for the developer and impacts performance of the results. Model-View-Controler (MVC) is a common software architecture pattern. With the client side being far more capable of managing the view part, on its own, the server side can be simpler. Instead of cluttering up the server-side app with trying to display information, it can simply focus on securing data and access and providing data services via RESTful interfaces.

So, what I am looking for are pieces to help me access persistent data and build RESTful interfaces. And something to help keep all the code flow organized. Based on the article, I can look at Silex, Slim, Lumen, or Phalcon for the latter. For database access, Ive worked with RedBeans before and liked it, so I will probably pick that up again. If one of the frameworks doesnt help implement and configure a RESTful interface simply, then I may get back into the framework building business again.

Model: RedBeans
View: Frontend client
Controler: one of Silex, Slim, Lumen, or Phalcon

A WordPress bug allowed my website to be hacked. I thought this was lost forever until I thought to reconstitute it from http://Archive.org!!