galbe 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/deploy_website.yml +1 -1
- package/README.md +3 -1
- package/docs/getting-started.md +16 -16
- package/docs/handler.md +1 -0
- package/docs/hooks.md +88 -0
- package/docs/plugins.md +1 -0
- package/docs/router.md +1 -0
- package/docs/routes.md +11 -11
- package/docs/schemas.md +24 -28
- package/package.json +1 -1
- package/src/server.ts +5 -1
|
@@ -16,5 +16,5 @@ jobs:
|
|
|
16
16
|
-H "Accept: application/vnd.github+json" \
|
|
17
17
|
-H "Authorization: Bearer ${{ secrets.GH_WEBSITE_TOKEN }}" \
|
|
18
18
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
19
|
-
https://api.github.com/repos/pierre-cm/galbe-website/actions/workflows/
|
|
19
|
+
https://api.github.com/repos/pierre-cm/galbe-website/actions/workflows/deploy.yml/dispatches \
|
|
20
20
|
-d '{"ref":"main"}'
|
package/README.md
CHANGED
package/docs/getting-started.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Getting started
|
|
2
2
|
|
|
3
|
-
Galbe is a Javascript web framework
|
|
3
|
+
Galbe is a Javascript web framework for building fast and versatile backend servers with Bun.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Designed with simplicity in mind, Galbe allows you to quickly create and set up a project. In addition to its ease of use, Galbe also offers a range of useful features that help you focus on the core logic of your application.
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
@@ -26,12 +26,12 @@ Now you can start the dev server by running:
|
|
|
26
26
|
bun dev
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
This will start a web server on `
|
|
29
|
+
This will start a web server on `localhost:3000`.
|
|
30
30
|
|
|
31
|
-
To verify that the project was setup correctly and is running, try to reach `
|
|
31
|
+
To verify that the project was setup correctly and is running, try to reach `localhost:3000/hello` endpoint, this should return following greeting message:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
|
|
34
|
+
curl localhost:3000/hello
|
|
35
35
|
Hello from Galbe!
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -74,7 +74,7 @@ export default galbe
|
|
|
74
74
|
|
|
75
75
|
This is the recommended way to proceed but it is not mandatory. Galbe instances also provide a `listen` method that will allow you to manually start your server instance from the code.
|
|
76
76
|
|
|
77
|
-
> [!WARNING]
|
|
77
|
+
> [!WARNING]
|
|
78
78
|
> In the case you decide to not rely on Galbe CLI to run/build your app, you will not have access to [Automatic Route Analyzer](routes.md#automatic-route-analyzer) feature.
|
|
79
79
|
|
|
80
80
|
### Galbe CLI
|
|
@@ -85,7 +85,7 @@ galbe <command> <argument> [options]
|
|
|
85
85
|
|
|
86
86
|
Here are the available commands:
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
**dev**
|
|
89
89
|
|
|
90
90
|
Start a dev server running your Galbe application.
|
|
91
91
|
|
|
@@ -98,7 +98,7 @@ _options_
|
|
|
98
98
|
- `--port` or `-p`: port number (default: 3000)
|
|
99
99
|
- `--watch` or `-w`: watch file changes (default: true)
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
**build**
|
|
102
102
|
|
|
103
103
|
Bundle your Galbe application.
|
|
104
104
|
|
|
@@ -121,25 +121,25 @@ const galbe = new Galbe(configuration)
|
|
|
121
121
|
|
|
122
122
|
### Properties
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
**port**
|
|
125
125
|
|
|
126
126
|
The port number that the server will be listening on. Default is `3000`.
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
**basePath**
|
|
129
129
|
|
|
130
130
|
The base path is added as a prefix to all the routes created.
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
**routes**
|
|
133
133
|
|
|
134
134
|
A Glob Pattern or a list of Glob patterns defining the route files to be analyzed by the [Automatic Route Analyzer](routes.md#automatic-route-analyzer). Default is `src/**/*.route.{js,ts}`.
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
**plugin**
|
|
137
137
|
|
|
138
138
|
A property that can be used by plugins to add plugin's specific configuration. Every key should correspond to a [Unique Plugin Identifier](plugins.md).
|
|
139
139
|
|
|
140
140
|
### Examples
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
A common way to handle server configuration is to create new file `galbe.config.(js|ts|json)` at the root of your project directory and import it in your code. Here is an example:
|
|
143
143
|
|
|
144
144
|
galbe.config.js
|
|
145
145
|
|
|
@@ -217,7 +217,7 @@ Here are two examples of valid project structures by default:
|
|
|
217
217
|
|
|
218
218
|
In both cases, the [Automatic Route Analyzer](routes.md#automatic-route-analyzer) will analyze `foo.route.ts` and `bar.route.ts` Route Files to find route definitions.
|
|
219
219
|
|
|
220
|
-
You can find more info about Route Files definition
|
|
220
|
+
You can find more info about Route Files definition in the [Routes Files](routes.md#route-files) section.
|
|
221
221
|
|
|
222
|
-
> [!NOTE]
|
|
223
|
-
>
|
|
222
|
+
> [!NOTE]
|
|
223
|
+
> The examples provided above will work with the default configuration, but you can easily customize the routes property to fit your own project structure. Simply redefine the `routes` property with your own pattern(s) to to fit your own project structure.
|
package/docs/handler.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Handler
|
package/docs/hooks.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Hooks
|
|
2
|
+
|
|
3
|
+
Hooks provide a simple way to perform specific actions before and/or after reaching a specific route endpoint.
|
|
4
|
+
|
|
5
|
+
## Hook Definition
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const hook = (context, next) => {
|
|
9
|
+
context.state['foo'] = 'bar'
|
|
10
|
+
await next()
|
|
11
|
+
console.log('Hook end')
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The hook takes only two arguments, a `context` object and a `next` function.
|
|
16
|
+
|
|
17
|
+
**context**
|
|
18
|
+
|
|
19
|
+
The `context` object contains the request information along with a state property that is modifiable and preserved across all hooks and the handler. It is useful for sharing information or objects across hooks and handler. You can find more information about it in the [Context]() section.
|
|
20
|
+
|
|
21
|
+
**next**
|
|
22
|
+
|
|
23
|
+
The `next` function calls the next hook in the hook list or the handler if the current hook is the last one declared. The `next` function should be called at most once. If it is omitted, Galbe will call it automatically at the end of the execution of the current hook.
|
|
24
|
+
|
|
25
|
+
> [!TIP]
|
|
26
|
+
> Hooks are interruptible objects, meaning they can return a response at any moment. This provides a powerful mechanism for implementing custom logic, such as authentication, authorization, caching, and more."
|
|
27
|
+
|
|
28
|
+
## Hooks Declaration
|
|
29
|
+
|
|
30
|
+
Hooks should be declared just before the handler method in the [Route Definition]() method as a list of Hooks.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
galbe.get('foo', [ hook1, hook2, ... ], ctx => {})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Hooks are called just before the [Handler]() in the order that they have been declared in the hook list of the [Route Definition](). To get a better understanding of hooks execution during the request lifecycle, you can refer to the [Lifecycle]() section.
|
|
37
|
+
|
|
38
|
+
### Examples
|
|
39
|
+
|
|
40
|
+
Linear hooks declaration:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const hook1 = context => {
|
|
44
|
+
console.log('hook1 called')
|
|
45
|
+
}
|
|
46
|
+
const hook2 = context => {
|
|
47
|
+
console.log('hook2 called')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
galbe.get('example', [hook1, hook2], ctx => {
|
|
51
|
+
console.log('handler')
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
curl http://localhost:3000/example
|
|
57
|
+
hook1
|
|
58
|
+
hook2
|
|
59
|
+
handler
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Nested hooks declaration:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const hook1 = (context, next) => {
|
|
66
|
+
console.log('hook1 start')
|
|
67
|
+
await next()
|
|
68
|
+
console.log('hook1 end')
|
|
69
|
+
}
|
|
70
|
+
const hook2 = context => {
|
|
71
|
+
console.log('hook2 start')
|
|
72
|
+
await next()
|
|
73
|
+
console.log('hook2 end')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
galbe.get('example', [hook1, hook2], ctx => {
|
|
77
|
+
console.log('handler')
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
curl http://localhost:3000/example
|
|
83
|
+
hook1 start
|
|
84
|
+
hook2 start
|
|
85
|
+
handler
|
|
86
|
+
hook2 end
|
|
87
|
+
hook1 end
|
|
88
|
+
```
|
package/docs/plugins.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Plugins
|
package/docs/router.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Router
|
package/docs/routes.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Routes
|
|
2
2
|
|
|
3
|
+
Routes are the entry points for handling client requests in a Galbe application. In this section, we'll cover how to define routes, the various options available for route definitions, and how to use the Automatic Route Analyzer to simplify route setup.
|
|
4
|
+
|
|
3
5
|
## Route Definition
|
|
4
6
|
|
|
5
7
|
Here is how to define routes in Galbe.
|
|
@@ -23,15 +25,15 @@ There are two special segments:
|
|
|
23
25
|
|
|
24
26
|
**schema** (Schema) _Optional_
|
|
25
27
|
|
|
26
|
-
See [Schemas](schemas) section.
|
|
28
|
+
See [Schemas](schemas.md) section.
|
|
27
29
|
|
|
28
30
|
**hooks** (Hook[]) _Optional_
|
|
29
31
|
|
|
30
|
-
See [Hooks](hooks) section.
|
|
32
|
+
See [Hooks](hooks.md) section.
|
|
31
33
|
|
|
32
34
|
**handler** (Handler)
|
|
33
35
|
|
|
34
|
-
See [Handler](handler) section.
|
|
36
|
+
See [Handler](handler.md) section.
|
|
35
37
|
|
|
36
38
|
### Examples
|
|
37
39
|
|
|
@@ -78,15 +80,13 @@ galbe.get(
|
|
|
78
80
|
## Automatic Route Analyzer
|
|
79
81
|
|
|
80
82
|
> [!NOTE]
|
|
81
|
-
> This feature is only available if you run/build the app via the [Galbe CLI](), which is the case by default if you created your app following the [Automatic Installation]() step or
|
|
82
|
-
|
|
83
|
-
The Automatic Route Analyzer is in charge of analyzing all the Route Files of your project and set up the routes defintions to your Glabe server automatically.
|
|
83
|
+
> This feature is only available if you run/build the app via the [Galbe CLI](getting-started.md#galbe-cli), which is the case by default if you created your app following the [Automatic Installation](getting-started.md#automatic-installation) step or if you configured your package.json to do so.
|
|
84
84
|
|
|
85
|
-
By default, the analyzer will search for
|
|
85
|
+
The Automatic Route Analyzer is responsible for analyzing all the Route Files of your project and setting up the route definitions for your Galbe server automatically. By default, the analyzer will search for Route Files matching paths like `src/**/*.route.{js,ts}`. This can be configured by modifying the value of `routes` property of your Galbe configuration. A value of `false` will disable the analyzer.
|
|
86
86
|
|
|
87
87
|
### Route Files
|
|
88
88
|
|
|
89
|
-
In order to be properly analyzed, Route Files must export a default function that takes a Galbe instance as unique argument. Your routes should be defined using that Galbe instance. Here a basic
|
|
89
|
+
In order to be properly analyzed, Route Files must export a default function that takes a Galbe instance as unique argument. Your routes should be defined using that Galbe instance. Here's a basic example in JavaScript:
|
|
90
90
|
|
|
91
91
|
```ts
|
|
92
92
|
export default g => {
|
|
@@ -94,7 +94,7 @@ export default g => {
|
|
|
94
94
|
}
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
The same example
|
|
97
|
+
The same example in Typescript:
|
|
98
98
|
|
|
99
99
|
```ts
|
|
100
100
|
import type { Galbe } from 'galbe'
|
|
@@ -103,7 +103,7 @@ export default (g: Galbe) => {
|
|
|
103
103
|
}
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
The Automatic Route Analyzer
|
|
106
|
+
The Automatic Route Analyzer can also collect metadata about your Route File and your routes by analyzing multiline comments. This can be used by some plugins to perform specific tasks. Here's an example of a Route File with multiline comment metadata:
|
|
107
107
|
|
|
108
108
|
```js
|
|
109
109
|
/**
|
|
@@ -121,4 +121,4 @@ export default g => {
|
|
|
121
121
|
}
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
You will find more information about comment
|
|
124
|
+
You will find more information about comment metadata and how to use them along with examples in the [Plugin](plugins.md) section.
|
package/docs/schemas.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# Shemas
|
|
2
2
|
|
|
3
|
-
Galbe
|
|
4
|
-
|
|
5
|
-
The prime intention of that features is to offer an easy way to manage automatically request inputs validation and error handling. Moreover, it also greatly improve developper's experience by infering static Typescript types from schema definitions.
|
|
3
|
+
Galbe provides a custom Schema Type processor that offers type safety, data parsing, and validation. The primary purpose of this feature is to simplify request input validation and error handling automatically. Additionally, it enhances the developer's experience by inferring static TypeScript types from schema definitions.
|
|
6
4
|
|
|
7
5
|
## Schema Types
|
|
8
6
|
|
|
9
|
-
To
|
|
7
|
+
To start using Schema definitions, import `$T` from the `galbe` library:
|
|
10
8
|
|
|
11
9
|
```js
|
|
12
10
|
import { $T } from 'galbe'
|
|
@@ -62,32 +60,32 @@ Schema Type matching `array` values.
|
|
|
62
60
|
const arraySchema = $T.array($T.any(), options)
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
####
|
|
63
|
+
#### Optional
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
Makes any type optional. This allows for `undefined` values.
|
|
68
66
|
|
|
69
67
|
```ts
|
|
70
|
-
const
|
|
68
|
+
const optionalSchema = $T.optional($T.string())
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
####
|
|
71
|
+
#### Union
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
Creates an union of Schema Types.
|
|
76
74
|
|
|
77
75
|
```ts
|
|
78
|
-
const
|
|
76
|
+
const unionSchema = $T.union([$T.string(), $T.number()])
|
|
79
77
|
```
|
|
80
78
|
|
|
81
79
|
## Request Schema definition
|
|
82
80
|
|
|
83
|
-
The Request Schema definition allows you to define a schema for your request on your [Route Definition](). It must be defined right after the
|
|
81
|
+
The Request Schema definition allows you to define a schema for your request on your [Route Definition](routes.md#route-defintion). It must be defined right after the path of your route.
|
|
84
82
|
|
|
85
83
|
```js
|
|
86
84
|
const schema = {}
|
|
87
85
|
galbe.get('/foo/:bar', schema, ctx => {})
|
|
88
86
|
```
|
|
89
87
|
|
|
90
|
-
The Request Schema has
|
|
88
|
+
The Request Schema has four optional properties:
|
|
91
89
|
|
|
92
90
|
### headers
|
|
93
91
|
|
|
@@ -95,7 +93,7 @@ The Request Schema has 4 optional properties
|
|
|
95
93
|
headers: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
96
94
|
```
|
|
97
95
|
|
|
98
|
-
This is a key-value object where each key represents a request
|
|
96
|
+
This is a key-value object where each key represents a request header name, and the value is the associated Schema.
|
|
99
97
|
|
|
100
98
|
**Example**:
|
|
101
99
|
|
|
@@ -113,7 +111,7 @@ const schema = {
|
|
|
113
111
|
params: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
114
112
|
```
|
|
115
113
|
|
|
116
|
-
This is a key-value object where each key represents a request
|
|
114
|
+
This is a key-value object where each key represents a request path parameter name, and the value is the associated Schema.
|
|
117
115
|
|
|
118
116
|
**Example**:
|
|
119
117
|
|
|
@@ -127,7 +125,7 @@ const schema = {
|
|
|
127
125
|
```
|
|
128
126
|
|
|
129
127
|
> [!WARNING]
|
|
130
|
-
> Every key should match an existing [route path]() parameter. Otherwise Typescript will show
|
|
128
|
+
> Every key should match an existing [route path](routes.md#route-defintion) parameter. Otherwise Typescript will show an error.
|
|
131
129
|
>
|
|
132
130
|
> By default, if no schema is defined for a given parameter. Galbe will assume it is of type `string`.
|
|
133
131
|
|
|
@@ -137,7 +135,7 @@ const schema = {
|
|
|
137
135
|
query: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
138
136
|
```
|
|
139
137
|
|
|
140
|
-
This is a key-value object where each key represents a request
|
|
138
|
+
This is a key-value object where each key represents a request query parameter name, and the value is the associated Schema.
|
|
141
139
|
|
|
142
140
|
**Example**:
|
|
143
141
|
|
|
@@ -152,13 +150,15 @@ const schema = {
|
|
|
152
150
|
|
|
153
151
|
### body
|
|
154
152
|
|
|
153
|
+
<!-- prettier-ignore -->
|
|
155
154
|
```ts
|
|
156
|
-
body: STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral |
|
|
155
|
+
body: STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral |
|
|
156
|
+
STObject | STMulripartForm | STUrlForm
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
#### Json
|
|
160
160
|
|
|
161
|
-
To define an `application/json` request body
|
|
161
|
+
To define an `application/json` request body, use `STObject` Schema Type. Example:
|
|
162
162
|
|
|
163
163
|
```ts
|
|
164
164
|
const jsonBody = $T.object({
|
|
@@ -169,7 +169,7 @@ const jsonBody = $T.object({
|
|
|
169
169
|
|
|
170
170
|
#### Multipart
|
|
171
171
|
|
|
172
|
-
To define a `multipart/form-data` request body
|
|
172
|
+
To define a `multipart/form-data` request body, use `TMultipartForm` Schema Type. Example:
|
|
173
173
|
|
|
174
174
|
```ts
|
|
175
175
|
const multipartBody = $T.multipartForm({
|
|
@@ -180,7 +180,7 @@ const multipartBody = $T.multipartForm({
|
|
|
180
180
|
|
|
181
181
|
#### Url Form
|
|
182
182
|
|
|
183
|
-
To define an `application/x-www-form-urlencoded` request body
|
|
183
|
+
To define an `application/x-www-form-urlencoded` request body, use `TUrlForm` Schema Type. Example:
|
|
184
184
|
|
|
185
185
|
```ts
|
|
186
186
|
const urlBody = $T.urlForm({
|
|
@@ -191,11 +191,9 @@ const urlBody = $T.urlForm({
|
|
|
191
191
|
|
|
192
192
|
#### Stream
|
|
193
193
|
|
|
194
|
-
Some body request types can be streamed by using `STStream` Schema Type wrapper.
|
|
194
|
+
Some body request types can be streamed by using `STStream` Schema Type wrapper. The streamable Schema Types are `STByteArray`, `STString`, `STUrlForm` and `STMultipartForm`. This can be usefull to imporve performances in case you have heavy body payloads and you want to perform early validations on the body.
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Let's see a concrete example where that could be usefull. Imagine you want a `multipart/form-data` body request that has two properties `username` and `heavyImageFile`. In the normal case you would define something like that:
|
|
196
|
+
Let's look at a concrete example where this could be useful. Imagine you want a `multipart/form-data` body request that has two properties: `username` and `heavyImageFile`. In a normal case, you would define something like this:
|
|
199
197
|
|
|
200
198
|
```ts
|
|
201
199
|
galbe.post(
|
|
@@ -215,11 +213,9 @@ galbe.post(
|
|
|
215
213
|
})
|
|
216
214
|
```
|
|
217
215
|
|
|
218
|
-
This means that in the case where the username wouldn't pass the validation, the full request body including the `heavyImageFile
|
|
219
|
-
|
|
220
|
-
The `STStream` Schema Type wrapper was created to remediate to that issue. In practice it allows you to perform validations on the fly.
|
|
216
|
+
This means that in the case where the username wouldn't pass the validation, the full request body, including the `heavyImageFile`, would have been processed for nothing, as it is not used. This would induce unnecessary time and resource consumption.
|
|
221
217
|
|
|
222
|
-
Now in your handler, instead of receiving an object as ctx.body
|
|
218
|
+
The `STStream` Schema Type wrapper was created to remediate to remediate this issue. In practice it allows you to perform validations on the fly. Now in your handler, instead of receiving an object as `ctx.body`, you will receive an [AsyncGenerator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator).
|
|
223
219
|
|
|
224
220
|
```ts
|
|
225
221
|
galbe.post(
|
package/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -127,7 +127,11 @@ export default async (galbe: Galbe, port?: number) => {
|
|
|
127
127
|
call: async () => {
|
|
128
128
|
let nextCalled = false
|
|
129
129
|
let next = async () => {
|
|
130
|
-
|
|
130
|
+
if (nextCalled) console.error('Hook already called - ignored')
|
|
131
|
+
else {
|
|
132
|
+
nextCalled = true
|
|
133
|
+
await callChain[idx + 1].call()
|
|
134
|
+
}
|
|
131
135
|
}
|
|
132
136
|
await hook(context, next)
|
|
133
137
|
if (!nextCalled && !handlerCalled) await next()
|