Adapters

Adapters are a way of extending your exported API functionality with custom functions that execute on top of your defined router.

There are many options as to what you can do with an adapter, some of the use-cases include:

Declaration & Usage

Adapters are defined as an optional argument of the router call.

import { api } from '@hulla/api'

const a = api()
export const withAdapters = a.router({
  name: 'example',
  routes: [],
  adapters: {
    logRouterName: (adapter) => () => console.log(adapter.routerName)
  }
})

withAdapters.logRouterName() // 'example'

Router Adapter API

One thing that warrants a special mention is the fact, that router contains not only all of the methods of your router API, but also the following internal methods, that can help you write more effective adapters:

Check out the Adapter API Reference for more info

Adapter declaration syntax

If you studied the initial example with a keen eye, you may have noticed, that the adapter logRouterName is a curried callback - a function that returns a function. This is intentional, to prevent a common mistake a lot of people make when writing their adapters. Consider this:

adapters: { isAuthenticated: (_adp) => db.auth() } // 😱 DON'T DO THIS
// note we don't need adapter arg here, just making it explicit it's available if necessary

Looks relatively ok on first glance, and you’d expect it to work as usersAPI.isAuthenticated // boolean but this is actually a mistake! Adapters are executed at the create call with the passed router and this means, every time you tried using usersAPI it would execute the db.auth(), which could potentially be an issue, especially if you are using it inside client JSX that continually hydrates. For this reason, the line above actually a type error!

The proper way to do this would be:

const usersAPI = a.router({
  name: 'users',
  routes: [],
  adapters: { isAuthenticated: (_adp) => () => db.auth() } // ✅ correct
  //         note the extra callback here ^
})

Now instead of usersAPI.isAuthenticated we call usersAPI.isAuthenticated() - this executed the db.auth() only when we need it to, instead of every time the usersAPI is accessed.

Declaring constants (override)

As mentioned, you will get a type error if you attempt to create a constant instead of curried function.

This is to prevent you some pesky hydration errors and racking up your cloud service provider bills. That being said, if you despite the warnings want to override this, the package does give you the power to do it.

// @ts-expect-error i know what i'm doing and am aware it will run on every call
const your = a.router({
  name: 'smug-self-praise',
  routes: [],
  adapters: {
    // @ts-expect-error i know what i'm doing and am aware it will run on every call
    FAVORITE_PACKAGE: (_adp) => '@hulla/api'
  }
})

your.FAVORITE_PACKAGE // '@hulla/api'

Difference between adapters and custom methods

While both are primarily used by library authors for integrations, there are some key differences

DifferenceAdaptersCustom Methods
Intended useAdapting already defined routesA way of defining new routes
Defined inInside the .router() callInside the api() initalization
Used inin your API calls (exported .router() result)routes parameter of .router()
ArgumentRouterAdapterContext
Return TypeObject with adapter methodsObject with custom methods