Skip to content

Commit

Permalink
Separate interface and implementation (#13)
Browse files Browse the repository at this point in the history
* Separate interface and implementation for objects

* Remove extra line
  • Loading branch information
rikilele authored Jul 8, 2021
1 parent 8f8f2ba commit 6ecd17e
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 65 deletions.
12 changes: 4 additions & 8 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// Copyright 2021 Riki Singh Khorana. All rights reserved. MIT license.

export { Kyuko } from "./kyuko.ts";
export type {
KyukoMiddleware,
KyukoRequest,
KyukoRequestHandler,
} from "./kyuko.ts";

export { KyukoResponse } from "./KyukoResponse.ts";
export { Kyuko } from "./src/Kyuko.ts";
export type { KyukoMiddleware, KyukoRequestHandler } from "./src/Kyuko.ts";
export type { KyukoRequest } from "./src/KyukoRequest.ts";
export type { KyukoResponse } from "./src/KyukoResponse.ts";
38 changes: 12 additions & 26 deletions kyuko.ts → src/Kyuko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

/// <reference path='./deploy.d.ts' />

import { KyukoResponse } from "./KyukoResponse.ts";
import { KyukoRequest, KyukoRequestImpl } from "./KyukoRequest.ts";
import { KyukoResponse, KyukoResponseImpl } from "./KyukoResponse.ts";
import { RoutePathHandler } from "./RoutePathHandler.ts";

/**
Expand All @@ -19,32 +20,13 @@ export type KyukoRequestHandler = (
* and return early by calling `res.send()` when needed.
* Hands over execution to the next middleware / request handler on return.
*
* Notice how a `next()` call is unneeded.
* Notice how a `next()` call is unneeded unlike middleware functions in Express.
*/
export type KyukoMiddleware = (
| ((req: KyukoRequest, res: KyukoResponse) => void)
| ((req: KyukoRequest, res: KyukoResponse) => Promise<void>)
);

/**
* Request that gets passed into a `KyukoRequestHandler`.
* Can be extended further for middlewares to populate the original `Request`.
*/
export interface KyukoRequest extends Request {
/**
* Stores path parameters and their values in an object.
*/
params: {
[key: string]: string;
};

/**
* Stores query parameters and their values.
* Note that duplicate keys may map to different values.
*/
query: URLSearchParams;
}

/**
* An ultra-light framework for API servers hosted on [Deno Deploy](https://deno.com/deploy).
* Aims to provide a similar experience to developing API servers with [Express](https://expressjs.com/).
Expand All @@ -57,7 +39,13 @@ export class Kyuko {

/**
* Initializes a new Kyuko app.
* Supports routing for GET, POST, PUT, DELETE methods as of now.
* Supports routing for the following methods as of now:
* - GET
* - POST
* - PUT
* - DELETE
* - PATCH
* - HEAD
*/
constructor() {
this.#routes = new RoutePathHandler();
Expand Down Expand Up @@ -195,10 +183,8 @@ export class Kyuko {
}

private handleFetchEvent(event: FetchEvent) {
const req = event.request as KyukoRequest;
req.params = {};
req.query = new URLSearchParams();
const res = new KyukoResponse(event);
const req = new KyukoRequestImpl(event);
const res = new KyukoResponseImpl(event);
this.handleRequest(req, res);
}

Expand Down
41 changes: 41 additions & 0 deletions src/KyukoRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2021 Riki Singh Khorana. All rights reserved. MIT license.

/// <reference path='./deploy.d.ts' />

/**
* The request object that is handled in Kyuko applications.
* Can be extended further for middlewares to populate the original `Request`.
*/
export interface KyukoRequest extends Request {
/**
* Stores path parameters and their values in an object.
*/
params: {
[key: string]: string;
};

/**
* Stores query parameters and their values.
* Note that a single key may map to multiple different values.
*/
query: URLSearchParams;
}

/**
* This class is instantiated when a fetch request is captured by a Kyuko application.
* The instance is populated by the original request handed over from the event listener.
*/
export class KyukoRequestImpl extends Request implements KyukoRequest {
params: { [key: string]: string };
query: URLSearchParams;

/**
* Instantiates a `KyukoRequest` based on the original `fetchEvent` request.
* @param fetchEvent The event that this request originated from.
*/
constructor(fetchEvent: FetchEvent) {
super(fetchEvent.request);
this.params = {};
this.query = new URLSearchParams();
}
}
82 changes: 51 additions & 31 deletions KyukoResponse.ts → src/KyukoResponse.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
// Copyright 2021 Riki Singh Khorana. All rights reserved. MIT license.

/// <reference path='./deploy.d.ts' />

/**
* This class is instantiated when a fetch request is captured by a Kyuko application.
* The instance is responsible for storing information about the response to the request.
* The response built is finally sent out on a `send()` call.
* The response object that is handled in Kyuko applications.
* Responsible for storing information about the response to the request,
* as well as sending the response via the `send()` and `redirect()` methods.
*
* Note that `send()` or `redirect()` **must** be called or else the request will hang.
*/
export class KyukoResponse {
export interface KyukoResponse {
body: BodyInit | null;
statusCode: number | undefined;
statusText: string | undefined;
headers: Headers;

/**
* Sets the status code to `status`, and returns `this`.
*/
status(status: number): KyukoResponse;

/**
* Redirects the request to a new `address`.
* The `address` can be either a relative url path, or a full url.
* The optional `status` parameter can be used to set a custom status code.
* Otherwise overrides the current `res.statusCode` with 302.
*
* @param address The address to redirect to.
* @param status The status code of the response. Defaults to 302.
*/
redirect(address: string, status?: number): void;

/**
* Sends a response to the original request that instantiated this object.
* The response is built using the public attributes of this object,
* which should've been set by the user beforehand.
*
* @param body A response body that would supersede `this.body`
*/
send(body?: BodyInit): void;

/**
* @returns Whether the response was sent (`send()` was called) or not.
*/
wasSent(): boolean;
}

/**
* This class is instantiated when a fetch request is captured by a Kyuko application.
*/
export class KyukoResponseImpl implements KyukoResponse {
body: BodyInit | null;
statusCode: number | undefined;
statusText: string | undefined;
Expand All @@ -28,42 +70,23 @@ export class KyukoResponse {
this.#fetchEvent = fetchEvent;
}

/**
* Sets the status code to `status`, and returns `this`.
*/
status(status: number): this {
status(status: number) {
this.statusCode = status;
const statusText = KyukoResponse.STATUSES.get(status);
const statusText = KyukoResponseImpl.STATUSES.get(status);
if (statusText !== undefined) {
this.statusText = statusText;
}

return this;
}

/**
* Redirects the request to a new `address`.
* The `address` can be either a relative url path, or a full url.
* The optional `status` parameter can be used to set a custom status code.
* Otherwise overrides the current `res.statusCode` with 302.
*
* @param address The address to redirect to.
* @param status The status code of the response. Defaults to 302.
*/
redirect(address: string, status = 302): void {
redirect(address: string, status = 302) {
this.status(status);
this.headers.append("Location", encodeURI(address));
this.send();
}

/**
* Sends a response to the original request that instantiated this object.
* The response is built using the public attributes of this object,
* which should've been set by the user beforehand.
*
* @param body A response body that would supersede `this.body`
*/
send(body?: BodyInit): void {
send(body?: BodyInit) {
if (!this.#sent) {
const response = new Response(
body || this.body,
Expand All @@ -79,10 +102,7 @@ export class KyukoResponse {
}
}

/**
* @returns Whether the response was sent (`send()` was called) or not.
*/
wasSent(): boolean {
wasSent() {
return this.#sent;
}

Expand Down
2 changes: 2 additions & 0 deletions RoutePathHandler.test.ts → src/RoutePathHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright 2021 Riki Singh Khorana. All rights reserved. MIT license.

import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
import { RoutePathHandler } from "./RoutePathHandler.ts";

Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 6ecd17e

Please sign in to comment.