Skip to content

Commit

Permalink
fix: Various fixes (#11)
Browse files Browse the repository at this point in the history
* Fixed issue with empty URLs and Response methods not available

* Update test with correct values

* Fixed bad import on readme

* Added package description to npm

* Return data key only if exists

* Update readme.md

* Improve readme.md guide
  • Loading branch information
frangeris committed Oct 5, 2024
1 parent 171fbe9 commit 6585ae0
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 23 deletions.
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ By setting up this initialization, you ensure that every request you make using
Once the module is initialized, you can easily make requests to the defined endpoints. Here's a snippet for requests:

```ts
import { useRequests, init } from "./src";
import { useRequests, init } from "use-requests";

type User = {};

Expand All @@ -90,13 +90,28 @@ init("https://api.example.io/dev", { ...Api });
const main = async () => {
const { userById, users } = useRequests<typeof Api>();
const { data: usersRes } = await users.get<User[]>();
const { data: userByIdRes } = await userById.get<User>({ params: { id: 1 } });
const {
data: userByIdRes, // wrapper that cast to generic

// properties available from Response web API
json,
ok,
body,
blob,
bytes,
headers,
status,
text,
statusText,
} = await userById.get<User>({ params: { id: 1 } });
// Optionally, set headers for the request
// users.headers.set("Authorization", "Bearer token");
};
main();
```

The `data` property is a wrapper around the `Response.json()` method that looks for `data` key in the response, in case that property exists, it will be returned as the representation of the response `body.data` casted to the generic type used, eg: `User[]` or `User`.

The test endpoint is accessed with an id parameter. You can also set headers (like authorization tokens) as needed.

---
Expand All @@ -118,8 +133,6 @@ Each method returned by `useRequests` is type-safe, meaning that the parameters
The response by any methods is an instance of [fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object, which you can use to extract the data, status, headers, etc.

The `data` property is a wrapper around the `Response.json()` method, which returns a promise that resolves to the JSON representation of the response body casted to the generic type used, eg: `User[]` or `User`.

> [!WARNING]
> Any parameters defined in the endpoint definition are required when calling the method. If you omit a required parameter, TypeScript will throw a compile-time error or an exception will be thrown at runtime.
Expand Down
2 changes: 1 addition & 1 deletion __tests__/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("service", () => {
describe("build", () => {
it("should return empty string if no path is provided", () => {
const service = new Service("/resource");
expect(service["build"]()).toBe("");
expect(service["build"]()).toBe("/resource");
});

it("should return the path if a string path is provided", () => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "use-requests",
"description": "Type-Safe HTTP client hook (react) to handle requests based on native fetch api with some magic under the hood ✨",
"version": "1.0.8",
"author": "Frangeris Peguero <[email protected]>",
"license": "MIT",
Expand Down
46 changes: 29 additions & 17 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ export class Service<P> {
}

private build(path?: RequestPath<P>) {
if (!path) {
return "";
}

if (typeof path === "string") {
return path;
}

// build complex path
let url = this.resource;
if (!path) {
return url;
}

// build complex path
const { params, query } = path;
if (params) {
for (const k in params) {
Expand All @@ -63,24 +63,33 @@ export class Service<P> {
return url;
}

private async response<T>(request: Response): ServiceResponse<T> {
return {
...request,
data: (await request.json()) as T,
};
private async response<T>(res: Response): Promise<ServiceResponse<T>> {
const newRes = res.clone() as unknown as ServiceResponse<T>;

if (res.ok) {
const body = await res.json();
if (body?.data) {
newRes.data = body.data as T;
}
}

return newRes;
}

// HTTP methods
async get<T>(path?: RequestPath<P>): ServiceResponse<T> {
const request = await this.request({
async get<T>(path?: RequestPath<P>) {
const response = await this.request({
path,
method: "get",
});

return this.response<T>(request);
return this.response<T>(response);
}

async post<T>(payload: any, path?: RequestPath<P>): ServiceResponse<T> {
async post<T>(
payload: any,
path?: RequestPath<P>
): Promise<ServiceResponse<T>> {
const request = await this.request({
path,
method: "post",
Expand All @@ -90,7 +99,10 @@ export class Service<P> {
return this.response<T>(request);
}

async put<T>(payload: any, path?: RequestPath<P>): ServiceResponse<T> {
async put<T>(
payload: any,
path?: RequestPath<P>
): Promise<ServiceResponse<T>> {
const request = await this.request({
path,
method: "put",
Expand All @@ -100,7 +112,7 @@ export class Service<P> {
return this.response<T>(request);
}

async delete<T>(path?: RequestPath<P>): ServiceResponse<T> {
async delete<T>(path?: RequestPath<P>): Promise<ServiceResponse<T>> {
const request = await this.request({
path,
method: "delete",
Expand All @@ -112,7 +124,7 @@ export class Service<P> {
async patch<T>(
ops: PatchOperation[],
path?: RequestPath<P>
): ServiceResponse<T> {
): Promise<ServiceResponse<T>> {
const request = await this.request({
path,
method: "patch",
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type InitOptions = {

export type Services<T> = { [K in keyof T]: Service<T[K]> };

export type ServiceResponse<R> = Promise<Response & { data: R }>;
export type ServiceResponse<R> = { data: R } & Response;

export type RequestPath<P> =
| {
Expand Down

0 comments on commit 6585ae0

Please sign in to comment.