Cheatsheet

A quick overview of the most important mojo.js classes, helpers and hooks.

Classes

App

The mojo.js application object. Created with the mojo function, which accepts a few options, but none of them are required.

  1. import mojo from '@mojojs/core';
  2. const app = mojo({
  3. // Default configuration
  4. config: {name: 'My Application'},
  5. // Detect if the application has been imported and disable the command line interface if it has
  6. detectImport: true,
  7. // Format for HTTP exceptions ("html", "json", or "txt")
  8. exceptionFormat: 'html',
  9. // Rotating secret passphrases used for signed cookies and the like
  10. secrets: ['s3cret'],
  11. // Operating mode for application
  12. mode: 'development'
  13. });

It is usually called app.

  1. // config: plain configuration object, filled with data by config plugins, use it to to store arbitrary information
  2. app.config.foo = 'bar';
  3. const foo = app.config.foo;
  4. // exceptionFormat: format for HTTP exceptions ("html", "json", or "txt")
  5. app.exceptionFormat = 'html';
  6. // secrets: rotating secret passphrases used for signed cookies and the like
  7. app.secrets = ['s3cret pa$$phrase'];
  8. // mode: operating mode for application
  9. const mode = app.mode;
  10. // home: a `Path` object with the path of the application home directory
  11. const path = app.home.child('config.json').toString();
  12. const content = app.home.child('views', 'foo.html.tmpl').readFile('utf8');
  13. // models: plain object, use it to store arbitray models
  14. app.models.frameworks = [{name: 'Catalyst'}, {name: 'Mojolicious'}, {name: 'mojo.js'}];
  15. // log: the application logger
  16. const child = app.log.child({someContextValue: 123, anotherContextValue: 'test'});
  17. child.debug('Shut up and take my money!');
  18. // validator: general purpose JSON schema validator
  19. app.validator.addSchema({type: 'object'}, 'testForm');
  20. const validate = app.validator.schema('testForm');
  21. // ua: a `UserAgent` object for use inside the application
  22. const res = await app.ua.get('https://mojolicious.org');
  23. const dom = await res.html();
  24. const title = dom.at('title').text();
  25. // session: the encrypted cookie based session manager
  26. app.session.cookieName = 'myapp';
  27. app.session.sameSite = 'strict';
  28. // mime: manage MIME types
  29. app.mime.custom['foo'] = 'text/foo; charset=utf-8';
  30. // renderer: the application renderer, use it to add template engines and view directories
  31. app.renderer.addEngine('foo', fooEngine);
  32. app.renderer.viewPaths.push(app.home.child('templates').toString());
  33. // cli: the command line interface, use it to add your own custom commands
  34. app.cli.commandPaths.push(app.home.child('cli').toString());
  35. // newMockContext: create a new mock context for application (very useful for testing)
  36. const ctx = app.newMockContext();
  37. const html = ctx.faviconTag();
  38. // newTestUserAgent: create a new test user-agent for application
  39. const ua = await app.newTestUserAgent();
  40. (await ua.getOk('/')).statusIs(200).bodyIs('Hello World!');
  41. // addAppHook: add an application hook to extend the framework
  42. app.addAppHook('server:start', async app => {
  43. app.config.deployment = 'server';
  44. });
  45. // addContextHook: add a context hook to extend the framework
  46. app.addContextHook('dispatch:before', async ctx => {
  47. ctx.res.set('Server', 'MyServer 1.0');
  48. });
  49. // addHelper: add a helper
  50. app.addHelper('debug', (ctx, str) => {
  51. ctx.app.log.debug(str);
  52. });

The router is the most commonly used application property and there are various shortcut methods for it.

  1. // Create routes to controllers
  2. app.router.get('/users/:id').to('#users#show');
  3. // Create routes to controllers via shortcut method
  4. app.get('/users/:id').to('users#show');
  5. // Add placeholder types
  6. app.router.addType('futuramaName', ['bender', 'leela']);
  7. // Create routes directly to actions
  8. app.any('/hello/<name:futuramaName>', ctx => ctx.render({text: `Hello ${ctx.stash.name}`}));

Context

The main object representing an HTTP request or WebSocket handshake, usually called ctx.

  1. // req: the request object
  2. const path = ctx.req.path;
  3. // res: the response object
  4. await ctx.res.status(200).type('text/html').send('Hello World!');
  5. // params: all form parameters combined
  6. const params = await ctx.params();
  7. const foo = params.get('foo');
  8. // log: log messages with request id as context
  9. ctx.log.debug('Shut up and take my money!');
  10. // urlFor: generate URL for route or path
  11. const url = ctx.urlFor('index');
  12. // urlForFile: generate URL for static file
  13. const url = ctx.urlForFile('/foo/app.css');
  14. // urlWith: generate URL for route or path and preserve the current query parameters
  15. const url = ctx.urlWith('index');
  16. // session: persistent data storage for the next few requests.
  17. const session = await ctx.session();
  18. session.user = 'kraih';
  19. const user = session.user;
  20. // flash: data storage persistent only for the next request.
  21. const flash = await ctx.flash();
  22. flash.confirmation = 'The record has been updated';
  23. // config: access application config
  24. const foo = ctx.config.foo;
  25. // models: access application models
  26. const users = ctx.models.users;
  27. // schema: access validation function for JSON schema
  28. const validate = ctx.schema({$id: 'testForm', type: 'object'});
  29. const result = validate(await ctx.req.json());
  30. const valid = result.isValid;
  31. // redirectTo: send `302` redirect response
  32. await ctx.redirectTo('index');
  33. await ctx.redirectTo('https://mojojs.org');
  34. // respondTo: automatically select best possible representation for resource
  35. await ctx.respondTo({
  36. json: {json: {hello: 'world'}},
  37. any: {text: 'Hello World!'}
  38. });
  39. // sendFile: send static file
  40. await ctx.sendFile(ctx.home.child('index.js'));
  41. // exceptionFormat: format for HTTP exceptions ("html", "json", or "txt")
  42. ctx.exceptionFormat = 'html';
  43. // app: the mojo.js application object
  44. const app = ctx.app;

The ctx.render() method is the most common way to generate a response.

  1. // Create a response from a string
  2. await ctx.render({text: 'Hello World!'});
  3. // Create a JSON response from a data structure
  4. await ctx.render({json: {hello: 'world'}});
  5. // Create a YAML response from a data structure
  6. await ctx.render({yaml: {hello: 'world'}});
  7. // Create a response by rendering the view "views/foo/bar.*.*"
  8. await ctx.render({view: 'foo/bar'});
  9. // Create a response by rendering an inline view (tmpl by default)
  10. await ctx.render({inline: 'Hello <%= name %>'}, {name: 'Mojo'});
  11. // Render something, but return it as a string
  12. const json = await ctx.renderToString({json: {hello: 'world'}});

Request

The ctx.req property of the context object. All URLs use URL objects and form parameters URLSearchParams objects.

  1. // method: HTTP request method
  2. const method = ctx.req.method;
  3. // path: request path
  4. const path = ctx.req.path;
  5. // baseURL: base URL for request (protocol might be from the X-Forwarded-Proto header)
  6. const baseURL = ctx.req.baseURL;
  7. // ip: the client IP address (may be from the X-Forwarded-For header)
  8. const address = ctx.req.ip;
  9. // userinfo: Basic authentication data
  10. const userinfo = ctx.req.userinfo;
  11. // requestId: reasonably unique request id
  12. const requestId = ctx.req.requestId;
  13. // get: request headers
  14. const accept = ctx.req.get('Accept');
  15. // getCookie: get cookie values
  16. const cookie = ctx.req.getCookie('foo');
  17. // query: query parameters
  18. const params = ctx.req.query();

There are multiple methods to receive the request content in various formats.

  1. // Retrieve request body as a string
  2. const text = await ctx.req.text();
  3. // Retrieve request body as a `Buffer`
  4. const buffer = await ctx.req.buffer();
  5. // Retrieve "application/x-www-form-urlencoded" or "multipart/form-data" form parameters
  6. const params = await ctx.req.form();
  7. const foo = params.get('foo');
  8. // Retrieve request body as parsed JSON
  9. const data = await ctx.req.json();
  10. // Retrieve request body as parsed YAML
  11. const data = await ctx.req.yaml();
  12. // Retrieve request body as parsed XML via `@mojojs/dom`
  13. const dom = await ctx.req.xml();
  14. const title = dom.at('foo > title').text();
  15. // Retrieve request body as parsed HTML via `@mojojs/dom`
  16. const dom = await ctx.req.html();
  17. const title = dom.at('head > title').text();
  18. // Retrieve request body as a `Readable` stream
  19. const stream = ctx.req.createReadStream();
  20. // Pipe request body to `stream.Writable` object
  21. await ctx.req.pipe(process.stdout);
  22. // Retrieve request body from async iterator
  23. const parts = [];
  24. for await (const chunk of ctx.req) {
  25. parts.push(chunk);
  26. }
  27. const buffer = Buffer.concat(parts);

Response

The ctx.res property of the context object.

  1. // status: set response code and message
  2. ctx.res.status(200);
  3. ctx.res.status(289, 'Custom Status');
  4. // set: set response headers
  5. ctx.res.set('Server', 'Mojo/1.0');
  6. // type: set "Content-Type" header
  7. ctx.res.type('quote/futurama');
  8. // length: set "Content-Length" header
  9. ctx.res.length(12);
  10. // setCookie: set cookie
  11. ctx.res.setCookie('user', 'Bender', {path: '/', httpOnly: true});

The ctx.res.send() method is used to actually send the response.

  1. // Send response with `stream.Readable` object as body
  2. await ctx.res.status(200).send(stream);
  3. // Send response with a string as body
  4. await ctx.res.status(200).type('text/plain').length(12).send('Hello World!');
  5. // Send response without a body
  6. await ctx.res.status(204).send();

Helpers

These generic utility helpers are currently available by default:

currentRoute

  1. const name = ctx.currentRoute();

Get the current route name.

inspect

  1. const serialized = ctx.inpsect({hello: 'world'});

Serialize data structure for debugging.

Exception Helpers

These exception helpers are currently available by default, they can be overloaded to change framework behavior:

exception

  1. await ctx.exception(new Error('Something went wrong!'));

Render an exception response in the appropriate format by delegating to more specific exception helpers for HTTP and WebSockets.

htmlException

  1. await ctx.htmlException(new Error('Something went wrong!'));

Render an HTML response and set the response status to 500.

htmlNotFound

  1. await ctx.htmlNotFound();

Render an HTML response and set the response status to 404.

httpException

  1. await ctx.httpException(new Error('Something went wrong!'));

Log the exception and render an HTTP exception response in the appropriate format and set the response status to 500 by delegating to more specific exception helpers for HTML, JSON and plain text rendering.

jsonException

  1. await ctx.jsonException(new Error('Something went wrong!'));

Render a JSON response and set the response status to 500.

jsonNotFound

  1. await ctx.jsonNotFound();

Render a JSON response and set the response status to 404.

notFound

  1. await ctx.notFound();

Render a not found response in the appropriate format by delegating to more specific exception helpers for HTTP and WebSockets.

txtException

  1. await ctx.txtException(new Error('Something went wrong!'));

Render a plain text response and set the response status to 500.

txtNotFound

  1. await ctx.txtNotFound();

Render a plain text response and set the response status to 404.

websocketException

  1. await ctx.websocketException(new Error('Something went wrong!'));

Log the exception and close the WebSocket connection with an 1011 error code.

View Helpers

These view helpers are currently available by default:

faviconTag

  1. %= ctx.faviconTag()
  2. %= ctx.faviconTag('/favicon.ico')

Generate <link> tag for a favison, defaults to the mojo.js favicon.

imageTag

  1. %= ctx.imageTag('/myapp/logo.png')

Generate <img> tag for image file.

include

  1. %= await ctx.include('_navbar')

Include a partial template.

linkTo

  1. %= ctx.linkTo('some_route', {class: 'foo'}, 'Link to some route');

Generate portable a tag with ctx.urlFor().

scriptTag

  1. %= ctx.scriptTag('/bootstrap/bootstrap.bundle.min.js')

Generate <script> tag for JavaScript file.

styleTag

  1. %= ctx.styleTag('/bootstrap/bootstrap.min.css')

Generate <link> tag for CSS file.

tag

  1. %= tag 'div'
  2. %= tag 'div', {class: 'wrapper'}
  3. %= tag 'div', {class: 'wrapper'}, 'Hello World!'

Generate HTML tag.

Hooks

These are all application hooks that are currently available, in the same order they usually run:

command:before

Runs after app.start() has been called, and before the application reaches a command or prints the command list.

  1. app.addAppHook('command:before', async (app, args) => {
  2. if (args[2] === 'legacy-server') args[2] = 'server';
  3. });

Useful for reconfiguring the application before running a command or to modify the behavior of a command. Passed the application object and command arguments.

server:start

Runs whenever the server has been started.

  1. app.addAppHook('server:start', async app => {
  2. app.config.deployment = 'server';
  3. });

Useful for reconfiguring the application or warming up caches. Passed the application object.

server:stop

Runs whenever the server has been stopped.

  1. app.addAppHook('server:stop', async app => {
  2. await app.models.foo.someCleanupCode();
  3. });

Useful for cleanup tasks. Passed the application object.

command:after

Runs after a command is finished or the command list has been printed.

  1. app.addAppHook('command:after', async (app, args) => {
  2. if (args[2] === 'server') await app.models.foo.someCleanupCode();
  3. });

Useful for cleanup tasks. Passed the application object and command argument.

Context Hooks

These are all context hooks that are currently available, in the same order they usually run:

dispatch:before

Runs after a new request has been received and before the static file server and router start their work.

  1. app.addContextHook('dispatch:before', async ctx => {
  2. const agent = ctx.req.get('user-agent') ?? '';
  3. if (/Internet Explorer/.test(agent) === true) ctx.stash.browser = 'ie';
  4. });

Useful for rewriting incoming requests and other preprocessing tasks. Passed the context object. Can return true to intercept the static file server and router.

static:before

Runs before the static file server sends a response.

  1. app.addContextHook('static:before', async (ctx, file) => {
  2. ctx.res.set('Cache-Control', 'max-age=3600, must-revalidate');
  3. });

Mostly used for post-processing static file responses. Passed the context object and the static file to be sent. Can return true to intercept sending the static file.

router:before

Runs after the static file server determined if a static file should be served and before the router starts its work.

  1. app.addContextHook('router:before', async ctx => {
  2. if (ctx.req.path !== '/test') return;
  3. await ctx.render({text: 'Intercepted!'});
  4. return true;
  5. });

Mostly used for custom dispatchers and collecting metrics. Passed the context object. Can return true to intercept the router.

send:before

Runs after ctx.res.send() has been called and before dynamically generated content is sent with the response.

  1. app.addContextHook('send:before', async (ctx, body) => {
  2. ctx.res.set('Cache-Control', 'max-age=3600, must-revalidate');
  3. });

Useful for post-processing dynamically generated content. Passed the context object and the dynamically generated content. Can return an arbitrary value to replace the dynamic content.

Support

If you have any questions the documentation might not yet answer, don’t hesitate to ask in the Forum, on Matrix, or IRC.