Giter Site home page Giter Site logo

rayepps / exobase-js Goto Github PK

View Code? Open in Web Editor NEW
17.0 2.0 3.0 29.36 MB

A library that implements the Abstract & Compose design pattern, write you API or web service on any framework -- then switch.

Home Page: exobase-js.vercel.app

JavaScript 2.49% TypeScript 97.51%
aws express gcp lambda nextjs node

exobase-js's Introduction

Exobase

radash

Typescript Framework for Node Web Services & APIs

Install

To keep your dependencies lean, Exobase is split into seperate packages for each hook (learn about hooks here) + a core package. To install, you have two options.

  1. Install everything. This is the easiest way to get started quickly.
yarn add @exobase/core @exobase/hooks
  1. Only install what you need. If you only need the useJsonArgs and useCors hooks then only install those packages.
yarn add @exobase/use-json-args @exobase/use-cors

A lot of thought and effort is put into keeping the hooks small, minimal, and lean. The root hooks however typically depend on the framework libraries so you'll want to make sure you're only installing the specific ones you need.

yarn add @exobase/use-lambda
yarn add @exobase/use-express
yarn add @exobase/use-next

Getting Started

Using our Express example project you can have an API running in a few minutes. Here's a simple health check endpoint.

import { compose } from 'radash'
import type { Props } from '@exobase/core'
import { useExpress } from '@exobase/use-express'

export const ping = async ({ args, services }: Props<Args, Services>) => {
  return {
    message: 'pong'
  }
}

export default compose(useExpress(), ping)

exobase-js's People

Contributors

manas-vessel avatar sodiray avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

exobase-js's Issues

QUESTION: centralized error handling

I've got a large project using Exobase and I have the same pattern in my endpoints:

export const handler = _.compose(
  useLambda(),
  useCatch((props, error) => {
    if (error.error instanceof ExobaseError) {
      const ret = {
        ...props.response,
        status: error.error.status,
        body: { message: error.error.message },
        key: error.error.key,
      };
      return ret;
    }
    throw error;
  }),
  useCors(),
 ...
);

Is there a way to compose useLambda, useCatch, and useCors into a single hook that I can include in all my endpoints? I've mucked around with compose and chain from radash, but I haven't been able to get that working.

QUESTION: dependency injection for unit tests

So how do you do dependency injection for testing with this framework? Say I have a function, listThings, implemented like so:


import type { Props } from '@exobase/core';
import makeStorageService, {
  StorageService,
} from '../storage-service';
import { useService } from '@exobase/hooks';
import _ from 'radash';
import config from '../config';

interface Args {
  listThingsArgs: any;
}

interface Services {
  storage: StorageService;
}

type Response = Array<string>;

async function listThings({args, services}: Props<Args, Services>): Promise<Response> {
  return this.services.storage.listThings(args.listThingsArgs)
}

export default _.compose(
  useService<Services>({
    storage: await makeStorageService(config.storageService)
  }),
  listThings
);

(As I'm sure you can deduce, I lifted a lot of this pattern from your published endpoint code)

So two things to note here:

First, there's no framework code here. While this is intended to run in Lambda / API Gateway, my goal is to be able to compose the framework code from Exobase with my function code so that I can very easily port this to another framework if need be.

Second, I'm loading my config in the function definiiton for listThings. If I want to use a custom config for my tests, how am I supposed to inject that? Or should I just mock the call to makeStorageService?

FEATURE REQUEST: Hook for passing AWS API Gateway path parameters

I'd like a hook that allows me to extract { path: 'foo/bar' } from the path /my/endpoint/foo/bar where the endpoint is defined in the API as /my/endpoint/{path}. I've implemented it in my own project as follows, but I didn't want to submit a PR as I don't have tests defined for it and I don't know where it would be appropriate to put it in the codebase, as it's a hook but an API Gateway specific hook.

export { useJsonArgs, useQueryArgs, useHeaderArgs } from '@exobase/hooks';
import { partial } from 'radash';
import { useValidation } from '@exobase/hooks';
import { Props, ApiFunction, Request } from '@exobase/core';
import { APIGatewayProxyEvent } from 'aws-lambda';
import * as yup from 'yup';

type APIGatewayProxyRequest = Request & { event: APIGatewayProxyEvent };
type Yup = typeof yup;
type KeyOfType<T, Value> = { [P in keyof T]: Value };

/**
 * Get args from the parsed path parameters (AWS API Gateway-specific)
 */
export const useLambdaPathArgs = partial(
  useValidation,
  (props: Props<any, any, any, APIGatewayProxyRequest>) =>
    props.req.event.pathParameters
) as <TArgs = any>(
  shapeMaker: (yup: Yup) => KeyOfType<TArgs, any>
) => (func: ApiFunction) => ApiFunction;

body-parser error results in abrupt connection close

when using exbobase w/ express and sending in a malformed json body to a POST

(extra comma at the end of mypayload)

{
    "mybody": {
        "name": "mypayload", 
    }
}

the server abruptly terminates

SyntaxError: Expected double-quoted property name in JSON at position 103 (line 4 column 5)
    at JSON.parse (<anonymous>)
    at parse (/app/node_modules/.pnpm/[email protected]/node_modules/body-parser/lib/types/json.js:89:19)
    at /app/node_modules/.pnpm/[email protected]/node_modules/body-parser/lib/read.js:128:18
    at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
    at invokeCallback (/app/node_modules/.pnpm/[email protected]/node_modules/raw-body/index.js:231:16)
    at done (/app/node_modules/.pnpm/[email protected]/node_modules/raw-body/index.js:220:7)
    at IncomingMessage.onEnd (/app/node_modules/.pnpm/[email protected]/node_modules/raw-body/index.js:280:7)
    at IncomingMessage.emit (node:events:519:28)
    at IncomingMessage.emit (node:domain:488:12)
    at endReadableNT (node:internal/streams/readable:1696:12) {
  expose: true,
  statusCode: 400,
  status: 400,
  body: '{\n' +
    '    "mybody": {\n' +
    '        "name": "mypayload",\n' +
    '    }\n' +
    '}',
  type: 'entity.parse.failed'
}
Node.js v21.5.0
 ELIFECYCLE  Command failed with exit code 1.
/app/apps/component-api:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @xeol/[email protected] start: `pnpm with-env tsx src/server.ts`
Exit status 1

in postman I see

Could not send request: Error: connect ECONNREFUSED 127.0.0.1:3000

the line throwing is

  const requestAfterMiddlware = await middleware(req, res)

which I can get to not fail with a try/catch

I've also tried to catch by adding a top level express error handler but logic never reaches this far before blowing up

app.use((err: Error, req: Request, res: Response) => {
    ...
});

FEATURE REQUEST: "strict" option in hooks/useQueryString

I'd like the option to pass the "strict" option to the call to zod.object in useQueryString so that I can immediately return an error on an unrecognized query param. I've implemented it myself like so:

export const useQueryString: <TArgs extends {}, TProps extends Props = Props>(
  shapeMaker: (z: Zod) => KeyOfType<TArgs, any>,
  strict?: boolean
) => (
  func: Handler<
    TProps & {
      args: TProps['args'] & TArgs;
    }
  >
) => Handler<TProps> = (shapeMaker, strict) => (func) => {
  let model: AnyZodObject;
  if (strict) {
    model = zod.object(shapeMaker(zod)).strict();
  } else {
    model = zod.object(shapeMaker(zod));
  }
  return (props) => withQueryString(func as Handler, model, null, props);
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.