Code Monkey home page Code Monkey logo

server's Introduction

server.js for Node.js

Subscribe Downloads Status Dependencies status

Powerful server for Node.js that just works so you can focus on your awesome project:

// Include it and extract some methods for convenience
const server = require('server');
const { get, post } = server.router;

// Launch server with options and a couple of routes
server({ port: 8080 }, [
  get('/', ctx => 'Hello world'),
  post('/', ctx => {
    console.log(ctx.data);
    return 'ok';
  })
]);

Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better.

โ€• Edsger W. Dijkstra

Getting started

There's a whole tutorial on getting started for beginners but the quick version is to first install server as a dependency:

npm install server

Server requires Node.js 7.6.0 or newer. Node.js 8.x.y LTS is recommended.

Then you can create a file called index.js with this code:

// Include the server in your file
const server = require('server');
const { get, post } = server.router;

// Handle requests to the url "/" ( http://localhost:3000/ )
server([
  get('/', ctx => 'Hello world!')
]);

Execute this in the terminal to get the server started:

node .

And finally, open your browser on localhost:3000 and you should see 'Hello world!' on your browser.

Documentation

The library is documented here:

Full Documentation

Subscribe here to receive tutorials when released. Tutorials are good for learning while the documentation is good for reference/quick use once you know the basics.

You can also download the repository and try the examples by browsing to them and node . inside each of them in /examples.

Use cases

The package server is great for many situations. Let's see some of them:

Small to medium projects

Everything works out of the box, you get great support for most features and you can easily tap into Express' middleware ecosystem. What's not to love?

Some of the included features: body and file parsers, cookies, sessions, websockets, Redis, gzip, favicon, csrf, SSL, etc. They just work so you will save a headache or two and can focus on your actual project. Get a simple form going:

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

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

API design

From the flexibility and expressivity of the bundle, designing APIs is a breeze:

// books/router.js
const { get, post, put, del } = require('server/router');
const ctrl = require('./controller');

module.exports = [
  get('/book', ctrl.list),
  get('/book/:id', ctrl.item),
  post('/book', ctrl.create),
  put('/book/:id', ctrl.update),
  del('/book/:id', ctrl.delete)
];

Real time

Websockets were never this easy to use! With socket.io on the front-end, you can simply do this in the back-end to handle those events:

// chat/router.js
const { socket } = require('server/router');
const ctrl = require('./controller');

module.exports = [
  socket('connect', ctrl.join),
  socket('message', ctrl.message),
  socket('disconnect', ctrl.leave)
];

Author & support

This package was created by Francisco Presencia but hopefully developed and maintained by many others. See the the list of contributors here.

You can also sponsor the project, get your logo in here and some other perks with tons of โ™ฅ

server's People

Contributors

arpitgoyalgg avatar bstil22 avatar coppolaemilio avatar franciscop avatar justinpage avatar justman100 avatar kinostl avatar naycon avatar reed1 avatar teymour-aldridge avatar wm123450405 avatar yxwzaxns avatar zbarbuto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

server's Issues

Direction and 1.0.0

I was going to release the first beta 1.0.0 soon but I've decided to step back and rework and improve some areas. Since 1.0.0 is a big step and I want it to be the foundation of what is to come for the foreseeable future*, it should at least be forward-looking:

  • Future-proof middleware. I just like (not love) the way Express middleware works, and I think with Promises a lot more could be accomplished, while I find Koa's approach too complex for server's goals. The specifics will be detailed later, but the fingerprint would be basically a (context) => new Promise() or a (context) => { return; }. Note: should they still be called middleware?
  • Past-proof middleware: provide an easy way to include all the middleware that exists today.
  • Router: goes along with the previous point, making it compatible.
  • Plugins: nothing at all released publicly, however the router can be using them to iron-out the edges. Or perhaps just wait until 1.1 with websockets; the public interface should remain the same though as plugins won't be released in 1.0.

That is for finishing the alpha versions. Then the betas will focus on testing, documentation and website.

What do you think? Anything to be improved? Your opinion is wanted here.

*I think that for any project aimed to be big-ish, you should try to make it right on the 1.0.0. See python 3 mess or search for tutorials of Express where you'll be flooded with V3 tutorials instead of V4 (which changes a lot). For this I think a minimal, extensible and well tested and documented base is crucial. Of course I can still make a big f**k-up and break something big-time meaning a major version bump, but I'm trying to plan it properly so that doesn't happen even if it means a few more weeks of development.

Response/Reply

Find a decent way to handle the responses for the beta, even though it won't be properly documented in the alpha there should be a canonical example. There are three main options and then combinations of them:

A bit of magic

server(ctx => 'Hello ไธ–็•Œ');
server(ctx => render('index.pug'));
server(ctx => 404);
server(ctx => ['I', 'am', 'a', 'json']);
server(ctx => { I: 'am', also: 'a json' });

Pros:

  • Conceptually really nice
  • Follows somewhat Koa
  • Simple and clear
  • Final+error simple to check

Cons:

  • It still needs some contextual extra methods such as render(), sendFile(), etc.
  • Combining them is awkward; this should be combined with one of the options below.

Explicit external

The methods are part of a global and should be returned:

const { send, json, render, status } = server.reply;

server(ctx => send('Hello ไธ–็•Œ'));
server(ctx => render('index.pug'));
server(ctx => status(404));
server(ctx => json(['I', 'am', 'a', 'json']));
server(ctx => json({ I: 'am', also: 'a json' }));

Pros:

  • Consistent
  • Explicit
  • Return == send analogy still works

Cons:

  • Hidden dependency; the function has to be returned.
  • Final+error become unreliable if the hidden dependency is broken.

Explicit internal

Everything is within the context. Work around the verb "send" to avoid changing/using res (as res uses callbacks which mess up everything):

server(ctx => ctx.send('Hello ไธ–็•Œ'));
server(ctx => ctx.send.render('index.pug'));
server(ctx => ctx.send.status(404));
server(ctx => ctx.send.json(['I', 'am', 'a', 'json']));
server(ctx => ctx.send.json({ I: 'am', also: 'a json' }));

Pros:

  • Really explicit and clear what it does.

Cons:

  • Way too verbose, becomes annoying after a while.

Alternatives

Keep a bit of magic + explicit external or internal for the complex cases.

Return an object = response (instead of JSON). Example: { body: 'whatever', status: 200 }

Return an instance of a class = response (such as Reply). Example: status(200).send('whatever');

Body parser limit ignored

Description

I'm trying to increase the limit for a form post from 100kb to 1mb, per the example in the documentation. In my project I have a hidden field that contains approximately 300kb of data that needs to be posted to the server. The size of this hidden field varies from product to product and is generally in the 60-90kb range, but a few of them are quite larger and that's where I'm running into problems.

Expected outcome

When my post data exceeds 100kb I should be able to receive a confirmation and proceed in my program as normal. For posts that are below 100kb it is working correctly.

Actual outcome

Currently data posted that's less than 100kb is received and correctly. Anything over 100kb is rejected and the log returns: request entity too large

Live Demo

I can't precisely point to a live demo, but I can show the code on the server-side that seems most relevant:

server({ 
    parser: { body: { limit: '1mb' } },
    security: { csrf: false } 
    }, 
    router,   
    error(ctx => {
        console.log(ctx.error.message);
        return status(500).send(ctx.error.message)
    })
);

The parser: { body: { limit: '1mb' } } line is lifted verbatim from the documentation, but does not appear to be doing anything. I tried lowering it to 1kb as well to try and force errors in the previously working scenarios, but it made no difference โ€” those continued to work, suggesting to me this line was being completely ignored.

Curiously, the security: { csrf: false } line is being adhered to, as I needed to add that to make this particular application work.

I've also tried setting json: { limit: '1mb' } alongside limit within the body parameter, like this:

parser: {
    body: { limit: '1mb' },
    json: { limit: '1mb' }
}

Still made no difference.

The code on the front-end that is creating this request looks something like this (amended for brevity):

  fetch("{{ url }}", {
             method:'post',
             body: JSON.stringify({data:data})
         }).then((response) => {
         })

As I said before, it works fine for requests under 100kb. As the default upper limit value for body requests is 100kb it seems clear to me that's what I'm bumping into, but I'm a little stumped as to how to override it. I read the documentation for bodyParse.urlencoded but didn't see anything that looked like it would help. I event tried renaming body to urlencoded as a wild guess, but it didn't change anything.

System Information

OS: Debian Linux 8.7 (jessie) and macOS 10.13.2
Node Version: v8.4.0
Server Version: 1.0.9

Let me know if I can provide other details that might help, and thanks for any assistance!

http request method OPTIONS

I am trying to get an ajax post from a browser to work and its getting a 404 back when its sends the preflight OPTIONS check.
I don't see anything document on OPTIONS request types.
I have the post route defined. and it works if there is no preflight-check.

Cannot find module 'server/test/run'

Installed server.js and have loved it so far. Really easy to get setup and a lot of simple conveniences that you don't get from straight-up express. I'm now wanting to write a test suite, but it's looking like the test folder doesn't exist in the source files.

I installed via npm and am running at v1.0.1.
Listing the contents of the node_modules folder shows that test is missing.

root@2f31a5645e01:/usr/app/node_modules/server# ls
Contributing.md Gruntfile.js LICENSE README.md api.js appveyor.yml circle.yml coverage error package.json plugins reply roadmap.md router server.js src

can not log 'ctx'

i just run :
server({ port: 8080 }, [ get('/abc', ctx =>{console.log(ctx)}), ]);

but error:

'Cannot convert a Symbol value to a string',

nodejs : v8.2.1

TypeScript definitions

Hi!
I was just curious if TypeScript definitions have been discussed for this project or are already available.

Thanks!

Every POST request returns 403/Forbidden

Apologies if I am doing something wrong, but when I tried this simple server, POST requests always return 403/Forbidden.

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

server({ port: 8080 }, post('/foo', ctx => 'test'));

If I swap the type to get on line 3 the request works just fine.

$ curl -X POST -v 'http://localhost:8080/foo'  
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /foo HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 403 Forbidden
< X-Powered-By: Express
< Vary: X-HTTP-Method, X-HTTP-Method-Override, X-Method-Override
< Content-Type: text/plain; charset=utf-8
< Content-Length: 9
< ETag: W/"9-PatfYBLj4Um1qTm5zrukoLhNyPU"
< set-cookie: connect.sid=s%3AU3PJ-nFxLLL1OrHYcYZmhFZ-AHDa_czV.1yPFK3MucYbpwdGZr%2BgYqZVkWYDNuC6t6ByS69%2FtGlc; Path=/; HttpOnly
< Date: Mon, 06 Nov 2017 21:58:17 GMT
< Connection: keep-alive
< 
* Connection #0 to host localhost left intact
Forbidden% 

shouldn't this just work out of the box?

Found a problem with the env.js parsing

On my machine (win 10, latest node) I have an environment variable that looks like this:
ConEmuArgs2=-cmdlist {cmd} -cur_console:n

The is.json test fails on this.
I think you should never do a JSON.parse outside a try catch

A simple fix is this:

const type = str => {
  if (!str) return;
  if (typeof str !== 'string') return str;
  if (is.numeric(str)) return +str;
  if (is.boolean(str)) return /true/i.test(str);
  if (is.json(str)) {
    try {
      return JSON.parse(str);
    }
    catch(e) {

    }
  }
  return str;
};

Provide a changelog

Description

A changelog would be very useful when deciding whether to upgrade this package. Even something automated like changelog-maker (example) would be good enough. I find it forces me to write better git messages anyway.

Expected outcome

A list of changes or short description for every version, in the form of a CHANGELOG.md file or notes in the releases page.
Example: Breaking changes in error handling. See xx for migration instructions.

Actual outcome

No changelog provided

Replying from middleware

I was implementing a validation middleware that replies with status code 400 and an error message when request body is invalid, but quickly saw that replying from middleware does not prevent the rest of the pipeline from executing.

Which results in an error:

WARNING There is an unhandled error:
ERROR Error: Can't set headers after they are sent.
    at validateHeader (_http_outgoing.js:494:11)
    at ServerResponse.setHeader (_http_outgoing.js:501:3)
    at ServerResponse.header (./node_modules/express/lib/response.js:767:10)
    at ServerResponse.send (./node_modules/express/lib/response.js:170:12)
    at stack.push.ctx (./node_modules/server/reply/reply.js:134:13)
    at Reply.exec (./node_modules/server/reply/reply.js:157:11)
    at processReturn (./node_modules/server/src/join/index.js:16:31)
    at ./node_modules/server/src/join/index.js:41:17
    at <anonymous>

A minimal code displaying this behavior:

server(
  ctx => 'foo', // this middleware sends 'foo'
  ctx => 'bar' // but this one is executed, too, trying to send 'bar'
);

Is this by design?

I also tried throwing an error from middleware after setting the reply, which does bypass the rest of the middleware, but the thrown error then gets reported as unhandled in the console.

Sessions set-up for production

Sessions work out of the box for developing, but they need a bit of extra work to be ready for production.

Secret

The first thing to change is adding a session secret as an environment variable in .env for your machine:

SECRET=your-random-string-here

This will be used to secure the cookies as well as for other plugins that need a secret. Make it unique, long and random. Then don't forget to add a different one for the production server and other stages in your deploy pipeline if any.

Storage

By default sessions work in-memory with server so they are not ready for production:

// Simple visit counter for the main page
const server = require('server');
const counter = server.router.get('/', ctx => {
  const session = ctx.req.session;
  session.views = (session.views || 0) + 1;
  ctx.res.send(session.views);
});
server(counter);

This works great for testing; for quick demos and for short sessions, but all session data will die when the server is restarted since they are stored in the RAM.

To make them persistent we recommend using a compatible session storage. We bundle Redis for Node.js by default, so you just have to install it (*nix systems have it easily available) and edit your .env to include REDIS_URL:

SECRET=your-random-string-here
REDIS_URL=redis://:password@hostname:port/db_number

Note: for Heroku this variable is created automatically when adding the appropriate add-on. For other hosting companies please consult their documentation.

Note2: as in the alpha Redis is not yet ready

Otherwise add your preferred storage to the session through the options:

const server = require('server');
// Your own file for the config:
const storage = require('./session-storage.js');
server({ session: { storage: storage } }, [
  // Routes here
]);

Alternatives

Why not just use cookie-session? Here is an explanation of the alternative, but it boils down to:

  • They are more insecure, since all the session data (including sensitive data) is passed forward and backward from the browser to the server in each request.
  • If the session data is large then that means adding an unnecessary load to both the server and the browser.

Wildcard Method Requests

Description

The current method implementations don't support catch-all endpoints.

Requested Syntax

const server = require('server');
const { status } = server.reply;
const { get } = server.router;

server(
  // This should get invoked for every request
  // An alternative syntax would be to support Regular Expressions.
  get('*', (ctx) => {
    return status(200);
  })
);

Current Workaround

const server = require('server');
const { status } = server.reply;

server(
  (ctx) => {
    if (ctx.method !== 'GET') return;

    // Do super cool api stuff
    return status(200);
  })
);

Relevant Code

The following code doesn't support wildcards / catch-all endpoints.

server/router/generic.js

Lines 21 to 23 in dd1ad51

// Only do this if the correct path
ctx.req.params = match(ctx.req.path);
if (!ctx.req.params) return;

Logging

Include some logging plugin possibly through Winston or log:

server({ log: 'error' }, ctx => {
  ctx.log.error('This is displayed');
  ctx.log.info('This is NOT displayed');
  ctx.log('This is NOT displayed');  // Same as above
});

Tip: if you want to help, probably a good moment to attach it is in init or before for the plugin.

about express use method need a path arguments sample

the express sample

var PouchDB = require('pouchdb');
var express = require('express');
var app = express();

app.use('/db', require('express-pouchdb')(PouchDB));

app.listen(3000);

I try in my router.js

import server from 'server'
import { sub } from 'server/router'
import PouchDB from 'pouchdb'
const { modern } = server.utils

export default [
    sub('db', modern(
        require('express-pouchdb')
        (PouchDB)
    )),
    ctx => 'Hello world'
]
โžœ http :10087/db
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 11
Content-Type: text/html; charset=utf-8
Date: Fri, 12 Jan 2018 06:39:46 GMT
ETag: W/"b-e1AsOh9IyGCa4hLN+2Od7jlnP14"
Strict-Transport-Security: max-age=15552000; includeSubDomains
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
set-cookie: connect.sid=s%3A1_Z_VAiCK6pH8KRMsFEW6OZ3lyF5QK2o.R9vH2IoG0gp218Z5OUyGkWLeLox4S0HecoGcxkQ0M8I; Path=/; HttpOnly

Hello world

how to write the sample code if i use server.js ?

CORS

What is the preferred way to enable CORS using this framework?

sub fails on multi-level subdomains

Description

The currently implementation of sub joins the subdomains incorrectly
resulting in multi-level subdomains not working.

Example

const server = require('server');
const { status } = server.reply;
const { sub } = server.router;

server(
  sub('api.local', get('/', (ctx) => {
    return status(200).send('ok');
  })
);

Relevant Code

The following code should return api.local, however instead it returns local.api.

const full = ctx.req.subdomains.join('.');

How to emit an event from outside the server

Hi! I'm using Server for a small app, that should emit when a file changes. I'm using fs.watch for this, but I don't know how to emit the event. The examples that I've seen have only have response of events, but the server is never the source of one event.

Nested Routes

Hello, do you have plans to include "use" method on router?
This way we could create nested routes, example:

//products.js
const {get, post, del} = require('server/router')
module.exports = [
get('/',ctx=>{return "products"}),
get('/:id',ctx=>{return "product"}),
post('/',ctx=>{return "product"}),
del('/:id',ctx=>{return "product"}),
]

//index.js
const server = require('server')
const { use } = server.router
server([
use('/products',require('./products'))
])

This would do the same effect as the express router, creating sub routes hierarchically.
Thanks for the project, I really enjoyed it.

Performance

At this stage performance is something not to worry about; however it is a future concern so this thread should work as a general guideline and plan. Server's big advantage has nothing to do with performance so as long as it's competitive performance-wise it's okay.

  1. Usability >> performance.

  2. Optimize only the slowest paths. No micro-optimizations that don't change the bottom line.

  3. A bunch of it will probably come for free as V8 gets improved, notably on Promises-related code. See this.

The objective for 1.x is that server performs decently when compared with raw Express 4.0 (in which it's based). Right now it can perform 37.4% of the total requests that express can for a hello world example.

Create subdomain router

Add a new subdomain router:

const server = require('server');
const { sub } = server.router;

server([
  sub('user', userrouter),   // A specific router
  sub(/^app\_/, approuter),   // A regex router
  ['es', 'en', 'jp'].map(lang => sub(lang, langrouter)),    // Map to several routers
  sub(/^(en|es|jp)$/, langrouter),    // Same as the above but expressed differently

  homerouter,     // Anything left, including www
]);

Things to take into account:

  • Empty string router doesn't make sense => it'd be the same as omitting it.
  • How differently should `` subdomain vs www. subdomain be treated? It should at least be documented.

Access ctx.session from websocket?

Is there any way to access the ctx.session object from a websocket handler callback?

const connect = ctx => {
  if (ctx.session){  // <--------------- This does not seem to exist in socket related callbacks
    console.log('Yay Session!',ctx.session);
  }else{
    console.log('Booo no session');
  }
  ctx.io.emit('session', ctx.session);
};

server(serverOpts, [
  cors,
  get('/', ctx =>render('index.html'),
  get(ctx => status(404)),
  socket('connect', connect),
  error(ctx => status(500).send(ctx.error.message))
]).then(ctx => {
  console.log(`Server launched on http://localhost:${ctx.options.port}/`);
});

Options for plugins

A small exploration about how the options for plugins might look like for a developer of a plugin. Now I've written quite a few and have a better idea of the possibilities and limitations of them. I will be using log as an example. For the simple way with defaults:

plugin.options = {
  level: { default: 'info' },
  reporter: { default: process.stdout },
  __root: 'level'
};

There are no mandatory fields, however setting a default is strongly recommended. The last root bit would make both of these usages equivalent when using the plugin log:

server({
  log: 'info'
});

server({
  log: { level: 'info' }
});

The .env is also quite straightforward in this situation thanks to the __root option. Both of these are equivalent as well:

# Single option
LOG=info

# Multiple options (note: cannot do a function here though!)
LOG_LEVEL=info

Advanced options

Now you might want all bells and whistles going on. For instance, let's define the type of the parameter. This will add a small validate function internally:

log.options = {
  level: {
    default: 'info',
    type: String
  }
};

List of advanced options with their defaults inspired by Mongoose. First, options to use the correct variable:

  • default: the default to set in case it is not set. Leave it unset and it won't have a value if it is not explicitly set.
  • env: NAME || true: defines the name for that variable in the environment variables. If set to false, it will not accept it through the environment.
  • arg: NAME || true: defines the key of the value for options in server(OPTIONS). If set to false it will not accept it from the options object (some arguments that MUST only be accepted through the environment).
  • inherit: NAME || false: passing a name, it inherits the value from a global variable by this order of preference: [validate:] specific environment > global environment > specific argument > global argument > specific default > global default.
  • find: FN || false: a function that receives the options passed on the main function, then all of the environment and finally the default. It returns the wanted value.
  • extend: true || false: extend the default value for the unwritten properties with the default props if they are not set. Value passed: { main: 'a', second: 'b' }, { default: { second: 'c', third: 'd' }, extend: true } => { main: 'a', second: 'b', third: 'd' }.

Then you can perform several validations:

  • required: FN || false: make sure the option is set before proceeding. This doesn't make sense when default is set.
  • type: false: define the type of the variable. A type or an array of types. Will only check the primitive types Boolean, Number, String, Array, Object. Can be also an array of types.
  • enum: ['a', 'b']: the variable should be within the list.
  • validate: FN || false: defines a function to validate the value. It will accept first the current value, then all the currently set values and must return true for a valid value or false otherwise. Note: this is done AFTER any of the other specific checks like type check or the enumerate check.

Note: all of the functions described here can be either synchronous or asynchronous by returning a Promise (or using the async keyword).

TODO: check the engine for mongoose to see if it makes sense to extract the validation part.

How to authenticate

I am curious on how to authenticate using server. I don't see how to configure my session store to be mongoDB using connect-mongo. A tutorial on how to this will be really helpful.

Clarify logging functionality

Hi,

I've been testing various features of server today, and logging functionality puzzles me. If I just set log level via either .env file or via options on startup, ctx.log.info('foo') doesn't write anything to STDOUT. I can produce STDOUT output by using the stream directly (ctx.log.stream.write('foo')). Normal console.log works as expected.

Is there a log file somewhere? How can I log into STDOUT / STDERR using ctx.log?

Thanks in advance!

Matias


server version: 1.0.1
node version: 8.9.0
Platform: macOS Sierra
Terminal: iTerm 2 build 3.1.4

using csuf middle in server.js

I trying to use csurf as middleware in server.js , as mentioned in docs the code i tried.

const server = require('server');
// requirements
const { get, post} =require('server/router');

const {error} = server.router;

const {modern}=server.utils;
var sassMiddleware = require('node-sass-middleware')
let connect = require('connect');
let path=require("path");
let connect_app= connect();
let csrf = require('csurf');
const routes= require('./routes');

const mid_csrf=server.utils.modern(csrf);
const {status}= server.reply;

server(mid_csrf,{
public:'public',
views:'pages',
port:80,
parser: {
    cookie: {
      maxAge: 900000,
    }
  },
 // security: { csrf:csrf()  } ,
},routes);

the Error am getting...

(node:26772) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Only boolean, string, array or function can be middleware
(node:26772) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

can I mount an express app on specific path?

like offical documention on express http://expressjs.com/en/api.html does

var express = require('express');

var app = express(); // the main app
var admin = express(); // 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

If yes, How?
If no, do you have plan to support this?

Returning a status code with an empty body

I want a specific route to return a 200 with an empty body. I've tried these two methods:

get('/health', ctx => status(200))
get('/health', ctx => 200)

They both work in that they return a 200 response but the body isn't empty (a default body of "OK" is returned) and they still trigger the "Your middleware did not return anything to the user." error that gets printed to the console.

How can I return an empty body without triggering the error message that gets printed to the console?

Best way to emit socket.io events not triggered by a request

I've read the real-time tutorial and I've build my own with absolute no problems at all.

But I'm unsure about how to emit events through socket.io that isn't triggered by a request, unlike in the chat tutorial.

Currently I'm trying to figure out how to emit tweets as they come in via the twit package.

What is the best way to get hold of ctx.io so that I can call ctx.io.emit('tweet', tweet)?
Or how do you independently of a request emit an event through socket.io from the server?

My current "solution" is this:

// [...]
var streamActive = false

const startStream = ctx => {
  if (streamActive) return
  streamActive = true

  const stream = T.stream('statuses/filter', { track: 'apple' })
  stream.on('tweet', tweet => {
    ctx.io.emit('tweet', tweet.text)
  })

}

server([
  socket('connect', startStream)
])

Redis

Add and test Redis for session management.

Doesn't work on Windows!

For some reason the files served with the static middleware don't load on Windows.

  1. Find the bug (check absolute paths and C:// issues).
  2. Fix the bug.
  3. Establish a testing methodology that involves windows as well.

Error in server.js

when I test out your framework I noticed the following error with just the following in index.js:

// Import the library
const server = require('server');

// Answers to any request
server(ctx => 'Hello world');

I run "node ." on Ubuntu 16.04 and get the following error:

const Server = async (...middle) => {
                      ^^^

Added documentation search!

Hi everyone! I have added a new feature that I am sure many were missing: search the Docs.

It will search through the main title first and then the body of each docs. The body could be improved a bit more, but I am really happy with how it works right now. Give it a try! It adds 100kb to the page load for full documentation search: pretty sweet IMHO.

Example:

notifications

Authentication

Hello,

Are there any approaches for authentication implemented yet? For example, something like passport-jwt would be nice to have.

Thanks.

Unexpected token

been following https://serverjs.io/tutorials/getting-started/

when i start the server i get

`/mnt/Documents/node/chat/node_modules/server/server.js:21
const Server = async (...middle) => {
^

SyntaxError: Unexpected token (
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object. (/mnt/Documents/node/chat/index.js:2:16)`

ubuntu 17.10 node 6.11.4

views option not worked

I followed the document from the website to set views option, but it not worked.
The server looked up default views path and ignore my option, is anything wrong?
This my code:

// app.js
server({
  'port': 3000,
  'public': 'public',
  'engine': 'ejs',
  'views': './src/views'
}, [
  get('/', ctx => render('index'))
])

logout:

ERROR { Error: Failed to lookup view "index" in views directory "****/Demo/views"
    at Function.render (****/Demo/node_modules/express/lib/application.js:580:17)
    at ServerResponse.render (****/Demo/node_modules/express/lib/response.js:1008:7)
    at Promise (****/Demo/node_modules/server/reply/reply.js:124:13)
    at Promise (<anonymous>)
    at stack.push.ctx (****/Demo/node_modules/server/reply/reply.js:121:26)
    at Reply.exec (****/Demo/node_modules/server/reply/reply.js:157:11)
    at processReturn (****/Demo/node_modules/server/src/join/index.js:11:22)
    at ****/Demo/node_modules/server/src/join/index.js:41:17
    at <anonymous>
  view: 
   View {
     defaultEngine: 'ejs',
     ext: '.ejs',
     name: 'index',
     root: '****/Demo/views',
     engine: [Function],
     path: undefined } }

No routing if route handler returns undefined

Hey,

coworker and I tested this thing out on a small api server and it's working well. Nice job!
During testing, my coworker (who is not super familiar with ES6 syntax) attempted to do this:

server([
  get('/', ctx => { 'Hello world!' })
]);

which resulted in the server not routing to '/' because the handler method returned undefined.
We spotted the error fairly quickly and it did work in the end, but maybe server.js could warn if a handler is defined, but does not return anything?

Issues using body-parser

I may or may not be using your express middleware function correctly, but there it is.

import * as server from 'server';
import * as bodyParser from 'body-parser';

const { modern } = server.utils;

server(
    modern(bodyParser.json({limit: '5mb'})),
    modern(bodyParser.urlencoded({limit: '5mb', extended: true})),
    ...
)

However I still receive this error:

ERROR { PayloadTooLargeError: request entity too large,
  message: 'request entity too large',
  expected: 170381,
  length: 170381,
  limit: 102400,
  type: 'entity.too.large' }```

unable to send response for post request

Am using using vue-resource to send the post request to server.here's the code I used to send the post request.

   {
this.$http.post('/register',{ 
      username:this.username,
      firstname:this.firstname,
     lastname:this.lastname,
     phoneno:this.phoneno  }
     ).then(response=>{
     console.log('responded...'+response.ok);
     }).then(error=>{
    console.log('Error...'+error);
     });
}

And the data is received successfully ,but am I unable to send the response here's how i did this.

server({
public:'public',
views:'pages',
port:80,
},[
    post('/register',ctx=>
    {
        if(!ctx.data)
        throw new Error('no data');
    console.log( ctx.data);         (**)
return status(200).send('success'); (***)
    }
    ),
   
]);

As for the proof that I have received the data sucessfully ,( ) as logged in the above code.I got this output in my console.(*) but the response is not sent which I don't know why.As I refered from this

{ 
username: 'guru001',
  firstname: 'guru',
  lastname: 'prasaad',
  phoneno: '09898454543'
 }

Am getting 403 (Forbidden) in the browser console.


[Sun Dec 24 2017 14:11:22 GMT+0500 (Pakistan Standard Time)] ERROR { ForbiddenError: invalid csrf token
    at csrf (D:\workspace\@node\node_modules\csurf\index.js:112:19)
    at Promise (D:\workspace\@node\node_modules\server\src\modern\index.js:20:5)
    at new Promise (<anonymous>)
    at ctx (D:\workspace\@node\node_modules\server\src\modern\index.js:13:17)
    at module.exports.before.ctx (D:\workspace\@node\node_modules\server\plugins\security\index.js:52:49)
    at D:\workspace\@node\node_modules\server\src\join\index.js:42:27
    at <anonymous> code: 'EBADCSRFTOKEN' }

        mountpath: '/',

server/reply/cookie =>`TypeError: option expires is invalid`

In returning a cookie response I'm attempting to set the expiration date of the cookie. Following the express docs, options object has an expires key.

    const portalAccessToken = authService.generateToken(user);
    return status(200).cookie('accessToken', portalAccessToken, {
        httpOnly: true,
        expires: new Date()
    });

Plugins

Here comes the big one, plugins. First, a wish list. I'd like for a plugin to have this API available:

module.exports = {

  // This is working right now (highly unstable)
  // String
  name: 'whatever',

  // Object
  config: {}

  // String, Function, Array
  init: () => {},

  // String, Function, Array
  before: () => {},

  // String, Function, Array
  after: () => {},

  // String, Function, Array
  final: () => {},


  // Not working yet but desirable:
  // Function (named 'whatever' like the plugin), Object with { name: fn } pairs
  router: () => {},

  // Function (named 'whatever' like the plugin), Object with { name: fn } pairs
  reply: () => {}
};

Now, I am not 100% it makes sense to open router and reply right now. I think there are some situations where it'd be really useful, like sending a PDF back for example:

// Send a pdf from server
server(ctx => pdf('./readme.pdf'));

But there are many options here and doing it one way might limit some other options. So my first question:

What new router and reply would you want for server.js?

Simple example: database

Why are these useful? Isn't is enough with middleware?

Well no, for instance for database connections it is really useful. Let's say we develop a @server/mongoose and install it with npm install @server/mongoose. Afterwards, just passing the options and we have access to the db through the context:

server({ mongoose: 'url for mongodb (or in .env)' },
  get('/', ctx => 'Hello world'),
  get('/sales', hasUser, ctx => ctx.db.sales.find({ user: ctx.user.id }))
});

This can be applied to anything that has to be connected or configured once initially and later on can be used in the middleware.

Advanced example: sass

It also opens up to new possibilities, let's see a small example with sass. Let's say that we want to make a sass plugin that rebuilds the whole thing on each request for dev and only once on production:

module.exports = {
  name: 'sass',
  options: {
    __root: 'source',
    source: {
      default: 'style/style.scss',
      type: String,
      file: true
    },
    destination: {
      default: 'public/style.css',
      type: String
    }
  },
  init: async ctx => {

    // If `ctx.options.sass.destination` exists and was not generated by @server/sass
    //   throw an early error and ask for the file to be (re)moved

    // Remove the `ctx.options.sass.destination` file

    if (ctx.options.env === 'production') {
      // Compile everything and store it in `ctx.options.sass.destination`
    }
  },

  // This will only get called in dev+test, since `style.css` will be found in production
  before: get('/style.css', async ctx => {
    // Reply with the whole `ctx.options.sass.source` compiled dynamically
  })
};

To use it is really simple. First npm install @server/sass, then if your options are the default ones you won't even need to write any specific javascript for it. Let's say though that we want to change our source file, which is the root option:

server({ sass: './front/style.sass' }, ctx => render('index'));

That's it, with the flexibility of plugins you wouldn't need any more code to have a sass plugin.

This is why I think plugins can be really awesome if they are built and documented properly. What plugin would you like to see?

React plugin

EDIT: rethought the plugin system. Install an official plugin (those with the @server/ prefix):

npm install @server/react

Then just use it. Any installed official package will be automatically required by server, so no need to write specific code for importing it. Then just use it however its documentation describes:

// index.js
const server = require('server');
const { render } = server.reply;

server(ctx => render('app.jsx', { user: 'Francisco' }));

Then we would be able to have in our app.jsx, reading explicit and implicit variables:

// app.jsx
import react from 'react';

export default (props) => (
  <div>
    <h1>Welcome {props.user}</h1>
    <p>server.js is so cool! You are reading {props.path}</p>
  </div>
);

Inspired by Next.js and checked feasibility with Paypal's react-engine and React.js' express-react-views.

CORS

Hi, i try to call my server with an another port, but i'm blocked by cross-origin security. And in documentation, i don't find a reference to cors.
I know resolve this issue on express:
https://enable-cors.org/server_expressjs.html

How can i resolve it on serverjs ?

thx.

internal error when adding new routes

I Followed the chat tutorial and to learn more by expanding it, The issue I am currently having is adding new gets in this case, a rules page

server({port: 8080},[ get('/', ctx => render('index.html')), get('/rules', ctx => render('rules.html')), socket('connect', updateCounter), socket('disconnect', updateCounter), socket('message', sendMessage) ]);

in the browser gives Internal Server Error, no errors in the console

Question on using different templating framework like nunjucks

Thanks for this awesome framework. It shall be as big as Django in the node family!

Quick question, if I do this:

server([
  get('/', ctx => nunjucks.render('views/index.hbs', {chai: "chai"})),
  post('/', ctx => json(ctx.data)),
  get(ctx => status(404))
]);

"nunjucks.render()" and exclude your "render()" function, am I losing any kind of functionality? It works ok, but is this the proper way?

Thanks!!

Server and Nunkunks

Hi Guys, someone can tell me how use server.js with nunjucks as template render?
I can use server.js with nunjucks using this:

const server = require('server')
const { get } = server.router

const njk = require('nunjucks')
njk.configure('views')

server(
  get('/', ctx => njk.render('template.njk')),
)

or:

const server = require('server')
const njk = require('nunjucks')

const { get, post } = server.router
const { render } = server.reply

server(
  get('/', ctx => render('template.njk')),
).then((v) => njk.configure('views', {express: v.app}))

What is the best flow?

Thanks for your time and for this great lib.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.