muxpress

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2023 License: MIT Imports: 19 Imported by: 1

README

Go Report Card GitHub Actions Go Reference codecov

muxpress

import "github.com/szkiba/muxpress"

Package muxpress provides Express.js like micro web framework for goja.

Features

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)

}
Output
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)

}
Output
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)

}
Output
Hello World!


API

Variables

Declarations holds TypeScript declaration file contents for muxpress types.

var Declarations []byte

func NewApplicationConstructor

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)

}
Output
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)
	}

}
Output

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)
	}

}
Output

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)

JavaScript API

muxpress is an Express.js like micro web framework for goja.

Class: Application

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)
Constructors
constructor

new Application()

Creates a new application instance.

Defined in

index.d.ts:40

Methods
delete

delete(path, ...middleware): void

Routes HTTP DELETE requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:100


get

get(path, ...middleware): void

Routes HTTP GET requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:50


head

head(path, ...middleware): void

Routes HTTP HEAD requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:60


listen

listen(addr?, callback?): void

Starts the server.

Parameters
Name Type Description
addr? string -
callback? () => void host name or IP address for listening on, default 127.0.0.1
Returns

void

The instance for fluent/chaining API

Defined in

index.d.ts:135


options

options(path, ...middleware): void

Routes HTTP OPTIONS requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:110


patch

patch(path, ...middleware): void

Routes HTTP PATCH requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:90


post

post(path, ...middleware): void

Routes HTTP POST requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:70


put

put(path, ...middleware): void

Routes HTTP PUT requests to the specified path with the specified middleware functions.

You can provide multiple middleware functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:80


static

static(path, docroot): void

Mount static web content from given source directory.

Parameters
Name Type Description
path string The path where the source will be mounted on
docroot string The source directory path
Returns

void

Defined in

index.d.ts:126


use

use(path, ...middleware): void

Uses the specified middleware function or functions.

Parameters
Name Type Description
path string The path for which the middleware function is invoked (string or path pattern)
...middleware Middleware[] Middleware functions
Returns

void

Defined in

index.d.ts:118

Interface: Request

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);
});
Properties
body

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.

Defined in

index.d.ts:155


cookies

cookies: Record<string, string>

This property is an object that contains cookies sent by the request.

Defined in

index.d.ts:160


get

get: (field: string) => string

Type declaration

▸ (field): string

Returns the specified HTTP request header field (case-insensitive match).

Parameters
Name Type Description
field string the header field name
Returns

string

the header field value.

Defined in

index.d.ts:207


header

header: (field: string) => string

Type declaration

▸ (field): string

Returns the specified HTTP request header field (case-insensitive match).

Parameters
Name Type Description
field string the header field name
Returns

string

the header field value.

Defined in

index.d.ts:215


method

method: string

Contains a string corresponding to the HTTP method of the request: GET, POST, PUT, and so on.

Defined in

index.d.ts:165


params

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.

Defined in

index.d.ts:172


path

path: string

Contains the path part of the request URL.

Defined in

index.d.ts:177


protocol

protocol: string

Contains the request protocol string: either http or (for TLS requests) https.

Defined in

index.d.ts:182


query

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']
Defined in

index.d.ts:199

Interface: Response

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);
});
Properties
append

append: (field: string, value: string) => Response

Type declaration

▸ (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.

Parameters
Name Type Description
field string the header field name
value string the value to append
Returns

Response

Defined in

index.d.ts:235


binary

binary: (body: string | number[] | ArrayBuffer) => Response

Type declaration

▸ (body): Response

Sends a binray response. This method sends a response (with the "application/octet-stream" content-type) that is the body paramter.

Parameters
Name Type Description
body string | number[] | ArrayBuffer the data to send
Returns

Response

Defined in

index.d.ts:263


html

html: (body: string) => Response

Type declaration

▸ (body): Response

Sends a HTML text response. This method sends a response (with the correct content-type) that is the body string paramter.

Parameters
Name Type Description
body string the string to send
Returns

Response

Defined in

index.d.ts:256


json

json: (body: Record<string, any>) => Response

Type declaration

▸ (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.

Parameters
Name Type Description
body Record<string, any> the object to send
Returns

Response

Defined in

index.d.ts:242


redirect

redirect: (code: number, loc: string) => Response

Type declaration

▸ (code, loc): Response

Redirects to the URL, with specified status, a positive integer that corresponds to an HTTP status code.

Parameters
Name Type Description
code number the HTTP status code (301, 302, ...)
loc string the location to redirect
Returns

Response

Defined in

index.d.ts:311


send

send: (body: string | number[] | ArrayBuffer) => Response

Type declaration

▸ (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.

Parameters
Name Type Description
body string | number[] | ArrayBuffer the data to send
Returns

Response

Defined in

index.d.ts:274


set

set: (field: string, value: string) => Response

Type declaration

▸ (field, value): Response

Sets the response’s HTTP header field to value.

Parameters
Name Type Description
field string the header field name
value string the value to set
Returns

Response

Defined in

index.d.ts:303


status

status: (code: number) => Response

Type declaration

▸ (code): Response

Sets the HTTP status for the response.

Parameters
Name Type Description
code number the satus code value
Returns

Response

Defined in

index.d.ts:281


text

text: (format: string, v?: any[]) => Response

Type declaration

▸ (format, v?): Response

Sends a plain text response. This method sends a response (with the correct content-type) that is the string formatting result.

Parameters
Name Type Description
format string go format string
v? any[] format values (if any)
Returns

Response

Defined in

index.d.ts:250


type

type: (mime: string) => Response

Type declaration

▸ (mime): Response

Sets the Content-Type HTTP header to the MIME type as from mime parameter.

Params

mime the content type

Parameters
Name Type
mime string
Returns

Response

Defined in

index.d.ts:288


vary

vary: (header: string) => Response

Type declaration

▸ (header): Response

Adds the header field to the Vary response header.

Parameters
Name Type Description
header string the header filed name
Returns

Response

Defined in

index.d.ts:295

Auxiliary

Classes
Interfaces
Type Aliases
Middleware

Ƭ Middleware: (req: Request, res: Response, next: () => void) => void

Type declaration

▸ (req, res, next): void

Middleware defines middleware and request handler callback function.

Parameters
Name Type Description
req Request the request object
res Response the response object
next () => void calling from middleware enables processing next middleware
Returns

void

Defined in

index.d.ts:16

Documentation

Overview

Package muxpress provides Express.js like micro web framework for goja.

Features

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)

}
Output:

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)

}
Output:

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)

}
Output:

Hello World!

Index

Examples

Constants

This section is empty.

Variables

View Source
var Declarations []byte

Declarations holds TypeScript declaration file contents for muxpress types.

Functions

func NewApplicationConstructor

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)

}
Output:

Hello World!
Example (WithContext)

In this example custom context will passed to muxpress runtime. All http server will be stopped when context canceled.

package main

import (
	"context"
	"time"

	"github.com/dop251/goja"
	"github.com/szkiba/muxpress"
)

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)
	}

}
Output:

Example (WithLogger)

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)
	}

}
Output:

Types

type Option

type Option = func(*options)

Option is an option for the NewApplicationConstructor factory function.

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.

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

type RunnerFunc func(func() error)

RunnerFunc is used to execute middlewares on incoming requests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL