Skip to content

Commit

Permalink
chore: release v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
twlite committed Apr 24, 2023
1 parent b46f825 commit 71b3500
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 37 deletions.
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"arrowParens": "always",
"trailingComma": "none",
"semi": true,
"singleQuote": true,
"printWidth": 120,
"endOfLine": "lf",
"tabWidth": 4
}
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Simple, functional http-client router for JavaScript/TypeScript
* Works with any http client
* Simple API
* Zero dependencies
* TypeScript support

# Installation

Expand All @@ -19,15 +20,24 @@ $ npm install --save nxrouter
# Example

```ts
import { NxRouter } from 'nxrouter';
import { NxRouter, MethodImplementor, ParamArgs } from 'nxrouter';

// optional API routes definition
interface ApiRoutes {
demo: {
posts: MethodImplementor<{
(id: ParamArgs): MethodImplementor;
}>;
}
}

// base url endpoint
const BASE = 'https://my-json-server.typicode.com/typicode';

// nxrouter
const client = new NxRouter({
const client = new NxRouter<ApiRoutes>({
// request implementor
async onRequest(router, options) {
async onRequest(options) {
console.log(`Requesting ${options.path}`);

// here we make request using fetch api
Expand All @@ -53,12 +63,16 @@ console.log(await client.api.demo.posts.get<APIResponse[]>());

// initiate GET /demo/posts/1
console.log(await client.api.demo.posts(1).get<APIResponse>());

// log the value
console.log(client.api.demo.posts(5).toString());
// -> /demo/posts/5
```

## Request Methods

```js
const client = new NxRouter(...);
const client = new NxRouter<OptionalRouteDefinition>(...);

// GET
client.api.get();
Expand All @@ -83,4 +97,15 @@ client.api.head();

// TRACE
client.api.trace();
```

## Reflection Methods

```js
const client = new NxRouter<OptionalRouteDefinition>(...);

// path result
client.api.toString();
client.api.toJSON();
client.api.valueOf();
```
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
"name": "nxrouter",
"packageManager": "[email protected]",
"description": "Simple, functional http-client router for JavaScript/TypeScript",
"version": "0.1.0",
"version": "1.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": ["dist"],
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"test": "node ./test/index.mjs"
"test": "node ./test/index.mjs",
"format": "prettier --write \"./{src,test}/**/*.{js,ts,mjs,mts}\""
},
"repository": {
"type": "git",
Expand All @@ -33,6 +36,7 @@
},
"homepage": "https://github.com/neplextech/nxrouter#readme",
"devDependencies": {
"prettier": "^2.8.8",
"tsup": "^6.7.0",
"typescript": "^5.0.4"
}
Expand Down
54 changes: 33 additions & 21 deletions src/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,59 @@

import type { NxRouter, NxRequestMethod } from './index';

const REFLECTORS = ["toJSON", "toString", "valueOf"] as const;
const METHODS: Array<Lowercase<NxRequestMethod>> = ["get", "put", "post", "patch", "delete", "connect", "head", "trace", "options"];
const noop = () => undefined;
export const REFLECTORS = ['toJSON', 'toString', 'valueOf'] as const;
export const METHODS: Array<Lowercase<NxRequestMethod>> = [
'get',
'put',
'post',
'patch',
'delete',
'connect',
'head',
'trace',
'options'
];
export const noop = () => undefined;

type ParamArgs = string | number | boolean;
export type ParamArgs = string | number | boolean;

export type ProxyHandlerReq = {
export type HttpMethodImplementor = {
[prop in Lowercase<NxRequestMethod>]: <T = unknown>(data?: unknown) => Promise<T>;
};

export type ProxyReflectorHandler = {
export type MethodImplementor<T extends object = {}> = HttpMethodImplementor & ReflectionHandler & T;

export type ReflectionHandler = {
[prop in (typeof REFLECTORS)[number]]: (data?: { query: Record<string, string> }) => string;
};

export type NestedRouteCalls = {
(...args: ParamArgs[]): NxRouterExecutor;
[prop: string]: NxRouterExecutor;
(...args: ParamArgs[]): DefaultNxRouterExecutor;
[prop: string]: DefaultNxRouterExecutor;
};

export type PathRegisterFunc = NestedRouteCalls & NxRouterExecutor;
export type PathRegisterFunc = NestedRouteCalls & DefaultNxRouterExecutor;

export type ProxyHandlerPath = {
export type UntypedPath = {
[prop: string]: PathRegisterFunc;
};

export type NxRouterExecutor = ProxyHandlerReq & ProxyReflectorHandler & ProxyHandlerPath & NestedRouteCalls;
export type DefaultNxRouterExecutor = HttpMethodImplementor & ReflectionHandler & UntypedPath & NestedRouteCalls;

export function createRouter(nx: NxRouter): NxRouterExecutor {
export function createRouter<N extends object = DefaultNxRouterExecutor>(nx: NxRouter<N>): N {
const params: string[] = [];

const handler = {
get(_target, p: string, _receiver) {
if (METHODS.indexOf(p as typeof METHODS[number]) !== -1) {
if (METHODS.indexOf(p as (typeof METHODS)[number]) !== -1) {
return (data?: object & { query?: Record<string, string> }) => {
const queryParams = data?.query
? Object.entries(data.query)
.map(([m, n]) => `${m}=${n}`)
.join("&")
.map(([m, n]) => `${m}=${n}`)
.join('&')
: null;
return nx.dispatchRequest({
path: `/${params.join("/")}${queryParams ? "?" + queryParams : ""}`,
path: `/${params.join('/')}${queryParams ? '?' + queryParams : ''}`,
data: data || {},
method: p.toUpperCase() as NxRequestMethod
});
Expand All @@ -51,10 +63,10 @@ export function createRouter(nx: NxRouter): NxRouterExecutor {
return (data?: { query?: Record<string, string> }) => {
const queryParams = data?.query
? Object.entries(data.query)
.map(([m, n]) => `${m}=${n}`)
.join("&")
.map(([m, n]) => `${m}=${n}`)
.join('&')
: null;
return `/${params.join("/")}${queryParams ? "?" + queryParams : ""}`;
return `/${params.join('/')}${queryParams ? '?' + queryParams : ''}`;
};
}

Expand All @@ -67,5 +79,5 @@ export function createRouter(nx: NxRouter): NxRouterExecutor {
}
} as ProxyHandler<typeof noop>;

return new Proxy(noop, handler) as unknown as NxRouterExecutor;
}
return new Proxy(noop, handler) as unknown as N;
}
16 changes: 9 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createRouter } from "./executor";
import { createRouter, DefaultNxRouterExecutor } from './executor';

export type NxRequestImplementor = <O = unknown, T = unknown>(router: NxRouter, options: NxRequestInit<O>) => Promise<T>;
export * from './executor';

export type NxRequestImplementor = <O = unknown>(options: NxRequestInit<O>) => Promise<any>;

export interface NxRequestInit<T = unknown> {
path: string;
Expand All @@ -14,32 +16,32 @@ export interface NxRouterInit {
onRequest: NxRequestImplementor;
}

export class NxRouter {
export class NxRouter<R extends object = DefaultNxRouterExecutor> {
/**
* Create NxRouter client
* @param options Options to initialize NxRouter
*/
public constructor(public options: NxRouterInit) { }
public constructor(public options: NxRouterInit) {}

/**
* NxRouter executor
*/
public get api() {
return createRouter(this);
return createRouter<R>(this);
}

/**
* Simulate api request
* @param options Options to execute this method
* @returns req implementor's response
*/
public dispatchRequest<O = unknown, T = unknown>(options: NxRequestInit<O>) {
public async dispatchRequest<O = unknown, T = unknown>(options: NxRequestInit<O>): Promise<T> {
const fn = this.options.onRequest;

if (typeof fn !== 'function') {
throw new Error('request implementor not found');
}

return fn<O, T>(this, options);
return fn<O>(options);
}
}
4 changes: 2 additions & 2 deletions test/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { NxRouter } from '../dist/index.mjs';
const BASE = 'https://my-json-server.typicode.com/typicode';

const client = new NxRouter({
async onRequest(router, options) {
async onRequest(options) {
console.log(`Requesting ${options.path}`);
const res = await fetch(`${BASE}${options.path}`, {
method: options.method,
Expand All @@ -20,4 +20,4 @@ const client = new NxRouter({
console.log(await client.api.demo.posts.get());

// get /demo/posts/1
console.log(await client.api.demo.posts(1).get());
console.log(await client.api.demo.posts(1).get());
42 changes: 42 additions & 0 deletions test/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NxRouter, MethodImplementor, ParamArgs } from '../src/index';

// base url endpoint
const BASE = 'https://my-json-server.typicode.com/typicode';

interface ApiRoutes {
demo: {
posts: MethodImplementor<{
(id: ParamArgs): MethodImplementor;
}>;
};
}

// nxrouter
const client = new NxRouter<ApiRoutes>({
// request implementor
async onRequest(options) {
console.log(`Requesting ${options.path}`);

// here we make request using fetch api
const res = await fetch(`${BASE}${options.path}`, {
method: options.method,
...options.data
});

if (!res.ok) throw new Error(`Failed with status code ${res.status}`);

// and return json response
return await res.json();
}
});

interface APIResponse {
id: number;
title: string;
}

// initiate GET /demo/posts
console.log(await client.api.demo.posts.get<APIResponse[]>());

// initiate GET /demo/posts/1
console.log(await client.api.demo.posts(1).get<APIResponse>());
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "nxrouter@workspace:."
dependencies:
prettier: ^2.8.8
tsup: ^6.7.0
typescript: ^5.0.4
languageName: unknown
Expand Down Expand Up @@ -1402,6 +1403,15 @@ __metadata:
languageName: node
linkType: hard

"prettier@npm:^2.8.8":
version: 2.8.8
resolution: "prettier@npm:2.8.8"
bin:
prettier: bin-prettier.js
checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8
languageName: node
linkType: hard

"promise-inflight@npm:^1.0.1":
version: 1.0.1
resolution: "promise-inflight@npm:1.0.1"
Expand Down

0 comments on commit 71b3500

Please sign in to comment.