Skip to content

Commit

Permalink
Improve README and correct usage of MallocNanoZone=0
Browse files Browse the repository at this point in the history
USER=brianward
COMMAND_MODE=unix2003
__CFBundleIdentifier=co.zeit.hyper
PATH=/Users/brianward/.deno/bin:/Users/brianward/.sst/bin:/Users/brianward/.sst/bin:/Users/brianward/.pyenv/shims:/Users/brianward/.pyenv/bin:/Users/brianward/Code/google-cloud-sdk/bin:/opt/homebrew/bin:/Users/brianward/.nvm/versions/node/v18.12.1/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/brianward/.composer/vendor/bin
LOGNAME=brianward
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.HRV72Y7RKm/Listeners
HOME=/Users/brianward
SHELL=/bin/zsh
TMPDIR=/var/folders/q_/8mdlkf0n0hs7jpx_4b8s77x00000gn/T/
__CF_USER_TEXT_ENCODING=0x1F5:0:2
XPC_SERVICE_NAME=0
XPC_FLAGS=0x0
ORIGINAL_XDG_CURRENT_DESKTOP=undefined
LANG=en_GB.UTF-8
TERM=xterm-256color
COLORTERM=truecolor
TERM_PROGRAM=Hyper
TERM_PROGRAM_VERSION=3.4.1
PWD=/Users/brianward/Code/raptor
SHLVL=1
OLDPWD=/Users/brianward/Code/raptor-router
NVM_DIR=/Users/brianward/.nvm
NVM_CD_FLAGS=-q
NVM_BIN=/Users/brianward/.nvm/versions/node/v20.14.0/bin
NVM_INC=/Users/brianward/.nvm/versions/node/v20.14.0/include/node
CLICOLOR=1
LSCOLORS=Exfxcxdxbxegedabagacad
GREP_OPTIONS=--color=auto
EDITOR=code
VISUAL=code
PROMPT_EOL_MARK=
CONDA_CHANGEPS1=no
PYENV_ROOT=/Users/brianward/.pyenv
PYENV_SHELL=zsh
AWS_PROFILE=Briward-SSO
VIRTUAL_ENV_DISABLE_PROMPT=12
_=/usr/bin/env as this was unnecessary.
  • Loading branch information
briward committed Nov 11, 2024
1 parent b35b1c6 commit 515f8a8
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 48 deletions.
88 changes: 51 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,33 @@
<a href="https://jsr.io/@raptor"><img src="https://jsr.io/badges/@raptor?logoColor=3A9D95&color=3A9D95&labelColor=083344" alt="" /></a>
</p>

# About Raptor
# Introduction

Raptor is a tiny, unopinionated middleware framework written for use with Deno. The goal of this project is to build a simple, easy to use middleware framework using Web Standards.

This framework is heavily inspired by many who came before it, such as Oak, Express, Laravel and Slim Framework in PHP.
Raptor is a tiny yet eloquent middleware framework designed for use with Deno. It's been built for those who value readability and expressive code. With Raptor, you'll find a harmonious balance between functionality and simplicity, allowing you to convey complex logic with minimal verbosity.

# Usage

> [!NOTE]
> This is under heavy development and not yet suitable for production use, you
> have been warned.
> This is currently under heavy development and is not yet suitable for production use. Please proceed with caution.
## Installation

To start using Raptor, simply install it through the CLI or import it directly from JSR.

### Using the Deno CLI

```
deno add @raptor/framework
deno add jsr:@raptor/framework
```

### Importing with JSR

Raptor is also available to import directly via JSR:
[https://jsr.io/@raptor/framework](https://jsr.io/@raptor/framework)

```ts
import { type Context, Kernel } from "jsr:@raptor/framework";
```

## Getting started
## Middleware

HTTP Middleware can be added to the container using the `add` method of the Kernel.
Raptor's kernel will run through and process each middleware in the order they were added to the stack. However, if you wish to process the next middleware within another, you can use the built-in `next` argument which is provided to the callback (see "Calling the next middleware").

```ts
import { type Context, Kernel } from "jsr:@raptor/framework";
Expand All @@ -54,11 +49,11 @@ app.serve({ port: 8000 });

### Response

With Raptor, setting the response headers is completely optional. Instead, you can simply return the data you wish from the middleware handler and let Raptor decide which content-type header is best for your response.
In Raptor, at least one middleware function is required to return a body response to ensure that the request cycle completes successfully. The `Content-Type` of your response will be automatically determined by the framework, allowing you to focus on your own application code.

#### Returning JSON

The following middleware response will be automatically detected as `application/json` and the `content-type` header will be set accordingly.
If the middleware response body is an object, it will be automatically recognized as `application/json`. Consequently, both the `Content-Type` header and the response body will be appropriately set to reflect this format.

```ts
app.add(() => ({
Expand All @@ -68,57 +63,77 @@ app.add(() => ({

#### Returning HTML

When returning a string, the `content-type` header will automatically be set to `text/plain`. However, if we return a string which has been detected as including HTML, then it will be automatically set to `text/html`.
When a string is returned, the `Content-Type` header is automatically set to `text/plain`. However, if the string is detected to contain HTML, the `Content-Type` header will be automatically adjusted to `text/html`.

```ts
app.add(() => '<h1>Hello, Dr Malcolm!</h1>');
```

#### Overriding headers

While it's great not having to configure a `content-type` and simply return the data we want, we might have the need to decide specifically which header to set. With that in mind, you can do the following:
Although it's convenient to return data without configuring a `Content-Type`, there may be instances where you need to specify a particular header. In such cases, you can proceed as follows:

```ts
app.add((context: Context) => {
context.response.headers.set('content-type', 'application/hal+json');
context.response.headers.set('Content-Type', 'application/hal+json');

return {
name: 'Dr Ian Malcolm'
}
});
```

### Middleware Context
### Context

The context object is provided as the first parameter of a middleware function. This object contains the HTTP Request, the HTTP Response and any other properties that may have been added by prior middleware calls. You can take a look at what is stored in this context below:
The context object is passed as the first parameter to a middleware function. It includes the HTTP request, the HTTP response, and any additional properties added by previous middleware calls.

### Calling the next middleware

The next middleware function is responsible for invoking the subsequent middleware in the stack. It must be called if the current middleware doesn't handle the request and provide a response; otherwise, the system will hang. As an example, the following script demonstrates how to calculate runtime across two middleware:

```ts
app.add((context: Context) => {
console.log(context.request, context.response);
app.add(async (_context: Context, next: CallableFunction) => {
const start = Date.now();

await next();

const ms = Date.now() - start;

console.log(`${ms}ms`);
});

app.add(async () => {
await new Promise(resolve => setTimeout(resolve, 3000));

return 'Hello, Dr Malcolm!';
});

// console: ~3004ms
```

### Calling the next middleware
## Error handling

The next middleware function can be used to call the next middleware in the stack. If the middleware doesn't resolve the request and respond then the `next` function must be called. If the `next` function is not called then the system will hang. The following script will return "Ellie Sattler":
Just as response content types are automatically recognized, error responses are handled the same way. If you want errors to be returned in `text/plain`, you simply need to explicitly set the `Content-Type` as follows:

```ts
app.add((_context: Context, next: CallableFunction) => {
next();
import { type Context, NotFound } from "jsr:@raptor/framework";

return {
name: 'Dr Ian Malcolm'
}
});
app.add((context: Context) => {
context.response.headers.set('Content-Type', 'text/plain');

app.add(() => ({
name: 'Dr Ellie Sattler'
}));
throw new NotFound();
});
```

## Error handling
The following errors are currently available to import and throw from within the framework:

* `NotFound`
* `BadRequest`
* `ServerError`
* `TypeError`

Errors are caught and returned in the response object. If the JSON content-type is set within a middleware callback, all errors thrown in subsequent callbacks will respond with JSON, by design.
> [!NOTE]
> Further work to improve overall error handling will be coming soon.
# Deployment

Expand Down Expand Up @@ -152,10 +167,9 @@ npx wrangler deploy

# Available extensions

Raptor is designed to be an unopinionated modular framework, allowing you to decide what batteries you wish to include or not. Here is a list of the first-party extensions available:
Raptor is built as an unopinionated modular framework, giving you the flexibility to choose which components to include. Below is a list of available first-party extensions:

* Router Middleware: [https://jsr.io/@raptor/router](https://jsr.io/@raptor/router)
* Request Validation (soon...)

# License

Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@raptor/framework",
"version": "0.4.1",
"version": "0.4.2",
"exports": "./mod.ts",
"publish": {
"exclude": [
Expand Down
10 changes: 8 additions & 2 deletions src/http/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ export default class Processor {
* Process a body into a valid Response object.
*
* @param body A body value to process.
* @param status A valid HTTP status code.
* @returns A valid HTTP response object.
*/
public process(body: any): Response {
public process(body: any, status: number = 200): Response {
// If the middleware provides a Response object, use it.
if (body instanceof Response) {
return body;
Expand All @@ -32,6 +33,7 @@ export default class Processor {
}

return new Response(JSON.stringify(body), {
status,
headers: this.context.response.headers,
});
}
Expand All @@ -49,10 +51,14 @@ export default class Processor {
}

return new Response(body as string, {
status,
headers: this.context.response.headers,
});
}

return new Response(body as string);
return new Response(body as string, {
status,
headers: this.context.response.headers,
});
}
}
10 changes: 2 additions & 8 deletions src/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class Kernel {
*/
constructor() {
this.context = new Context(
new Request(Deno.env.get("APP_URL") as string),
new Request("http://localhost"),
new Response(null),
);

Expand Down Expand Up @@ -109,12 +109,6 @@ export default class Kernel {
* @returns void
*/
private handleError(error: Error): Response {
return new Response(
error.message,
{
status: error.status,
headers: this.context.response.headers,
},
);
return this.processor.process(error, error.status);
}
}

0 comments on commit 515f8a8

Please sign in to comment.