https://github.com/makerxstudio/node-common
A set of MakerX core NodeJS types and utilities
https://github.com/makerxstudio/node-common
node-common npm package typescript
Last synced: about 2 months ago
JSON representation
A set of MakerX core NodeJS types and utilities
- Host: GitHub
- URL: https://github.com/makerxstudio/node-common
- Owner: MakerXStudio
- License: mit
- Created: 2022-08-30T22:59:51.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2024-02-14T19:03:28.000Z (over 2 years ago)
- Last Synced: 2024-03-14T21:23:11.923Z (over 2 years ago)
- Topics: node-common, npm, package, typescript
- Language: TypeScript
- Homepage:
- Size: 973 KB
- Stars: 0
- Watchers: 12
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Node Common
A set of MakerX core NodeJS types and utilities.
## Environment
This module standardises minimal environment definitions based on the NODE_ENV environment variable.
| NODE_ENV | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| localdev | Indicates a local development setup, code can expect to have `devDependencies` installed, logging is expected to be more verbose etc |
| dev | Indicates a deployed environment with non-production data and behaviour |
| production | Indicates the live production environment with real data and optimised behaviour |
- `environment` returns process.env.NODE_ENV
- `isLocalDev` indicates whether the environment is `localdev`
- `isDev` indicates whether the environment is `dev`
- `isProduction` indicates whether the environment is `production`
## Logger
This type can be used to optionally emit logging from packages without taking a dependency on any specific logging framework.
```ts
export type Logger = {
error(message: string, ...optionalParams: unknown[]): void
warn(message: string, ...optionalParams: unknown[]): void
info(message: string, ...optionalParams: unknown[]): void
verbose(message: string, ...optionalParams: unknown[]): void
debug(message: string, ...optionalParams: unknown[]): void
}
```
Example usage:
```ts
logger?.verbose('About to do something')
const result = doSomething()
logger?.info('Did something', { result })
```
The `Logger` representation is compatible with [Winston](https://github.com/winstonjs/winston).
Or, if you want console output, you could use:
```ts
const logger: Logger = {
error: (message: string, ...params: unknown[]) => console.error
warn: (message: string, ...params: unknown[]) => console.warn
info: (message: string, ...params: unknown[]) => console.info
verbose: (message: string, ...params: unknown[]) => console.trace
debug: (message: string, ...params: unknown[]) => console.debug
}
```
## HTTP
### HttpClient
`HttpClient` is a class wrapping `fetch` to make calling Web API endpoints slightly easier:
- supports setting a base URL so relative paths can be used
- supports providing an function to set an authorization header on every request
- provides individual GET POST PUT PATCH DELETE methods
- provides default behaviour of reading the response body as JSON
- supports a form-urlencoded body POST via `postForm` method
- extracts some response info into an thrown error for non-200 responses to make error responses visible in logs
Example:
```ts
export class ThingClient extends HttpClient {
constructor(options: HttpClientOptions) {
super(options)
}
public things(): Promise {
return this.get(`things`)
}
public thing(id: string): Promise {
return this.get(`things/${id}`)
}
public async createThing(thingInput: { name: string; date: Date }): Promise {
const thing = await this.post(`things`, { data: thingInput })
this.options.logger.info('Created a thing', { thingInput, thing })
}
}
const onBehalfOfAuthFactory: HttpAuthFactory = async ({ user }) => {
const { access_token, expires_in } = await getOnBehalfOfToken({ ...oboConfig, assertionToken })
return { authorization: `Bearer ${access_token}` }
}
export const createServices = (context: BaseContext): Services => {
const httpClientOptions = {
requestContext: context,
logger: context.logger,
correlationId: context.requestInfo.correlationId,
}
return {
thingClient: new ThingClient({
...httpClientOptions,
baseUrl,
authFactory: onBehalfOfAuthFactory,
}),
}
}
```
### makeHttpRequest
If the `HttpClient` class is too opinionated for your use case, or you simply want a stateless wrapper for the fetch api with some sensible defaults; we export the function `makeHttpRequest` which is what `HttpClient` uses internally. This function takes care of request logging but leaves request encoding and response decoding to the consumer which offers a higher degree of flexibility.
```ts
import { makeHttpRequest } from './http'
const usersResponse = await makeHttpRequest({
url: 'https://localhost:8080/api/users',
method: 'GET',
headers: {
Authorization: 'Bearer abc123def',
},
ensureSuccessStatusCode: false,
logger: myLogger,
requestLogLovel: 'debug',
logContext: {
service: 'My Service Name',
version: '1.0.0',
},
accept: 'application/json',
fetchInit: {
redirect: 'manual',
},
})
if (usersResponse.ok) {
const users = await usersResponse.json()
} else if (usersResponse.status === 302) {
const redirectLocation = usersResponse.headers.get('location')
} else {
// Do something with error code???
}
```
### HttpResponseError
A custom Error class which includes a `responseInfo` field to make investigating HTTP errors (via logs etc) a little easier.
A static `create` async factory will attempt to read the response body (as json, then text) and add it to the error.
```ts
const response = await fetch('https://broken.io/error', {
method: 'POST',
body,
})
if (!response.ok) throw await HttpResponseError.create(response, 'POST failed')
```
### Polyfilling fetch for NodeJS v16
When using NodeJS v16 or below, you must polyfill the global `fetch` function. We recommend using [node-fetch](https://github.com/node-fetch/node-fetch?tab=readme-ov-file#providing-global-access) as a drop-in polyfill choice. **Note:** Due to node 16 and earlier versions no longer being maintained, we also strongly recommend the project be updated to the current LTS version.
## Authorisation
A number of authorisation functions and `HttpAuthFactory` wrappers are exported:
### Client-Credentials flow
- `getClientCredentialsToken`: posts a client-credentials auth request to a token endpoint and returns an `AccessToken` response.
- `createClientCredentialsAuthFactory`: calls `getClientCredentialsToken` and returns an authorization header, caching the `AccessToken` response until it expires, when it will fetch a new token.
### On-Behalf-Of flow
- `getOnBehalfOfToken`: posts an on-behalf-of auth request to a token endpoint and returns an `AccessToken` response
- `createClientCredentialsAuthFactory`: calls `createOnBehalfOfAuthFactory` using an `assertionToken` and returns an authorization header.
### Basic auth
- `getBasicAuthHeader`: returns a `Basic {value}` auth header string based on the supplied username and password.
- `createBasicAuthFactory`: calls `getBasicAuthHeader` and returns an authorization header.