Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add ecosystem test for tupleson #677

Merged
merged 3 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/popular-avocados-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@httpx/exception': patch
---

Add tupleson tests and improve docs
12 changes: 12 additions & 0 deletions packages/exception/.size-limit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ module.exports = [
import: "{ fromJson }",
limit: "2000B",
},
{
name: "ESM ({ toJson })",
path: ["dist/serializer/index.mjs"],
import: "{ toJson }",
limit: "950B",
},
{
name: "ESM ({ fromJson, toJson })",
path: ["dist/serializer/index.mjs"],
import: "{ fromJson, toJson }",
limit: "2000B",
},
// ###################################################
// Commonjs full bundle
// ###################################################
Expand Down
183 changes: 130 additions & 53 deletions packages/exception/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,137 @@ yarn add @httpx/exception # via yarn
pnpm add @httpx/exception # via pnpm
```

## Documentation
## Bundle size

**👉 See full documentation on [https://belgattitude.github.io/httpx](https://belgattitude.github.io/httpx). 👈**
This library is best consumed in ESM, individual imports are tracked by a
size-limit action. In most situation the bundle size will be less than 1Kb.
That includes default messages :)

| Scenario | Size (esm -> min/gzip) |
| ------------------------------------------------ | ---------------------- |
| Import only one exception | ~ 400b |
| Import all exceptions or use createHttpException | < 1kb |
| All exceptions + typeguards + serializer | max 1.7kb |

## Usage

Basic usage below, but don't forget to check the
👉 full documentation on [https://belgattitude.github.io/httpx](https://belgattitude.github.io/httpx). 👈.
It includes serialization recipes, http422 with validation issues and more..

### Named

```typescript
import { HttpNotFound, HttpInternalServerError } from '@httpx/exception';

// 👉 1. Simple
const e404 = new HttpNotFound();

-> 🔥 e.statusCode === 404
-> 🔥 e.message inferred to be === "Not found"!

// 👉 2. Alternative custom message !
const e404 = new HttpNotFound("The graal is nowhere to be found");

// 👉 3. Custom params and (optional) context
const e500 = new HttpInternalServerError({
message: "Oups, this is on our side.",
url: "https://api.dev/gateway",
method: "POST",
errorId: 'track-me-in-the-logs',
code: 'custom internal code',
cause: new HttpGatewayTimeout({
code: "This Serverless Function has timed out",
errorId: "cdg1::h99k2-1664884491087-b41a2832f559",
}),
});
```

### Factory

```typescript
import { createHttpException } from '@httpx/exception';

// 👉 1. Simple
const e404 = createHttpException(404);
-> 🔥 e.statusCode === 404
-> 🔥 e.message inferred to be === "Not found"!

// 👉 2. Custom params and (optional) context

const e500 = createHttpException(500, {
message: "Oups, this is on our side.",
url: "https://api.dev/gateway",
method: "POST",
errorId: 'track-me-in-the-logs',
code: 'custom internal code',
cause: new HttpGatewayTimeout({
code: "This Serverless Function has timed out",
errorId: "cdg1::h99k2-1664884491087-b41a2832f559",
})
})
```

## Ecosystem

### Tupleson

Example with [tupleson](https://github.com/trpc/tupleson) serializer.

```typescript
import { createTson } from "tupleson";
import {
createHttpException,
HttpException,
HttpUnprocessableEntity,
} from "@httpx/exception";
import {
fromJson,
type SerializerError,
toJson,
} from "@httpx/exception/serializer";

const httpException: TsonType<HttpException | SerializerError, string> = {
deserialize: (v) => fromJson(v),
key: "HttpException",
serialize: (v) => toJson(v),
test: (v) => v instanceof HttpException,
};

const tson = createTson({
types: [httpException],
});

const obj = {
e422: new HttpUnprocessableEntity({
issues: [
{
message: "Invalid address",
path: ["addresses", 0, "line1"],
code: "empty_string",
},
],
}),
e404: createHttpException(404),
};

const serialized = tson.serialize(obj);
const deserialized = tson.deserialize(serialized);
expect(deserialized).toStrictEqual(obj);
```

## Serializer

Serialization

```typescript
import { fromJson, toJson } from "@httpx/exception/serializer";

const e = new HttpForbidden();

const json = toJson(e);
const deserialized = fromJson(json);
```

## Why ?

Expand Down Expand Up @@ -118,57 +246,6 @@ export const withApiErrorHandler = (params?: Params) => {

> Tip: @httpx/exception is small scoped by nature. The above example isn't to be taken "as is".

## Quick overview

Simple named exceptions:

```typescript
import {
HttpGatewayTimeout,
HttpInternalServerError,
HttpNotFound,
HttpServiceUnavailable,
} from "@httpx/exception";

throw new HttpNotFound(); // message = 'Not found', statusCode = 404

// Custom message
throw new HttpServiceUnavailable("Service temporarily unavailable");

// Custom context
throw new HttpInternalServerError({
message: "Oups, this is on our side.",
url: "https://api.dev/gateway",
code: "EXTERNAL_SERVICE_TIMEOUT",
cause: new HttpGatewayTimeout({
code: "This Serverless Function has timed out",
errorId: "cdg1::h99k2-1664884491087-b41a2832f559",
}),
});
```

By status code

```typescript
import { createHttpException } from "@httpx/exception";

const e404 = createHttpException(404);
const e500 = createHttpException(500, { message: "Server error" });
```

Serialization

```typescript
import { fromJson, toJson } from "@httpx/exception/serializer";

const e = new HttpForbidden();

const json = toJson(e);
const deserialized = fromJson(json);
```

More in the docs: [https://belgattitude.github.io/httpx](https://belgattitude.github.io/httpx)

## Support

Don't hesitate and open [an issue](https://github.com/belgattitude/httpx/issues).
Expand Down
2 changes: 1 addition & 1 deletion packages/exception/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"name": "Vanvelthem Sébastien",
"url": "https://github.com/belgattitude"
},
"homepage": "https://github.com/belgattitude/httpx",
"homepage": "https://belgattitude.github.io/httpx",
"repository": {
"type": "git",
"url": "https://github.com/belgattitude/httpx",
Expand Down
40 changes: 40 additions & 0 deletions packages/exception/test/specs/ecosystem.tupleson.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { TsonType } from 'tupleson';
import { createTson } from 'tupleson';
import {
createHttpException,
HttpException,
HttpUnprocessableEntity,
} from '../../src';
import { fromJson, type SerializerError, toJson } from '../../src/serializer';

describe('Ecosystem:tupleson', () => {
const httpException: TsonType<HttpException | SerializerError, string> = {
deserialize: (v) => fromJson(v),
key: 'HttpException',
serialize: (v) => toJson(v),
test: (v) => v instanceof HttpException,
};

const tson = createTson({
types: [httpException],
});

const obj = {
e422: new HttpUnprocessableEntity({
issues: [
{
message: 'Invalid address',
path: ['addresses', 0, 'line1'],
code: 'empty_string',
},
],
}),
e404: createHttpException(404),
};

it('should serialize/deserialize', () => {
const serialized = tson.serialize(obj);
const deserialized = tson.deserialize(serialized);
expect(deserialized).toStrictEqual(obj);
});
});