Code Monkey home page Code Monkey logo

micro-router's Introduction

๐Ÿš‰ Micro Router - A tiny and functional router for ZEIT's micro

GitHub release Build Status Coveralls Codacy Badge

๐Ÿ‘Œ ย  Features

  • Tiny. Just couple lines of code.
  • Functional. Write your http methods using functions.
  • Async. Design to use with async/await

๐Ÿ’ป ย  Usage

Install as project dependency:

$ yarn add microrouter

Then you can define your routes inside your microservice:

const { send } = require('micro')
const { router, get } = require('microrouter')

const hello = (req, res) => send(res, 200, `Hello ${req.params.who}`)

const notfound = (req, res) => send(res, 404, 'Not found route')

module.exports = router(get('/hello/:who', hello), get('/*', notfound))

async/await

You can use your handler as an async function:

const { send } = require('micro')
const { router, get } = require('microrouter')

const hello = async (req, res) =>
  send(res, 200, await Promise.resolve(`Hello ${req.params.who}`))

module.exports = router(get('/hello/:who', hello))

route methods

Each route is a single basic http method that you import from microrouter and has the same arguments:

  • get(path = String, handler = Function)
  • post(path = String, handler = Function)
  • put(path = String, handler = Function)
  • patch(path = String, handler = Function)
  • del(path = String, handler = Function)
  • head(path = String, handler = Function)
  • options(path = String, handler = Function)

path

A simple url pattern that you can define your path. In this path, you can set your parameters using a : notation. The req parameter from handler will return these parameters as an object.

For more information about how you can define your path, see url-pattern that's the package that we're using to match paths.

handler

The handler method is a simple function that will make some action base on your path. The format of this function is (req, res) => {}

req.params

As you can see below, the req parameter has a property called params that represents the parameters defined in your path:

const { router, get } = require('microrouter')
const request = require('some-request-lib')

// service.js
module.exports = router(
  get('/hello/:who', (req, res) => req.params)
)

// test.js
const response = await request('/hello/World')

console.log(response)  // { who: 'World' }
req.query

The req parameter also has a query property that represents the queries defined in your requision url:

const { router, get } = require('microrouter')
const request = require('some-request-lib')

// service.js
module.exports = router(
  get('/user', (req, res) => req.query)
)

// test.js
const response = await request('/user?id=1')

console.log(response)  // { id: 1 }

Parsing Body

By default, router doesn't parse anything from your requisition, it's just match your paths and execute a specific handler. So, if you want to parse your body requisition you can do something like that:

const { router, post } = require('microrouter')
const { json, send } = require('micro')
const request = require('some-request-lib')

// service.js
const user = async (req, res) => {
  const body = await json(req)
  send(res, 200, body)
}

module.exports = router(
  post('/user', user)
)

// test.js
const body = { id: 1 }
const response = await request.post('/user', { body })

UrlPattern instance as path

The package url-pattern has a lot of options inside it to match url. If you have a different need for some of your paths, like a make pattern from a regexp, you can pass an instance of UrlPattern as the path parameter:

const UrlPattern = require('url-pattern')
const { router, get } = require('microrouter')

const routes = router(
  get(
    new UrlPattern(/^\api/),
    () => 'This will match all routes that start with "api"'
  )
)

Namespaced Routes

If you want to create nested routes, you can define a namespace for your routes using the withNamespace high order function:

const { withNamespace, router, get } = require('microrouter')
const { json, send } = require('micro')

const oldApi = withNamespace('/api/v1')
const newApi = withNamespace('/api/v2')

const routes = router(
  oldApi(get('/', () => 'My legacy api route')),
  newApi(get('/', () => 'My new api route'))
)

PS: The nested routes doesn't work if you pass a UrlPattern instance as path argument!

๐Ÿ•บ ย  Contribute

  1. Fork this repository to your own GitHub account and then clone it to your local device
  2. Install dependencies using Yarn: yarn install
  3. Make the necessary changes and ensure that the tests are passing using yarn test
  4. Send a pull request ๐Ÿ™Œ

micro-router's People

Contributors

brendancwood avatar brunolemos avatar danielruf avatar fdaciuk avatar flamefork avatar johanbrook avatar leo avatar megamaddu avatar mingz-work avatar minigod avatar pedronauck avatar shidhincr avatar timneutkens avatar timreynolds avatar ulken 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

micro-router's Issues

Support passing options to `url-pattern`

I have a use case where I want to match everything after a certain path, but micro-router only lets me match certain characters, so instead I have to use a regex like this:

get(/^\/foo\/([^]*)$/, req => {
	const [match] = req.params;
	console.log(match);
	//=> whatever/requested.jpg
});

I would like to be able to name my capture groups like this and also have the ability to define the options sent to url-pattern.

Replying with stream fails when using micro-router

For some reason when replying with stream while using micro-router, request fails to go through.
E.g. the following code will result in Cannot GET /:

const {Readable} = require('stream');
const micro = require('micro');
const _ = require('highland');
const { router, get } = require('microrouter')

const streamTest = (req, res) => {
  const stream = _();
  micro.send(res, 200, new Readable().wrap(stream));
  stream.write("a");
  stream.write("b");
  stream.end();
};

const server = micro(router(get('/', streamTest)));

server.listen(3000);

Changing it to use only micro itself makes it work:

const {Readable} = require('stream');
const micro = require('micro');
const _ = require('highland');

const streamTest = (req, res) => {
  const stream = _();
  micro.send(res, 200, new Readable().wrap(stream));
  stream.write("a");
  stream.write("b");
  stream.end();
};
const server = micro(streamTest);

server.listen(3000);

Any idea why that might happen? Am I doing something wrong?
Is this a bug? (Haven't noticed any streams in tests)

The test of sending of stream, is badly constructed

Hi, this test line does not make sense, really what you are doing, is to test the behavior of microjs against a stream of data, not microrouter, the correct test would be like this:

test('route which sends a Stream', async (t) => {
    const readable = new Readable()
    const { methods, handler } = createRouter()
    const { post } = methods

    readable._read = () => {}
    readable.push('foo')
    readable.push(null)

    post('/stream', (req, res) => {
        let data = ''
        req.on('data', (chunk) => data += chunk)
        req.on('end', () => send(res, 200, data))
    })

    const url = await server(handler)
    // using node-fetch support ArrayBuffer | ArrayBufferView | NodeJS.ReadableStream, etc
    const response = await fetch(`${url}/stream`, {
        method: 'POST'
        , body: readable
    })

    t.is(await response.text(), 'foo')
  })` 

I realized this because I'm making my own routing system, based on yours.
Regards.

404 always

I'm using this library with the latest version of micro (8).

// index.js
const { router, get } = require('microrouter');
const { getUser } = require('./handlers');


module.exports = router(
  get('/users/:id', getUser),
);
// handlers/index.js
const { send } = require('micro');

const getUser = (req, res) => send(res, 200, {
  id: req.params.id,
  first_name: 'Foo',
  last_name: 'Bar',
});

module.exports = {
  getUser,
};

And i run the project with micro-dev
on the console I see the object I create in the function, but the browser always receive 404. How is it possible?

Is this project dead?

Thanks for the great work.
I see that the last commit made in this repo is more than a year ago.
So I want to know if this project is still maintained because I want to submit a PR.

Multiple routers

How to configure & use multiple routers?

const router1 = router(
  get('/hello/:who', hello)
);
const router2 = router(
  get('/hello1/:testing', testingFn)
)

Dist is out of date

Hey! This looks like a neat router library, but unfortunately dist/index.js was last updated on May 11th, despite the more recent feature releases.

I'd recommend doing the compilation in a prerelease script, rather than committing it.

Alternatively you could consider requiring Node.js 8, in which case no precompilation is required.

ES6 named import for `delete` under Standard

It appears impossible to require the delete method without upsetting Standard JS, either due to conflicting with a reserved word (const { delete } = require('microrouter')) or not defining the function (require('microrouter')). Any suggestions?

Trouble with matching routes

I've come across an issue where my requests are not getting handled as I would expect. In my code I've got something like this:

module.exports = router(
  get('/users/:id', usersHandler),
  get('/users/:id/resource', resourceHandler)
);

When I make a GET request to /users/12345/resource, the usersHandler is being called, not the resourceHandler. I believe this is happening because the request matches both paths but how can ensure that my request is handled by the correct handler?

Default 404 handler

Having connections hanging because of 404 is kind of silly.
I think it would make sense to have a default 404 handler.

Using a final /* route for now.

Crashing micro when throwing errors on Promises

The router causes micro to crash when errors are thrown on Promises.

module.exports = router(
  get('/badError', (req, res) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        throw new Error('deferred error')
      }, 1000)
    })
  }),
  get('goodError', (req, res) => {
    throw new Error('immediate error')
  })
)

Is this intended behavior?

One of the advantages of using micro is it's error handling. This issue breaks that.

Recent commit breaks router composition, backward compatibility

#19 (merged on July 18) makes it impossible to combine routers, e.g.

auth.js:

export default router(
    post( "/auth/request-token", requestToken ),
    ...
);

root.js:

import auth from "./auth";

export default router(
    get( "/hello", helo ),
    auth
);

This used to work, we had to fork and revert to keep our app working.

How to use router when use https.createSever

this is my code

const https = require('https')
const { run, send } = require('micro')

const cert = require('openssl-self-signed-certificate')

const PORT = process.env.PORT || 3443

const options = {
  key: cert.key,
  cert: cert.cert,
  passphrase: cert.passphrase
}

const microHttps = fn => https.createServer(options, (req, res) => run(req, res, fn))

const server = microHttps(async (req, res) => {
  send(res, 200, { encrypted: req.client.encrypted })
})

server.listen(PORT)
console.log(`Listening on https://localhost:${PORT}`)

from this example : https://github.com/zeit/micro/tree/master/examples/with-https

how to use router?

change listen port

Hi, more than an issue, it is a question
I was wondering if there is a way to change listen port from the app.js file, instead of using "micro -p 8080", something like

...
let rout = router(get('/hello/:who', hello), get('/*', notfound));
let server = micro(rout);
var port = process.env.PORT || 8080;
server.listen(port, () => console.log('microrouter listening on port ' + port));

Thanks, Lorenzo

Run some code on every request

What if I wanted to run some code on every request/route? For example, logging the headers to the console.

Is there any way to do this without having to put the code in every handler?

Thanks for the great library!

Possibility of add 405 Method Not Allowed

Hi,
There is the possibility of adding 405 Method Not Allowed, by default, when a resource is invoked through an erroneous method, a 500 status code without information is returned.
Any chance of achieving this?

Support nested router

want to have something like
const api1 = router(
'/', someendpoint
)
router(
'api/v1', api1,
'api/v2', api2
)

Routes with underline on argument don't work

An example:

This doesn't work:

module.exports = router(
  del('/:my_id', myFunctionWithRoute),
  post('/*', notFoundFunction),
);

But this one does:

module.exports = router(
  del('/:myId', myFunctionWithRoute),
  post('/*', notFoundFunction),
);

Note the difference between my_id and myId.

Middleware in router

I try use a middleware like a commented but, when run a code , this stop my request e don`t go a route

const {router, get, post} = require('microrouter')
const {json, send} = require('micro')


const UsersList = require('./users');

const routes = router(
  get('/', () => json({
    name: "Alexandre"
  })),
  get('/users', UsersList),
  post('/users', (req, res) => json(req))
)


const applyMiddleware = fn => (req, res) => {
  console.log(req.headers)
  fn(req, res)
}

module.exports = applyMiddleware(routes)

How use a Middleware without stop request in micro?

Using stream with router

Hello, I think it might be issue, please check this out:

this code below don't return any response from the server. neither error too.

const fs = require('fs')
const { router, get } = require('microrouter')


const bundle = (req, res) => {
	res.statusCode = 200
	const obj = fs.createReadStream('./dist/bundle.js')

	res.setHeader('Content-Type', 'application/javascript')

	obj.pipe(res)
}

module.exports = router(
	get('/', bundle)
)

The same arrow function will work without router functionality:

const fs = require('fs')


module.exports = (req, res) => {
	res.statusCode = 200
	const obj = fs.createReadStream('./dist/bundle.js')

	res.setHeader('Content-Type', 'application/javascript')

	obj.pipe(res)
}

`withNamespace` should support Higer Order nesting.

let say:

const api = withNamespace('/v1')
const affiliate = withNamespace('/affiliate')

  api(
     affiliate(
         post('/affiliation', affiliation)
         get('/dashboard', dashboard)
     )
  )

instead of

const api = withNamespace('/v1')

  api(
     post('/affiliate/affiliation', affiliation)
     get('/affiliate/dashboard', dashboard)
  )

As of now with withNamespace it's closely the same as :

  router(
     post('/v1/affiliate/affiliation', affiliation)
     get('/v1/affiliate/dashboard', dashboard)
  )

The other way around would be the possibility of nesting router by their namespaces.

@pedronauck

Ability to use function inside handler.

For auth purpose It would be good to be able to pass function to our handler.

This way you can use: get('/account', auth(account)) instead of

module.exports = async (req, res) => {
   await auth(req)
 }

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.