Comparing version 0.0.1 to 0.0.2
@@ -7,7 +7,8 @@ /// <reference types="node" /> | ||
import * as t from "io-ts"; | ||
export declare const withoutRequestBody: (method: "GET" | "DELETE" | "OPTIONS") => <TPath extends object, TResponseBody>(matcher: Match<TPath>, handler: (req: Readonly<{ | ||
export declare type THttpMethod = "GET" | "DELETE" | "OPTIONS" | "POST" | "PUT" | "PATCH"; | ||
export declare const get: <TPath extends object, TRequestQuery, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: any; | ||
@@ -20,7 +21,7 @@ stream: import("http").IncomingMessage; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const get: <TPath extends object, TResponseBody>(matcher: Match<TPath>, handler: (req: Readonly<{ | ||
export declare const options: <TPath extends object, TRequestQuery, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: any; | ||
@@ -33,7 +34,7 @@ stream: import("http").IncomingMessage; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const options: <TPath extends object, TResponseBody>(matcher: Match<TPath>, handler: (req: Readonly<{ | ||
export declare const del: <TPath extends object, TRequestQuery, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: any; | ||
@@ -46,19 +47,7 @@ stream: import("http").IncomingMessage; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const del: <TPath extends object, TResponseBody>(matcher: Match<TPath>, handler: (req: Readonly<{ | ||
export declare const post: <TPath extends object, TRequestQuery, TRequestBody, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
body: any; | ||
stream: import("http").IncomingMessage; | ||
}>) => Promise<Readonly<{ | ||
status: number; | ||
headers: import("fp-ts/lib/Option").Option<Record<string, string>>; | ||
body: TResponseBody; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const post: <TPath extends object, TRequestBody, TResponseBody>(matcher: Match<TPath>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: TRequestBody; | ||
@@ -71,7 +60,7 @@ stream: import("http").IncomingMessage; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const put: <TPath extends object, TRequestBody, TResponseBody>(matcher: Match<TPath>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
export declare const put: <TPath extends object, TRequestQuery, TRequestBody, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: TRequestBody; | ||
@@ -84,7 +73,7 @@ stream: import("http").IncomingMessage; | ||
}>>) => readonly composeMiddleware.Middleware<Koa.ParameterizedContext<any, {}>>[]; | ||
export declare const patch: <TPath extends object, TRequestBody, TResponseBody>(matcher: Match<TPath>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
export declare const patch: <TPath extends object, TRequestQuery, TRequestBody, TResponseBody>(matcher: Match<TPath>, queryType: t.Decoder<object, TRequestQuery>, bodyParser: t.Type<TRequestBody, TRequestBody, unknown>, handler: (req: Readonly<{ | ||
status: number; | ||
headers: Record<string, string>; | ||
url: string; | ||
path: TPath; | ||
path: TPath & TRequestQuery; | ||
body: TRequestBody; | ||
@@ -91,0 +80,0 @@ stream: import("http").IncomingMessage; |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
@@ -51,6 +62,7 @@ return new (P || (P = Promise))(function (resolve, reject) { | ||
var Middleware_1 = require("./Middleware"); | ||
exports.withoutRequestBody = function (method) { | ||
return function (matcher, handler) { | ||
var io_ts_reporters_1 = require("io-ts-reporters"); | ||
var withoutRequestBody = function (method) { | ||
return function (matcher, queryType, handler) { | ||
return Middleware_1.from(function (ctx, next) { return __awaiter(_this, void 0, void 0, function () { | ||
var pathParser, match, request, response; | ||
var pathParser, pathRoute, matchPath, query, request, response; | ||
return __generator(this, function (_a) { | ||
@@ -62,6 +74,12 @@ switch (_a.label) { | ||
pathParser = matcher.parser.map(function_1.identity); | ||
match = fp_ts_routing_1.parse(pathParser, fp_ts_routing_1.Route.parse(ctx.request.url), null); | ||
if (util_1.isNull(match)) | ||
pathRoute = fp_ts_routing_1.Route.parse(ctx.request.path); | ||
matchPath = fp_ts_routing_1.parse(pathParser, pathRoute, null); | ||
if (util_1.isNull(matchPath)) | ||
return [2 /*return*/, next()]; | ||
request = Request_1.koaContextToRequest(ctx, match); | ||
query = queryType.decode(ctx.query); | ||
if (query.isLeft()) | ||
return [2 /*return*/, ctx.throw(400, JSON.stringify({ | ||
errors: io_ts_reporters_1.reporter(query) | ||
}))]; | ||
request = Request_1.fromKoaContext(ctx, __assign({}, matchPath, query.value)); | ||
return [4 /*yield*/, handler(request)]; | ||
@@ -77,9 +95,9 @@ case 1: | ||
}; | ||
exports.get = exports.withoutRequestBody("GET"); | ||
exports.options = exports.withoutRequestBody("OPTIONS"); | ||
exports.del = exports.withoutRequestBody("DELETE"); | ||
exports.get = withoutRequestBody("GET"); | ||
exports.options = withoutRequestBody("OPTIONS"); | ||
exports.del = withoutRequestBody("DELETE"); | ||
var withRequestBody = function (method) { | ||
return function (matcher, bodyParser, handler) { | ||
return function (matcher, queryType, bodyParser, handler) { | ||
return Middleware_1.from(function (ctx, next) { return __awaiter(_this, void 0, void 0, function () { | ||
var pathParser, match, decodedBody, request, response; | ||
var pathParser, pathRoute, matchPath, query, decodedBody, request, response; | ||
return __generator(this, function (_a) { | ||
@@ -91,9 +109,15 @@ switch (_a.label) { | ||
pathParser = matcher.parser.map(function_1.identity); | ||
match = fp_ts_routing_1.parse(pathParser, fp_ts_routing_1.Route.parse(ctx.request.url), null); | ||
if (util_1.isNull(match)) | ||
pathRoute = fp_ts_routing_1.Route.parse(ctx.request.path); | ||
matchPath = fp_ts_routing_1.parse(pathParser, pathRoute, null); | ||
if (util_1.isNull(matchPath)) | ||
return [2 /*return*/, next()]; | ||
query = queryType.decode(ctx.query); | ||
if (query.isLeft()) | ||
return [2 /*return*/, ctx.throw(400, JSON.stringify({ | ||
errors: io_ts_reporters_1.reporter(query) | ||
}))]; | ||
decodedBody = bodyParser.decode(ctx.request.body); | ||
if (decodedBody.isLeft()) | ||
return [2 /*return*/, ctx.throw(400)]; | ||
request = Request_1.koaContextToRequest(ctx, match, decodedBody.value); | ||
return [2 /*return*/, ctx.throw(400, "request body invalid")]; | ||
request = Request_1.fromKoaContext(ctx, __assign({}, matchPath, query.value), decodedBody.value); | ||
return [4 /*yield*/, handler(request)]; | ||
@@ -125,3 +149,11 @@ case 1: | ||
var koa = new koa_1.default(); | ||
var middleware = koa_compose_1.default([koa_body_1.default()].concat(stack)); | ||
var middleware = koa_compose_1.default([ | ||
function (ctx, next) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
ctx.set('content-type', 'application/json'); | ||
return [2 /*return*/, next()]; | ||
}); | ||
}); }, | ||
koa_body_1.default() | ||
].concat(stack)); | ||
koa.use(middleware); | ||
@@ -128,0 +160,0 @@ return new Promise(function (resolve) { return koa.listen(port, resolve); }); |
@@ -12,3 +12,3 @@ /// <reference types="node" /> | ||
}>; | ||
export declare const koaContextToRequest: <PT, BT>(ctx: Koa.Context, path: PT, body?: BT | undefined) => Readonly<{ | ||
export declare const fromKoaContext: <PT, BT = any>(ctx: Koa.Context, path: PT, body?: BT | undefined) => Readonly<{ | ||
status: number; | ||
@@ -18,4 +18,4 @@ headers: Record<string, string>; | ||
path: PT; | ||
body: any; | ||
body: BT; | ||
stream: IncomingMessage; | ||
}>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.koaContextToRequest = function (ctx, path, body) { | ||
exports.fromKoaContext = function (ctx, path, body) { | ||
return Object.freeze({ | ||
@@ -10,4 +10,4 @@ url: ctx.url, | ||
path: path, | ||
stream: ctx.req | ||
stream: ctx.req, | ||
}); | ||
}; |
{ | ||
"name": "fp-ts-http", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Opinionated Typesafe http middleware library using fp-ts-routing and io-ts", | ||
"keywords": ["functional", "typesafe", "http", "rest", "routing", "fp-ts", "fp-ts-routing", "io-ts"], | ||
"keywords": [ | ||
"functional", | ||
"typesafe", | ||
"http", | ||
"rest", | ||
"routing", | ||
"fp-ts", | ||
"fp-ts-routing", | ||
"io-ts" | ||
], | ||
"files": [ | ||
"lib", | ||
"es6" | ||
], | ||
"typings": "lib/index.d.ts", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"dev": "nodemon --watch 'src/**/*.ts' --ignore 'src/**/*.test.ts' --exec 'ts-node' test.ts -- --experimental-modules", | ||
"build": "npm run clean && npm run build:lib && npm run build:es6", | ||
"dev": "nodemon --watch 'src/**/*.ts' --ignore 'src/**/*.test.ts' --exec 'ts-node' src/test.ts -- --experimental-modules", | ||
"build:watch": "concurrently 'npm run build:lib:watch' 'npm run build:es6:watch'", | ||
"build": "npm run clean && concurrently 'npm run build:lib' 'npm run build:es6'", | ||
"build:lib": "tsc", | ||
"build:es6": "tsc -p tsconfig.es6.json", | ||
"build:lib:watch": "tsc --watch", | ||
"build:es6:watch": "tsc --watch -p tsconfig.es6.json", | ||
"clean": "rimraf lib/* es6/*" | ||
@@ -27,2 +43,3 @@ }, | ||
"@types/node": "^12.0.10", | ||
"concurrently": "^4.1.1", | ||
"nodemon": "^1.19.1", | ||
@@ -43,2 +60,3 @@ "renamer": "^1.1.2", | ||
"io-ts": "^1.10.2", | ||
"io-ts-reporters": "0.0.21", | ||
"koa": "^2.7.0", | ||
@@ -45,0 +63,0 @@ "koa-body": "^4.1.0", |
# fp-ts-http | ||
### Install | ||
Includes `fp-ts` `fp-ts-routing` and `io-ts` | ||
```bash | ||
npm i fp-ts-http | ||
``` | ||
## Example | ||
```typescript | ||
import { end, lit, int } from 'fp-ts-routing' | ||
import * as t from "io-ts" | ||
import { get, post, driver } from "fp-ts-routing" | ||
import { TMiddlewareStack } from 'fp-ts-routing/lib/Middleware'; | ||
import * as io from "io-ts" | ||
import { get, post, driver } from "fp-ts-http" | ||
import { TMiddlewareStack } from 'fp-ts-http/lib/Middleware'; | ||
import { none } from 'fp-ts/lib/Option'; | ||
@@ -19,3 +28,3 @@ | ||
headers: none, | ||
body: "get success: " + req.path.userid | ||
body: `fetched user ${req.path.userid}` | ||
} | ||
@@ -25,3 +34,3 @@ })] | ||
const userMessages = userById.then(lit("messages")) | ||
const userMessageDto = t.type({ message: t.string }) | ||
const userMessageDto = io.type({ message: io.string }) | ||
@@ -39,3 +48,76 @@ const stack3 = [...stack2, ...post<{userid: number}, {message: string}, string>(userMessages.then(end), userMessageDto, async(req) => { | ||
.then(() => console.log("server running")) | ||
``` | ||
``` | ||
## Advanced query parsing example | ||
```typescript | ||
import { end, lit, query, str } from 'fp-ts-routing' | ||
import * as io from "io-ts" | ||
import { get, post, driver } from "." | ||
import { TMiddlewareStack } from './Middleware'; | ||
import { none } from 'fp-ts/lib/Option'; | ||
import { NumberFromString, BooleanFromString, ArrayFromString } from './Query'; | ||
const stack: TMiddlewareStack = [] | ||
type TUsersPath = { | ||
type: string; | ||
} | ||
const usersQuery = io.strict({ | ||
filter: io.union([ io.undefined, io.string ]), | ||
validStatusCodes: io.union([ io.undefined, ArrayFromString<number>(NumberFromString) ]), | ||
sortBy: io.union([ io.undefined, io.string]), | ||
sortDirection: io.union([ io.undefined, BooleanFromString]) | ||
}) | ||
type TUsersQuery = io.TypeOf<typeof usersQuery> | ||
type TUsersResponseBody = string | ||
const users = lit("users").then(str("type")) | ||
const stack2 = [...stack, ...get<TUsersPath, TUsersQuery, TUsersResponseBody>(users.then(end), usersQuery, async(req) => { | ||
return { | ||
status: 200, | ||
headers: none, | ||
body: await UserRepository.find({ | ||
type: req.path.type, | ||
sortBy: req.path.sortBy, | ||
sortDirection: req.path.sortDirection | ||
}) | ||
} | ||
})] | ||
type TUserMessageDto = { | ||
message: string | ||
} | ||
const userMessages = users.then(lit("messages")) | ||
const userMessageDto = io.type({ message: io.string }) | ||
type TUserMessagesResponseBody = string | ||
const stack3 = [...stack2, ...post<TUsersPath, TUsersQuery, TUserMessageDto, TUserMessagesResponseBody>(userMessages.then(end), usersQuery, userMessageDto, async(req) => { | ||
const users = await UserRepository.find({ | ||
type: req.path.type, | ||
sortBy: req.path.sortBy, | ||
sortDirection: req.path.sortDirection | ||
}) | ||
await Promise.all(users.map(async user => sendMessageToUser(user, req.body))) | ||
return { | ||
status: 200, | ||
headers: none, | ||
body: JSON.stringify(req.path) | ||
} | ||
})] | ||
driver(stack3, 3000).run() | ||
.then(() => console.log("server running")) | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
62378
34
1131
121
13
6
1
+ Addedio-ts-reporters@0.0.21
+ Addedio-ts-reporters@0.0.21(transitive)