import "github.com/szkiba/muxpress"
Package muxpress provides Express.js like micro web framework for goja.
Easy integration was the main design goal. Major features:
- Express.js like JavaScript API
- Context-aware implementation
- Event loop ready, tested with goja_nodejs and k6 event loop
- Also works without event loop
Example (Fixed)
This example starts a server and listens for connections on port 3000. The application responds with "Hello World!" for requests to the root URL. All other routes are answered with a 404 not found message.
In this example, the name of the constructor is `Application`, but the name you use is up to you.
package main
import (
"fmt"
"github.com/dop251/goja"
req "github.com/imroc/req/v3"
"github.com/szkiba/muxpress"
)
func main() {
const SCRIPT = `
const app = new Application()
app.get("/", (req, res) => {
res.text("Hello World!")
})
app.listen(3000)
`
runtime := goja.New()
ctor, _ := muxpress.NewApplicationConstructor(runtime)
runtime.Set("Application", ctor)
runtime.RunScript("example", SCRIPT)
message := req.MustGet("http://127.0.0.1:3000").String()
fmt.Println(message)
}
Hello World!
Example (Options)
This example starts a server and listens for connections on port 3000. The application responds with "Hello World!" for requests to the root URL. All other routes are answered with a 404 not found message. Constructor created with a custom context and logger passed as an option.
In this example, the name of the constructor is `Application`, but the name you use is up to you.
package main
import (
"context"
"fmt"
"github.com/dop251/goja"
req "github.com/imroc/req/v3"
"github.com/sirupsen/logrus"
"github.com/szkiba/muxpress"
)
func main() {
const SCRIPT = `
const app = new Application()
app.get("/", (req, res) => {
res.text("Hello World!")
})
app.listen(3000)
`
runtime := goja.New()
ctor, _ := muxpress.NewApplicationConstructor(
runtime,
muxpress.WithContext(context.TODO),
muxpress.WithLogger(logrus.StandardLogger().WithField("example", "hello")),
)
runtime.Set("Application", ctor)
message := req.MustGet("http://127.0.0.1:3000").String()
fmt.Println(message)
}
Hello World!
Example (Random)
This example starts a server and listens for connections on a randomly assigned free port. The application responds with "Hello World!" for requests to the root URL. All other routes are answered with a 404 not found message. The allocated port is accessed via the `app.port`` property. The example takes advantage of the fact that goja returns the value of the last expression evaluated.
In this example, the name of the constructor is `Application`, but the name you use is up to you.
package main
import (
"fmt"
"github.com/dop251/goja"
req "github.com/imroc/req/v3"
"github.com/szkiba/muxpress"
)
func main() {
const SCRIPT = `
const app = new Application()
app.get("/", (req, res) => {
res.text("Hello World!")
})
app.listen()
app.port // goja runtime returns the last evaluated expression
`
runtime := goja.New()
ctor, _ := muxpress.NewApplicationConstructor(runtime)
runtime.Set("Application", ctor)
port, _ := runtime.RunScript("example", SCRIPT)
location := fmt.Sprintf("http://127.0.0.1:%d", port.ToInteger())
message := req.MustGet(location).String()
fmt.Println(message)
}
Hello World!
Declarations holds TypeScript declaration file contents for muxpress types.
var Declarations []byte
func NewApplicationConstructor(runtime *goja.Runtime, option ...Option) (func(call goja.ConstructorCall) *goja.Object, error)
NewApplicationConstructor creates an application constructor function. The returned constructor function is ready for use and assignable to any name in a given [goja.Runtime]. This allow to decide the name of the JavaScript constructor. You can pass [Option] parameters to customize the muxpress runtime behavior.
Example
This example starts a server and listens for connections on a randomly assigned free port. The application responds with "Hello World!" for requests to the root URL. In this example, the name of the constructor is `WebApp`, but the name you use is up to you.
package main
import (
"fmt"
"github.com/dop251/goja"
req "github.com/imroc/req/v3"
"github.com/szkiba/muxpress"
)
func main() {
const SCRIPT = `
const app = new WebApp()
app.get("/", (req, res) => {
res.text("Hello World!")
})
app.listen()
app.port // goja runtime returns the last evaluated expression
`
runtime := goja.New()
ctor, err := muxpress.NewApplicationConstructor(runtime)
if err != nil {
panic(err)
}
err = runtime.Set("WebApp", ctor)
if err != nil {
panic(err)
}
port, err := runtime.RunScript("example", SCRIPT)
if err != nil {
panic(err)
}
message := req.MustGet("http://localhost:" + port.String())
fmt.Println(message)
}
Hello World!
Example (With Context)
In this example custom context will passed to muxpress runtime. All http server will be stopped when context canceled.
package main
import (
"context"
"github.com/dop251/goja"
"github.com/szkiba/muxpress"
"time"
)
func main() {
runtime := goja.New()
ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
getContext := func() context.Context { return ctx }
ctor, err := muxpress.NewApplicationConstructor(runtime, muxpress.WithContext(getContext))
if err != nil {
panic(err)
}
err = runtime.Set("WebApp", ctor)
if err != nil {
panic(err)
}
}
Example (With Logger)
In this example every log entry come from muxpress runtime will contains a `source` field with value `script`.
package main
import (
"github.com/dop251/goja"
"github.com/sirupsen/logrus"
"github.com/szkiba/muxpress"
)
func main() {
runtime := goja.New()
logger := logrus.StandardLogger().WithField("source", "script")
ctor, err := muxpress.NewApplicationConstructor(runtime, muxpress.WithLogger(logger))
if err != nil {
panic(err)
}
err = runtime.Set("WebApp", ctor)
if err != nil {
panic(err)
}
}
type Option
Option is an option for the [NewApplicationConstructor] factory function.
type Option = func(*options)
func WithContext
func WithContext(context func() context.Context) Option
WithContext returns an Option that specifies a [context.Context] getter function to be used for stopping application when context is canceled or done. Default is to use [context.TODO].
func WithFS
func WithFS(filesystem afero.Fs) Option
WithFS returns an Option that specifies a [afero.Fs] filesystem to be used for accessing static files.
func WithLogger
func WithLogger(logger logrus.FieldLogger) Option
WithLogger returns an Option that specifies a [logrus.FieldLogger] logger to be used for logging.
func WithRunOnLoop
func WithRunOnLoop(runOnLoop func(func(*goja.Runtime))) Option
WithRunOnLoop returns an Option that specifies [RunOnLoop] function from [goja_nodejs] package to be used for execute middlewares for incoming requests.
[RunOnLoop]: https://pkg.go.dev/github.com/dop251/goja_nodejs/eventloop#EventLoop.RunOnLoop [goja_nodejs]: https://github.com/dop251/goja_nodejs
func WithRunner
func WithRunner(runner RunnerFunc) Option
WithRunner returns an Option that specifies a runner function to be used for execute middlewares for incoming requests. This option allows you to schedule middleware calls in the event loop.
Since [goja.Runtime] is not goroutine-safe, the default is to execute middlewares in synchronous way.
func syncRunner() RunnerFunc {
var mu sync.Mutex
return func(fn func() error) {
mu.Lock()
defer mu.Unlock()
if err := fn(); err != nil {
panic(err)
}
}
}
type RunnerFunc
RunnerFunc is used to execute middlewares on incoming requests.
type RunnerFunc func(func() error)
muxpress is an Express.js like micro web framework for goja.
An application object represents a web application.
The following example starts a server and listens for connections on port 3000. The application responds with a JSON object for requests to the root URL. All other routes are answered with a 404 not found message.
In this example, the name of the constructor is Application
, but the name you use is up to you.
Example
const app = new Application()
app.get('/', (req, res) => {
res.json({message:"Hello World!"})
})
app.listen(3000)
• new Application()
Creates a new application instance.
▸ delete(path
, ...middleware
): void
Routes HTTP DELETE
requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ get(path
, ...middleware
): void
Routes HTTP GET requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ head(path
, ...middleware
): void
Routes HTTP HEAD requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ listen(addr?
, callback?
): void
Starts the server.
Name | Type | Description |
---|---|---|
addr? |
string |
- |
callback? |
() => void |
host name or IP address for listening on, default 127.0.0.1 |
void
The instance for fluent/chaining API
▸ options(path
, ...middleware
): void
Routes HTTP OPTIONS requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ patch(path
, ...middleware
): void
Routes HTTP PATCH requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ post(path
, ...middleware
): void
Routes HTTP POST requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ put(path
, ...middleware
): void
Routes HTTP PUT requests to the specified path with the specified middleware functions.
You can provide multiple middleware functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
▸ static(path
, docroot
): void
Mount static web content from given source directory.
Name | Type | Description |
---|---|---|
path |
string |
The path where the source will be mounted on |
docroot |
string |
The source directory path |
void
▸ use(path
, ...middleware
): void
Uses the specified middleware function or functions.
Name | Type | Description |
---|---|---|
path |
string |
The path for which the middleware function is invoked (string or path pattern) |
...middleware |
Middleware [] |
Middleware functions |
void
The req
object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.
In this documentation and by convention, the object is always referred to as req
(and the HTTP response is res
) but its actual name is determined by the parameters to the callback function in which you’re working.
Example
app.get("/user/:id", function (req, res) {
res.send("user " + req.params.id);
});
• body: Record
<string
, any
>
Contains key-value pairs of data submitted in the request body.
By default, it is undefined, and is populated when the request
Content-Type is application/json
.
• cookies: Record
<string
, string
>
This property is an object that contains cookies sent by the request.
• get: (field
: string
) => string
▸ (field
): string
Returns the specified HTTP request header field (case-insensitive match).
Name | Type | Description |
---|---|---|
field |
string |
the header field name |
string
the header field value.
• header: (field
: string
) => string
▸ (field
): string
Returns the specified HTTP request header field (case-insensitive match).
Name | Type | Description |
---|---|---|
field |
string |
the header field name |
string
the header field value.
• method: string
Contains a string corresponding to the HTTP method of the request: GET, POST, PUT, and so on.
• params: Record
<string
, string
>
This property is an object containing properties mapped to the named route parameters. For example, if you have the route /user/:name, then the “name” property is available as req.params.name. This object defaults to empty.
• path: string
Contains the path part of the request URL.
• protocol: string
Contains the request protocol string: either http or (for TLS requests) https.
• query: Record
<string
, any
>
This property is an object containing a property for each query string parameter in the route.
For example:
// GET /search?q=tobi+ferret
console.dir(req.query.q);
// => 'tobi ferret'
// GET /shoes?color=blue&color=black&color=red
console.dir(req.query.color);
// => ['blue', 'black', 'red']
The res
object represents the HTTP response that a server sends when it gets an HTTP request.
In this documentation and by convention, the object is always referred to as res
(and the HTTP request is req
) but its actual name is determined by the parameters to the callback function in which you’re working.
Example
app.get("/user/:id", function (req, res) {
res.send("user " + req.params.id);
});
• append: (field
: string
, value
: string
) => Response
▸ (field
, value
): Response
Appends the specified value to the HTTP response header field. If the header is not already set, it creates the header with the specified value.
Name | Type | Description |
---|---|---|
field |
string |
the header field name |
value |
string |
the value to append |
• binary: (body
: string
| number
[] | ArrayBuffer
) => Response
▸ (body
): Response
Sends a binray response. This method sends a response (with the "application/octet-stream" content-type) that is the body paramter.
Name | Type | Description |
---|---|---|
body |
string | number [] | ArrayBuffer |
the data to send |
• html: (body
: string
) => Response
▸ (body
): Response
Sends a HTML text response. This method sends a response (with the correct content-type) that is the body string paramter.
Name | Type | Description |
---|---|---|
body |
string |
the string to send |
• json: (body
: Record
<string
, any
>) => Response
▸ (body
): Response
Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a JSON string.
Name | Type | Description |
---|---|---|
body |
Record <string , any > |
the object to send |
• redirect: (code
: number
, loc
: string
) => Response
▸ (code
, loc
): Response
Redirects to the URL, with specified status, a positive integer that corresponds to an HTTP status code.
Name | Type | Description |
---|---|---|
code |
number |
the HTTP status code (301, 302, ...) |
loc |
string |
the location to redirect |
• send: (body
: string
| number
[] | ArrayBuffer
) => Response
▸ (body
): Response
Sends the HTTP response.
When the parameter is a ArrayBuffer or number[], the method sets the Content-Type response header field to “application/octet-stream”. When the parameter is a String, the method sets the Content-Type to “text/html”. Otherwise the method sets the Content-Type to "application/json" and convert paramter to JSON representation before sending.
Name | Type | Description |
---|---|---|
body |
string | number [] | ArrayBuffer |
the data to send |
• set: (field
: string
, value
: string
) => Response
▸ (field
, value
): Response
Sets the response’s HTTP header field to value.
Name | Type | Description |
---|---|---|
field |
string |
the header field name |
value |
string |
the value to set |
• status: (code
: number
) => Response
▸ (code
): Response
Sets the HTTP status for the response.
Name | Type | Description |
---|---|---|
code |
number |
the satus code value |
• text: (format
: string
, v?
: any
[]) => Response
▸ (format
, v?
): Response
Sends a plain text response. This method sends a response (with the correct content-type) that is the string formatting result.
Name | Type | Description |
---|---|---|
format |
string |
go format string |
v? |
any [] |
format values (if any) |
• type: (mime
: string
) => Response
▸ (mime
): Response
Sets the Content-Type HTTP header to the MIME type as from mime parameter.
Params
mime the content type
Name | Type |
---|---|
mime |
string |
• vary: (header
: string
) => Response
▸ (header
): Response
Adds the header field to the Vary response header.
Name | Type | Description |
---|---|---|
header |
string |
the header filed name |
Ƭ Middleware: (req
: Request
, res
: Response
, next
: () => void
) => void
▸ (req
, res
, next
): void
Middleware defines middleware and request handler callback function.
Name | Type | Description |
---|---|---|
req |
Request |
the request object |
res |
Response |
the response object |
next |
() => void |
calling from middleware enables processing next middleware |
void