0.2.0 • Published 3 years ago

@light-arrow/express v0.2.0

Weekly downloads
21
License
ISC
Repository
github
Last release
3 years ago

About

Light Arrow is a small library for type safe asynchronous programming in typescript. The library is based around the functional and composable Arrow data type. Arrows are data structures that describe asynchronous (and synchronous) operations that can succeed or fail and may have some dependencies. Please check out the documentation https://lauri3new.github.io/light-arrow-docs/ for a longer explanation and some examples.

This module exposes helper functions for building type safe http apps using the express framework and the Arrow data type. Please see this section https://lauri3new.github.io/light-arrow-docs/docs/HttpApp of the documentation for more detail, and this section https://lauri3new.github.io/light-arrow-docs/docs/Arrow for more on the Arrow data type.

Getting Started

Installation

npm install @light-arrow/arrow @light-arrow/express

Light-arrow/express provides bindings for writing type safe http apps. Instead of using native response methods we can instead describe the http response using a Result data type.

An HttpApp is simply a (ctx: Context) => Promise<Result>. To help with type safety and composability we can define HttpRoutes as Arrow<Context, notFound | Result, Result> and then use the seal function, providing handlers for 'not found' and exception cases. We can then bind an HttpApp to an express instance using the bindApp function, providing any dependencies the Arrows require at that time if we want.

interface Context {
  req: Request
}

We can express our whole express app using Arrows. Middlewares can be defined as Arrow<A, Result, B> where A and B extend the context. Multiple middlewares can be stacked together in a type safe manner using the andThen function. By defining our middleware this way we can expand and transform the context in a composable and type safe way, for example attaching services or authorisation data to the context.

An example Middleware

const authorizationMiddleware: Arrow<Context, Result, {
  loggedIn: boolean;
  req: Request;
}> = draw((ctx: Context) => {
  if (ctx.req.headers.authorization) {
    return succeed({ ...ctx, loggedIn: true })
  } else {
    return fail(Unauthorised({}))
  }
})

Handlers can be written as Arrow<A, Result, Result> where A is inferred from the middlewares. HttpRoutes are then Arrows of Type Arrow<A, notFound \ Result, Result>, and we can combine HttpRoutes together (similar to how we would use an express Router) using the orElse function.

An example HttpRoute

const getUsers: Arrow<{
  loggedIn: boolean;
  req: Request;
}, NotFound, Result> = get('/users')
  .andThen(draw((ctx: Context) => ctx.services.getUsers()))

Multiple routes and middlewares combined

const routes: Arrow<Context, NotFound | Result, Result> = orElse(
  get('/healthcheck').map(() => OK({})),
  authorizationMiddleware.andThen(
    orElse(
      getUsers,
      getTimeline
    )
  )
)

Converting HttpRoutes to an HttpApp

const httpApp = seal(
  routes,
  () => NotFound({
    message: 'not found'
  }),
  () => InternalServerError({
    message: 'oops something went wrong'
  })
)

Binding our HttpApp to an express instance

const expressInstance = express()
const { app } = bindApp(httpApp)(expressInstance)

app.listen(8080)

Once we have all the routes described we can convert the HttpRoutes to an HttpApp using the seal function, providing functions for converting the notFound type and runtime exceptions into http Results. We can then use the bindApp function to attach our httpApp to an express application instance and inject dependencies.

0.2.0

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.2

3 years ago

0.1.1

3 years ago

0.1.0

4 years ago