npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

matheusicaro-node-framework

v1.0.8

Published

My custom basic configuration setups for a quick build of services and APIs for Node.

Downloads

1,182

Readme

@mi-node-framework (matheusicaro)

This framework is a pack of @matheusicaro custom basic configurations and setups for quickly building services and APIs in Node.js for short projects like hackathons, studies, challenges, etc. A bunch of resources here might be useful for our next project 😃👍

Installing

npm package

npm i matheusicaro-node-framework

Resources

Dependency Injection

An abstraction class for an injection container for TypeScript using TSyring.

This abstraction allows me to use dependency injection in a contract defined in this framework without knowing the main provider (TSyring in v1.0.0).

If we decide to use another dependency injection provider, it will be easier for me to implement it here and update the following services with the new @mi-node-framework version.

How to use it:

function registerProviders(this: DependencyRegistry): void {
  this.container.register(ProviderTokens.MyProvider, {
    useValue: new MyProvider()
  });
}

export { registerProviders };
import { DependencyRegistry } from 'matheusicaro-node-framework';

let dependencyRegistry: DependencyRegistry;

const getDependencyRegistryInstance = (): DependencyRegistry => {
  if (!dependencyRegistry) {
    dependencyRegistry = new DependencyRegistry([ registerProviders, ...and others]);
  }

  return dependencyRegistry;
};

export { getDependencyRegistryInstance };
// application layer

import { inject } from 'matheusicaro-node-framework';

class MyController {
  constructor(
    @inject(ProviderTokens.MyProvider)
    private myProvider: MyProviderPort
  ) {}

  public handler(): Promise<void> {
    this.myProvider.run();
  }
}

export { MyController };
// tests layer

describe('MyController', () => {
  const provider = getDependencyRegistryInstance().resolve(ProviderTokens.MyProvider);

  //...
});

Logger

This is a custom logger already setup with winston. The logger will be printed in the files app console

How to use it:

import { DependencyInjectionTokens } from 'matheusicaro-node-framework';

class MyController {
  constructor(
    @inject(DependencyInjectionTokens.Logger)
    private logger: LoggerPort
  ) {}

  public handler(): Promise<void> {
    this.logger.info('trace handler');
  }
}
  const logger = getDependencyRegistryInstance().resolve(ProviderTokens.MyProvider)

  logger.info(message)
  logger.info(message, { id: "...", status: "..." })

  logger.error(message)
  logger.error(message, { id: "...", status: "...", error })

  logger.exception(error): void;

Files location:

  • file: logs/exceptions.log
2024-11-27 14:47:58 [ ERROR ]==> uncaughtException: failed on starting the app Error: failed on starting the app
    at Timeout._onTimeout (/Users/matheus.icaro/DEVELOPMENT/repositories/test/mi-gateway-service/src/app.ts:41:9)
    at listOnTimeout (node:internal/timers:573:17)
    at processTimers (node:internal/timers:514:7)
  • file: logs/combined.log
2024-11-27 14:50:53 [ ERROR ]==> {"message":"failed on starting the app","logData":{"trace_id":"fake_id","originalError":{"message":"its fail","stack":"Error: its fail\n    at Timeout._onTimeout (/Users/matheus.icaro/DEVELOPMENT/repositories/test/mi-gateway-service/src/app.ts:44:11)\n    at listOnTimeout (node:internal/timers:573:17)\n    at processTimers (node:internal/timers:514:7)"}}}

2024-11-27 14:53:37 [ INFO ]==> {"message":"logging data for trace","logData":{"id":"fake_id"}}

Controller Base

The controller base is an abstract class with some useful resources to use, like handling errors and responding to the client with a pre-defined payload.

RestControllerBase

RestControllerBase is the a base controller to be used in rest implementations, recommended express.

import { RestControllerBase } from 'matheusicaro-node-framework';

class HealthController extends RestControllerBase {
  constructor() {
    super();
  }

  public async getHealth(_req: Request, res: Response): Promise<Response<HealthResponse>> {
    try {
      return res.status(200).json({ message: 'success' });
    } catch (error) {
      return this.handleErrorThenRespondFailedOnRequest({
        error,
        response: res,
        responseData: {
          status: 'FAILED',
          time: new Date()
        }
      });
    }
  }
}

export { HealthController };

Testing

Factory

A factory to build objects easier with overrides for their props.

How to use it:

  • src/application/domain/entities/my-object.ts
// your entity/object

export interface MyObject {
  id: string;
  status: 'OPEN' | 'CLOSED' | 'IN_PROGRESS';
}
  • tests/factories/my-object.factory.ts:
// declare any custom function to override specific params and make it easier to build specific objects
class MyObjectFactory extends Factory<MyObject> {
  closed() {
    return this.params({
      status: 'CLOSED',
    });
  }

  open() {
    return this.params({
      status: 'OPEN',
    });
  }
}

// return the default object when build it
const myObjectFactory = MyObjectFactory.define(() => ({
  id: 'some-id',
  status: 'IN_PROGRESS',
}));

export { myObjectFactory };
    import { myObjectFactory } from '../factories/my-object.factory.ts';


    it('should find all closed status', async () => {
      
      // build the object in the desired state pre-defined
      const myObject = myObjectFactory.closed().build();

      stubDatabase.findOne.mockResolvedValueOnce(myObject);

      const result = await provider.findAllClosedStatus();

      expect(result).toEqual([myObject]);
    });


    it('should find by id', async () => {

      //override the build with any value for the fields from your object
      const myObject = myObjectFactory.build({ id: "any id" });

      stubDatabase.findOne.mockResolvedValueOnce(myObject);

      const result = await provider.findById("any id");

      expect(result).toEqual(myObject);
    });

JestStub

JestStub is a stub using (jest.fn()) for interface, types and objects. You can easily stub/mock when you are testing.

  import { jestStub } from 'matheusicaro-node-framework';

  //...

  const stubMyInterface = jestStub<MyInterface>();

  const myClass = new MyClass(stubMyInterface)

  //...

  test('should stub function correctly and set id', async () => {
    const userId = "id",

    stubMyInterface.anyMethod.mockResolvedValueOnce(100);

    const result = myClass.run(userId)

    expect(result).toEqual(100);
    expect(stubMyInterface).toHaveBeenCalledTimes(1);
    expect(stubMyInterface).toHaveBeenCalledWith(userId);
  });

VitestStub

VitestStub is a stub that uses Vitest (vi.fn()) for interfaces, types, and objects. You can easily stub/mock when testing with Vitest.

  import { vitestStub } from 'matheusicaro-node-framework';

  //...

  const stubMyInterface = vitestStub<MyInterface>();

  const myClass = new MyClass(stubMyInterface)

  //...

  test('should stub function correctly and set id', async () => {
    const userId = "id",

    stubMyInterface.anyMethod.mockResolvedValueOnce(100);

    const result = myClass.run(userId)

    expect(result).toEqual(100);
    expect(stubMyInterface).toHaveBeenCalledTimes(1);
    expect(stubMyInterface).toHaveBeenCalledWith(userId);
  });

DeepStubObject

DeepStubObject is a deep typer to be used when JestStub or VitesStub don't return the deeper prop.

  import { jestStub, vitestStub, DeepStubObject } from 'matheusicaro-node-framework';

  //...

  let stubProvider: ProviderInterface & DeepStubObject<ProviderInterface>;

  beforeAll(() => {

    // with jestStub
    stubProvider = jestStub<ProviderInterface>();

    // with vitestStub
    stubProvider = vitestStub<ProviderInterface>();

    myClass = new MyClass(stubProvider);
  });

  test('should stub function correctly and set id', async () => {
    //...
    stubMyInterface.anyProp.deepProp.deeperProp.mockResolvedValueOnce(100);
  });

Errors

You can use some custom errors in your business logic already implemented from ErrorBase which handles with logger and traces.

ErrorBase

you can implement your own errors from this ErrorBase.

class MyCustomErrorError extends ErrorBase {
  constructor(message: string);
  constructor(trace: InvalidStateErrorTrace);
  constructor(message: string, trace?: InvalidStateErrorTrace);
  constructor(messageOrTrace: string | InvalidStateErrorTrace, _trace?: InvalidStateErrorTrace) {
    const { message, trace } = alignArgs(messageOrTrace, _trace);

    super(ErrorCode.INVALID_STATE, InvalidStateError.name, message, {
      userMessage: trace?.userMessage,
      originalError: trace?.logData?.error,
      ...(trace?.logData && {
        logs: {
          data: trace?.logData,
          level: LogLevel.ERROR,
          instance: container.resolve<LoggerPort>(DependencyInjectionTokens.Logger)
        }
      })
    });
  }
}

export { InvalidStateError };

InvalidArgumentError

InvalidArgumentError is a type of error recommended to be used when an invalid argument is informed.

  • surface to the user with a known message for the invalid argument.
  • Log automatically the error & "trace" field when it is present in the args
    • new InvalidArgumentError(message) => do not error & message - new InvalidArgumentError(message, trace) => do log message and trace fields
new InvalidArgumentError('invalid argument', { userMessage: 'friendly user message', logData: { traceId: 'id' } });
  • userMessage can be sent in the response automatically when using RestControllerBase (here)

InvalidRequestError

InvalidRequestError is a type of error recommended to be used when an invalid argument is informed.

  • surface to the user with a known message for the invalid request.
  • Log automatically the error & "trace" field when it is present in the args
    • new InvalidRequestError(message) => do not error & message
    • new InvalidRequestError(message, trace) => do log message and trace fields
new InvalidRequestError('invalid request', { userMessage: 'friendly user message', logData: { traceId: 'id' } });
  • userMessage can be sent in the response automatically when using RestControllerBase (here)

InvalidStateError

InvalidStateError is a type of error recommended to be used when an invalid state is found and your app is not able to handle with.

  • surface to the user as a default error message (if not informed) once there is nothing the user can do at this point to fix the request
  • Log automatically the error & "trace" field when it is present in the args
    • new InvalidStateError(message) => do not error & message
    • new InvalidStateError(message, trace) => do log message and trace fields
new InvalidStateError('invalid state found', { userMessage: 'friendly user message', logData: { traceId: 'id' } });
  • userMessage can be sent in the response automatically when using RestControllerBase (here)

NotFoundError

NotFoundError is a type of error recommended to be used when a resource is not found.

  • surface to the user as an unknown error once there is nothing the user can do at this point to fix the request.
  • Log automatically the error & "trace" field when it is present in the args
    • new InvalidStateError(message) => do not error & message
    • new InvalidStateError(message, trace) => do log message and trace fields
new NotFoundError('doc was not found', { userMessage: 'friendly user message', logData: { docId: 'id' } });
  • userMessage can be sent in the response automatically when using RestControllerBase (here)