Documentation

Context

Context is the only parameter that middleware receives and contains all the information available at this point of the request:

name example type
.options { port: 3000, public: 'public' } Object
.data { firstName: 'Francisco '} Object
.params { id: 42 } Object
.query { search: '42' } Object
.session { user: { firstName: 'Francisco' } } Object
.headers { 'Content-Type': 'application/json' } Object
.cookie { acceptCookieLaw: true } Object
.files { profilepic: { ... } } Object
.ip '192.168.1.1' String
.url '/cats/?type=cute' String
.method 'GET' String
.path '/cats/' String
.secure true Boolean
.xhr false Boolean

It can appear at several points, but the most important one is as a middleware parameter:

// Load the server from the dependencies
const server = require('server');

// Display "Hello 世界" for any request
const middleware = ctx => {
  // ... (ctx is available here)
  return 'Hello 世界';
};

// Launch the server with a single middleware
server(middleware);

.options

An object containing all of the parsed options used by server.js. It combines environment variables and explicit options from server({ a: 'b' });:

const mid = ctx => {
  expect(ctx.options.port).toBe(3012);
};

/* test */
const res = await run({ port: 3012 }, mid, () => 200).get('/');
expect(res.status).toBe(200);

If we have a variable set in the .env or through some other environment variables, it'll use that instead as environment options take preference:

# .env
PORT=80
const mid = ctx => {
  expect(ctx.options.port).toBe(7693);
};

/* test */
const res = await run({ port: 7693 }, mid, () => 200).get('/');
expect(res.status).toBe(200);

.data

This is aliased as body as in other libraries. It is the data sent with the request. It can be part of a POST or PUT request, but it can also be set by others such as websockets:

const middle = ctx => {
  expect(ctx.data).toBe('Hello 世界');
};

// Test it (csrf set to false for testing purposes)
run(noCsrf, middle).post('/', { body: 'Hello 世界' });
run(middle).emit('message', 'Hello 世界');

To handle forms sent normally:

//- index.pug
form(method="POST" action="/contact")
  input(name="email")
  input(name="_csrf" value=csrf type="hidden")
  input(type="submit" value="Subscribe")

Then to parse the data from the back-end:

const server = require('server');
const { get, post } = server.router;
const { render, redirect } = server.reply;

server([
  get(ctx => render('index.pug')),
  post(ctx => {
    console.log(ctx.data);  // Logs the email
    return redirect('/');
  })
]);

.params

Parameters from the URL as specified in the route:

const mid = get('/:type/:id', ctx => {
  expect(ctx.params.type).toBe('dog');
  expect(ctx.params.id).toBe('42');
});

// Test it
run(mid).get('/dog/42');

They come from parsing the ctx.path with the package path-to-regexp. Go there to see more information about it.

const mid = del('/user/:id', ctx => {
  console.log('Delete user:', ctx.params.id);
});

.query

The parameters from the query when making a request. These come from the url fragment ?answer=42&...:

const mid = ctx => {
  expect(ctx.query.answer).toBe('42');
  expect(ctx.query.name).toBe('Francisco');
};

// Test it
run(mid).get('/question?answer=42&name=Francisco');

.session

After following the sessions in production tutorial, sessions should be ready to get rolling. This is an object that persist among the user refreshing the page and navigation:

// Count how many pages the visitor sees
const mid = ctx => {
  ctx.session.counter = (ctx.session.counter || 0) + 1;
  return ctx.session.counter;
};

// Test that it works
run(ctx).alive(async ctx => {
  await api.get('/');
  await api.get('/');
  const res = await api.get('/');
  expect(res.body).toBe('3');
});

.headers

Get the headers that were sent with the request:

const mid = ctx => {
  expect(ctx.headers.answer).toBe(42);
};

// Test it
run(mid).get('/', { headers: { answer: 42 } });

Object that holds the cookies sent by the client:

const mid = ctx => {
  console.log(ctx.cookies);
};

run(mid).get('/');

.files

Contains any and all of the files sent by a request. It would normally be sent through a form with an <input type="file"> field or through a FormData in front-end javascript:

<form method="POST" action="/profilepic" enctype="multipart/form-data">
  <input name="profilepic" type="input">
  <input type="hidden" name="_csrf" value="{{_csrf}}">
  <input type="submit" value="Send picture">
</form>

Note the csrf token and the enctype="multipart/form-data", both of them needed. Then to handle it with Node.js:

const mid = post('/profilepic', ctx => {
  // This comes from the "name" in the input field
  console.log(ctx.files.profilepic);
  return redirect('/profile');
});

.ip

The IP of the remote client. If it's behind a proxy that displays its proxy condition then the ips field will also be filled with the respective ips:

const mid = ctx => {
  console.log(ctx.ip, '|', ctx.ips);
};

run(mid).get('/');

.url

The full cuantified URL:

const mid = ctx => {
  expect(ctx.url).toBe('/hello?answer=42');
};

run(mid).get('/hello?answer=42');

.method

The request method, it can be GET, POST, PUT, DELETE:

const mid = ctx => {
  expect(ctx.method).toBe('GET');
};

// Test it
run(mid).get('/');

Or other methods:

const mid = ctx => {
  expect(ctx.method).toBe('POST');
};

// Test it
run(noCsrf, mid).post('/');

.path

Only the path part from the URL. It is the full URL except for the query:

const mid = ctx => {
  expect(ctx.path).toBe('/question');
};

// Test it
run(mid).get('/question?answer=42');

.secure

Returns true if the request is made through HTTPS. Take into account that if you are behind Cloudflare or similar it might be reported as false even though your clients see https:

const mid = ctx => {
  expect(ctx.secure).toBe(false);
};

// Test it
run(mid).get('/');

.xhr

A boolean set to true if the request was done through AJAX. Specifically, if X-Requested-With is “XMLHttpRequest”:

const mid = ctx => {
  expect(mid.xhr).toBe(false);
};

run(mid).get('/');

Keep reading

List of all the topics: