x/opine/.github/API/application.md
2.x API
Adapted from the ExpressJS API Docs.
Application
The app
object conventionally denotes the Opine application. Create it by
calling the top-level opine()
function exported by the Opine module:
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine();
app.get("/", function (req, res) {
res.send("hello world");
});
app.listen({ port: 3000 });
The app
object has methods for
- Routing HTTP requests; see for example, app.METHOD.
- Configuring middleware; see app.route.
- Rendering HTML views; see app.render.
- Registering a template engine; see app.engine.
It also has settings (properties) that affect how the application behaves; for more information, see Application settings.
The Opine application object can be referred from the request object and the response object as
req.app
, andres.app
, respectively.
Properties
app.locals
The app.locals
object has properties that are local variables within the
application.
console.dir(app.locals.title);
// => 'My App'
console.dir(app.locals.email);
// => 'me@myapp.com'
Once set, the value of app.locals
properties persist throughout the life of
the application, in contrast with res.locals
properties that are valid only
for the lifetime of the request.
Local variables are available in middleware via req.app.locals
(see
req.app
).
app.locals.title = "My App";
app.locals.email = "me@myapp.com";
app.mountpath
The app.mountpath
property contains one or more path patterns on which a
sub-app was mounted.
A sub-app is an instance of
opine
that may be used for handling the request to a route.
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine(); // the main app
const admin = opine(); // the sub app
admin.get("/", function (req, res) {
console.log(admin.mountpath); // /admin
res.send("Admin Homepage");
});
app.use("/admin", admin); // mount the sub app
It is similar to the baseUrl
property of the req
object, except
req.baseUrl
returns the matched URL path, instead of the matched patterns.
If a sub-app is mounted on multiple path patterns, app.mountpath
returns the
list of patterns it is mounted on, as shown in the following example.
const admin = opine();
admin.get("/", function (req, res) {
console.dir(admin.mountpath); // [ '/adm*n', '/manager' ]
res.send("Admin Homepage");
});
const secret = opine();
secret.get("/", function (req, res) {
console.log(secret.mountpath); // /secr*t
res.send("Admin Secret");
});
admin.use("/secr*t", secret); // load the 'secret' router on '/secr*t', on the 'admin' sub app
app.use(["/adm*n", "/manager"], admin); // load the 'admin' router on '/adm*n' and '/manager', on the parent app
Events
app.on(“mount”, callback(parent))
The mount
event is fired on a sub-app, when it is mounted on a parent app. The
parent app is passed to the callback function.
NOTE
Sub-apps will:
- Not inherit the value of settings that have a default value. You must set the value in the sub-app.
- Inherit the value of settings with no default value.
const admin = opine();
admin.on("mount", function (parent) {
console.log("Admin Mounted");
console.log(parent); // refers to the parent app
});
admin.get("/", function (req, res) {
res.send("Admin Homepage");
});
app.use("/admin", admin);
Methods
app.all(path, callback [, callback …])
This method is like the standard app.METHOD() methods, except it matches all HTTP verbs.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Examples
The following callback is executed for requests to /secret
whether using GET,
POST, PUT, DELETE, or any other HTTP request method:
app.all("/secret", function (req, res, next) {
console.log("Accessing the secret section ...");
next(); // pass control to the next handler
});
The app.all()
method is useful for mapping “global” logic for specific path
prefixes or arbitrary matches. For example, if you put the following at the top
of all other route definitions, it requires that all routes from that point on
require authentication, and automatically load a user. Keep in mind that these
callbacks do not have to act as end-points: loadUser
can perform a task, then
call next()
to continue matching subsequent routes.
app.all("*", requireAuthentication, loadUser);
Or the equivalent:
app.all("*", requireAuthentication);
app.all("*", loadUser);
Another example is white-listed “global” functionality. The example is similar to the ones above, but it only restricts paths that start with “/api”:
app.all("/api/*", requireAuthentication);
app.delete(path, callback [, callback …])
Routes HTTP DELETE requests to the specified path with the specified callback functions.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Example
app.delete("/", function (req, res) {
res.send("DELETE request to homepage");
});
app.disable(name)
Sets the Boolean setting name
to false
, where name
is one of the
properties from the app settings table. Calling
app.set('foo', false)
for a Boolean property is the same as calling
app.disable('foo')
.
For example:
app.disable("x-powered-by");
app.get("x-powered-by");
// => false
app.disabled(name)
Returns true
if the Boolean setting name
is disabled (false
), where name
is one of the properties from the app settings table.
app.disabled("x-powered-by");
// => true
app.enable("x-powered-by");
app.disabled("x-powered-by");
// => false
app.enable(name)
Sets the Boolean setting name
to true
, where name
is one of the properties
from the app settings table. Calling
app.set('foo', true)
for a Boolean property is the same as calling
app.enable('foo')
.
app.enable("x-powered-by");
app.get("x-powered-by");
// => true
app.enabled(name)
Returns true
if the setting name
is enabled (true
), where name
is one of
the properties from the app settings table.
app.enabled("x-powered-by");
// => false
app.enable("x-powered-by");
app.enabled("x-powered-by");
// => true
app.engine(ext, callback)
Register the given template engine callback for the provided extension.
The template engine callback can be async, and should take a path and template options as arguments.
async function render(path: string, options: any) {
const str = await Deno.readTextFile(path);
return str.replace("{{user.name}}", options.user.name);
}
app.engine("tmpl", render);
app.get(name)
Returns the value of name
app setting, where name
is one of the strings in
the app settings table. For example:
app.get("title");
// => undefined
app.set("title", "My Site");
app.get("title");
// => "My Site"
app.get(path, callback [, callback …])
Routes HTTP GET requests to the specified path with the specified callback functions.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Example
app.get("/", function (req, res) {
res.send("GET request to homepage");
});
app.listen(addr, [callback])
Binds and listens for connections on the specified address. This method is nearly identical to Deno’s http.listenAndServe(). An optional callback can be provided which will be executed after the server starts listening for requests - this is provided for legacy reasons to aid in transitions from Express on Node.
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine();
app.listen("localhost:3000");
The app.listen()
method returns a
http.Server
object and (for HTTP) is a convenience method for the following:
app.listen = function (options) {
const server = http.serve(options);
(async () => {
for await (const request of server) {
app(request);
}
})();
return server;
};
app.listen([port], [callback])
Binds and listens for connections on the specified numerical port. If no port is provided, an available port is assigned for you. This method is similar to Deno’s http.listenAndServe().
This method is supported for legacy reasons to aid in transitions from Express on Node.
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine();
const PORT = 3000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
app.listen(httpOptions, [callback])
Binds and listens for connections using the specified http.HTTPOptions. This method is nearly identical to Deno’s http.listenAndServe(). An optional callback can be provided which will be executed after the server starts listening for requests - this is provided for legacy reasons to aid in transitions from Express on Node.
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine();
app.listen({ port: 3000 });
app.listen(httpsOptions, [callback])
Binds and listens for connections using the specified http.HTTPSOptions. This method is nearly identical to Deno’s http.listenAndServeTLS(). An optional callback can be provided which will be executed after the server starts listening for requests - this is provided for legacy reasons to aid in transitions from Express on Node.
import opine from "https://deno.land/x/opine@2.0.1/mod.ts";
const app = opine();
app.listen({ port: 3000, certFile: "myCertFile", keyFile: "myKeyFile" });
app.METHOD(path, callback [, callback …])
Routes an HTTP request, where METHOD is the HTTP method of the request, such as
GET, PUT, POST, and so on, in lowercase. Thus, the actual methods are
app.get()
, app.post()
, app.put()
, and so on. See
Routing methods below for the complete list.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Routing methods
Opine supports the following routing methods corresponding to the HTTP methods of the same names:
checkout
copy
delete
get
head
lock
merge
mkactivity
mkcol
move
m-search
notify
options
patch
post
purge
put
report
search
subscribe
trace
unlock
unsubscribe
The API documentation has explicit entries only for the most popular HTTP
methods app.get()
, app.post()
, app.put()
, and app.delete()
. However, the
other methods listed above work in exactly the same way.
To route methods that translate to invalid JavaScript variable names, use the
bracket notation. For example, app['m-search']('/', function ...
.
The
app.get()
function is automatically called for the HTTPHEAD
method in addition to theGET
method ifapp.head()
was not called for the path beforeapp.get()
.
The method, app.all()
, is not derived from any HTTP method and loads
middleware at the specified path for all HTTP request methods. For more
information, see app.all.
app.param([name], callback)
Add callback triggers to route parameters, where name
is the name of the
parameter or an array of them, and callback
is the callback function. The
parameters of the callback function are the request object, the response object,
the next middleware, the value of the parameter and the name of the parameter,
in that order.
If name
is an array, the callback
trigger is registered for each parameter
declared in it, in the order in which they are declared. Furthermore, for each
declared parameter except the last one, a call to next
inside the callback
will call the callback for the next declared parameter. For the last parameter,
a call to next
will call the next middleware in place for the route currently
being processed, just like it would if name
were just a string.
For example, when :user
is present in a route path, you may map user loading
logic to automatically provide res.locals.user
to the route/middleware, or
perform validations on the parameter input.
app.param("user", function (req, res, next, id) {
// try to get the user details from the User model and attach it to the request object
User.find(id, function (err, user) {
if (err) {
next(err);
} else if (user) {
res.locals.user = user;
next();
} else {
next(new Error("failed to load user"));
}
});
});
Param callback functions are local to the router on which they are defined. They
are not inherited by mounted apps or routers. Hence, param callbacks defined on
app
will be triggered only by route parameters defined on app
routes.
All param callbacks will be called before any handler of any route in which the param occurs, and they will each be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples.
app.param("id", function (req, res, next, id) {
console.log("CALLED ONLY ONCE");
next();
});
app.get("/user/:id", function (req, res, next) {
console.log("although this matches");
next();
});
app.get("/user/:id", function (req, res) {
console.log("and this matches too");
res.end();
});
On GET /user/42
, the following is printed:
CALLED ONLY ONCE
although this matches
and this matches too
app.param(["id", "page"], function (req, res, next, value) {
console.log("CALLED ONLY ONCE with", value);
next();
});
app.get("/user/:id/:page", function (req, res, next) {
console.log("although this matches");
next();
});
app.get("/user/:id/:page", function (req, res) {
console.log("and this matches too");
res.end();
});
On GET /user/42/3
, the following is printed:
CALLED ONLY ONCE with 42
CALLED ONLY ONCE with 3
although this matches
and this matches too
app.path()
Returns the canonical path of the app, a string.
const app = opine();
const blog = opine();
const blogAdmin = opine();
app.use("/blog", blog);
blog.use("/admin", blogAdmin);
console.dir(app.path()); // ''
console.dir(blog.path()); // '/blog'
console.dir(blogAdmin.path()); // '/blog/admin'
The behavior of this method can become very complicated in complex cases of
mounted apps: it is usually better to use req.baseUrl
to get the canonical
path of the app.
app.post(path, callback [, callback …])
Routes HTTP POST requests to the specified path with the specified callback functions.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Example
app.post("/", function (req, res) {
res.send("POST request to homepage");
});
app.put(path, callback [, callback …])
Routes HTTP PUT requests to the specified path with the specified callback functions.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Example
app.put("/", function (req, res) {
res.send("PUT request to homepage");
});
app.render(view, [locals], callback)
Returns the rendered HTML of a view via the callback
function. It accepts an
optional parameter that is an object containing local variables for the view. It
is like res.render(), except it cannot send the rendered view to
the client on its own.
Think of
app.render()
as a utility function for generating rendered view strings. Internallyres.render()
usesapp.render()
to render views.The local variable
cache
is reserved for enabling view cache. Set it totrue
, if you want to cache view during development; view caching is enabled in production by default.
app.render("email", function (err, html) {
// ...
});
app.render("email", { name: "Deno" }, function (err, html) {
// ...
});
app.route(path)
Returns an instance of a single route, which you can then use to handle HTTP
verbs with optional middleware. Use app.route()
to avoid duplicate route names
(and thus typo errors).
const app = opine();
app
.route("/events")
.all(function (req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function (req, res, next) {
res.json({});
})
.post(function (req, res, next) {
// maybe add a new event...
});
app.set(name, value)
Assigns setting name
to value
. You may store any value that you want, but
certain names can be used to configure the behavior of the server. These special
names are listed in the app settings table.
Calling app.set('foo', true)
for a Boolean property is the same as calling
app.enable('foo')
. Similarly, calling app.set('foo', false)
for a Boolean
property is the same as calling app.disable('foo')
.
Retrieve the value of a setting with app.get()
.
app.set("title", "My Site");
app.get("title"); // "My Site"
Application Settings
The following table lists application settings.
Note that sub-apps will:
- Not inherit the value of settings that have a default value. You must set the value in the sub-app.
- Inherit the value of settings with no default value; these are explicitly noted in the table below.
Exceptions: Sub-apps will inherit the value of trust proxy
even though it has
a default value.
Property | Type | Description | Default |
---|---|---|---|
|
Boolean | Enable case sensitivity. When enabled, “/Foo” and “/foo” are different routes. When disabled, “/Foo” and “/foo” are treated the same. NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Varied | Set the ETag response header. For possible values, see the `etag` options table |
|
|
String | Specifies the default JSONP callback name. | “callback” |
|
Boolean |
Enable escaping JSON responses from the `res.json`, `res.jsonp`, and `res.send` APIs. This will escape the characters `<`, `>`, and `&` as Unicode escape sequences in JSON. The purpose of this it to assist with mitigating certain types of persistent XSS attacks when clients sniff responses for HTML.
NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Varied | The ‘replacer’ argument used by `JSON.stringify`.
NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Varied | The ‘space’ argument used by `JSON.stringify`.
This is typically set to the number of spaces to use to indent prettified JSON.
NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Boolean | Enable strict routing. When enabled, the router treats “/foo” and “/foo/” as different. Otherwise, the router treats “/foo” and “/foo/” as the same. NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Number | The number of dot-separated parts of the host to remove to access subdomain. |
2 |
|
Varied |
Indicates the app is behind a front-facing proxy, and to use the `X-Forwarded-*` headers to determine the connection and the IP address of the client. NOTE: `X-Forwarded-*` headers are easily spoofed and the detected IP addresses are unreliable. When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies. The `req.ips` property, then contains an array of IP addresses the client is connected through. To enable it, use the values described in the trust proxy options table. The `trust proxy` setting is implemented using a Deno port of the proxy-addr NPM package. For more information, see its documentation. NOTE: Sub-apps will inherit the value of this setting, even though it has a default value. |
|
|
String or Array | A directory or an array of directories for the application’s views. If an array, the views are looked up in the order they occur in the array. |
|
|
Boolean | Enables view template compilation caching.
NOTE: Sub-apps will not inherit the value of this setting in production
(when |
|
|
String | The default engine extension to use when omitted.
NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
|
Boolean | Enables the “X-Powered-By: Opine” HTTP header. |
|
trust proxy
setting
Options for Type | Value |
---|---|
Boolean |
If If |
String String containing comma-separated values Array of strings |
An IP address, subnet, or an array of IP addresses, and subnets to trust. Pre-configured subnet names are:
Set IP addresses in any of the following ways: Specify a single subnet: app.set("trust proxy", "loopback"); Specify a subnet and an address: app.set("trust proxy", "loopback, 123.123.123.123"); Specify multiple subnets as CSV: app.set("trust proxy", "loopback, linklocal, uniquelocal"); Specify multiple subnets as an array: app.set("trust proxy", ["loopback", "linklocal", "uniquelocal"]); When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client’s IP address. |
Number | Trust the nth hop from the front-facing proxy server as the client. |
Function |
Custom trust implementation. Use this only if you know what you are doing.
app.set("trust proxy", function (ip: string) {
if (ip === "127.0.0.1" || ip === "123.123.123.123") return true;
// trusted IPs
else return false;
}); |
etag
setting
Options for Type | Value |
---|---|
Boolean |
`true` enables weak ETag. This is the default setting. `false` disables ETag altogether. |
String |
If “strong”, enables strong ETag. If “weak”, enables weak ETag. |
Function | Custom ETag function implementation. Use this only if you know what you are doing.
app.set("etag", function (body, encoding) {
return generateHash(body, encoding); // consider the function is defined
}); |
app.use([path,] callback [, callback…])
Mounts the specified middleware function or functions at the specified path: the
middleware function is executed when the base of the requested path matches
path
.
Arguments
Argument | Description | Default |
---|---|---|
path |
The path for which the middleware function is invoked; can be any of:
For examples, see Path examples. |
’/’ (root path) |
callback |
Callback functions; can be:
You can provide multiple callback functions that behave just like
middleware, except that these callbacks can invoke
Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples. |
None |
Description
A route will match any path that follows its path immediately with a “/
”. For
example: app.use('/apple', ...)
will match “/apple”, “/apple/images”,
“/apple/images/news”, and so on.
Since path
defaults to “/”, middleware mounted without a path will be executed
for every request to the app. For example, this middleware function will be
executed for every request to the app:
app.use(function (req, res, next) {
console.log("Time: %d", Date.now());
next();
});
NOTE
Sub-apps will:
- Not inherit the value of settings that have a default value. You must set the value in the sub-app.
- Inherit the value of settings with no default value.
Middleware functions are executed sequentially, therefore the order of middleware inclusion is important.
// this middleware will not allow the request to go beyond it
app.use(function (req, res, next) {
res.send("Hello World");
});
// requests will never reach this route
app.get("/", function (req, res) {
res.send("Welcome");
});
Error-handling middleware
Error-handling middleware always takes four arguments. You must provide four
arguments to identify it as an error-handling middleware function. Even if you
don’t need to use the next
object, you must specify it to maintain the
signature. Otherwise, the next
object will be interpreted as regular
middleware and will fail to handle errors. For details about error-handling
middleware.
Define error-handling middleware functions in the same way as other middleware
functions, except with four arguments instead of three, specifically with the
signature (err, req, res, next)
):
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send("Something broke!");
});
Path examples
The following table provides some simple examples of valid path
values for
mounting middleware.
Type | Example |
---|---|
Path |
This will match paths starting with `/abcd`:
app.use("/abcd", function (req, res, next) {
next();
}); |
Path Pattern |
This will match paths starting with `/abcd` and `/abd`:
app.use("/abc?d", function (req, res, next) {
next();
}); This will match paths starting with app.use("/ab+cd", function (req, res, next) {
next();
}); This will match paths starting with app.use("/ab*cd", function (req, res, next) {
next();
}); This will match paths starting with app.use("/a(bc)?d", function (req, res, next) {
next();
}); |
Regular Expression |
This will match paths starting with `/abc` and `/xyz`:
app.use(/\/abc|\/xyz/, function (req, res, next) {
next();
}); |
Array |
This will match paths starting with `/abcd`, `/xyza`, `/lmn`, and `/pqr`:
app.use(["/abcd", "/xyza", /\/lmn|\/pqr/], function (req, res, next) {
next();
}); |
Middleware callback function examples
The following table provides some simple examples of middleware functions that
can be used as the callback
argument to app.use()
, app.METHOD()
, and
app.all()
. Even though the examples are for app.use()
, they are also valid
for app.use()
, app.METHOD()
, and app.all()
.
Usage | Example |
---|---|
Single Middleware | You can define and mount a middleware function locally.
app.use(function (req, res, next) {
next();
}); A router is valid middleware. const router = Router();
router.get("/", function (req, res, next) {
next();
});
app.use(router); An Opine app is valid middleware. const subApp = opine();
subApp.get("/", function (req, res, next) {
next();
});
app.use(subApp); |
Series of Middleware |
You can specify more than one middleware function at the same mount path.
const r1 = Router();
r1.get("/", function (req, res, next) {
next();
});
const r2 = Router();
r2.get("/", function (req, res, next) {
next();
});
app.use(r1, r2); |
Array |
Use an array to group middleware logically.
If you pass an array of middleware as the first or only middleware parameters,
then you must specify the mount path.
const r1 = Router();
r1.get("/", function (req, res, next) {
next();
});
const r2 = Router();
r2.get("/", function (req, res, next) {
next();
});
app.use("/", [r1, r2]); |
Combination |
You can combine all the above ways of mounting middleware.
function mw1(req, res, next) {
next();
}
function mw2(req, res, next) {
next();
}
const r1 = Router();
r1.get("/", function (req, res, next) {
next();
});
const r2 = Router();
r2.get("/", function (req, res, next) {
next();
});
const subApp = opine();
subApp.get("/", function (req, res, next) {
next();
});
app.use(mw1, [mw2, r1, r2], subApp); |