Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use micro with two routes? #16

Closed
rafaeljesus opened this issue Mar 7, 2016 · 27 comments
Closed

How to use micro with two routes? #16

rafaeljesus opened this issue Mar 7, 2016 · 27 comments

Comments

@rafaeljesus
Copy link

How would I design a service with more then one route?

For example I have a event-trace-api service where I trace all transactions in my app and expose a api where I can search for these events,

So basically I have 2 routes:
POST /v1/events
GET /v1/events/search

Following micro design I would have 2 separate servers, one for create and other for search, that looks too much for me,

Thanks

@sergiodxa
Copy link

I think you should use search as a query param instead of part of the url, then you can just check request.query.search and if it's undefined return the full list.

If you want to use search as part of the url then I'm sure you should to use 2 separate servers, one for the complete list and other one for the searched list.

@rafaeljesus
Copy link
Author

Hi @sergiodxa thx for your reply,

That's not very intuitive to have one route to deal with POST and GET

Looks like micro is a nano-service lib and that's totally fine I think

@sergiodxa
Copy link

Micro is just to create HTTP services using async/await and thinking in microservices, it's a wrapper for the http Node.js native module. But it not give you a router handler, you can use other router handlers which can receive the request object.

Thinking in how made that what about something like this?

import micro, {
  json,
  send,
  sendError,
} from 'micro';

/**
 * handle POST requests
 */
async function postHandler(request) { /* ... */ }
/**
 * handle GET requests
 */
async function getHandler(request) { /* ... */ }

/**
 * Check the request method and use postHandler or getHandler (or other method handlers)
 */
async function methodHandler(request, response) {
  try {
    switch (request.method) {
    case 'POST':
      return await postHandler(request);
    case 'GET':
      return await getHandler(request);
    default:
      send(response, 405, 'Invalid method');
      break;
    }
  } catch (error) {
    throw error;
  }
}

/**
 * our micro service, run methodHandler and send result as a response (or an error)
 */
const server = micro(async (request, response) => {
  try {
    send(response, 200, await methodHandler(request));
  } catch (error) {
    sendError(request, response, error);
  }
});

server.listen(process.env.PORT);

This way you have a function for each valid method.

@rafaeljesus
Copy link
Author

@sergiodxa thank you very much for your explanation,

I thought doing this would go against micro principals design,

And this design totally fit into my trace-events-api service that I have running in production now,

Cheers

@rauchg
Copy link
Member

rauchg commented Mar 8, 2016

@sergiodxa that's a beautiful example. Thank you for contributing it.

If you want routes, you can write another micro service that maps routes to backends, or use something like Nginx. You would host each of your services under a subdomain or port independently.

We're running 20+ microservices already in production. We had to use routing once, and we simply looked at req.url and performed a simple string comparison.

If you need a sophisticated routing mechanism, you're probably doing too much in one Node process. You'll pay in the form of more complicated debugging, conflated logging and lack of error isolation.

@rauchg rauchg closed this as completed Mar 8, 2016
@rauchg
Copy link
Member

rauchg commented Mar 8, 2016

Based on your example, in fact, I see that you want to do versioning. Not having routing built in is great for this.

Let's say /v2/events/search is the same, but /v2/events POST changed. You basically can bump versions by simply forwarding all v2 traffic to the existing v1 backends, and only add one exception, for /v2/events to go to add-event-2.0.0.yourhost.com. URLs are just labels!

@rafaeljesus
Copy link
Author

Great explanation @rauchg

I'll think on how to design this on Heroku, I don't have nginx right there,

So basically, are you doing in your production servers "one server for one route"?

Cheers

@rauchg
Copy link
Member

rauchg commented Mar 8, 2016

Indeed. I have no experience with Heroku.

@rafaeljesus
Copy link
Author

But hey I'm going to remove koajs form some microservices in production and use micro right now \o

@diorahman
Copy link

@sergiodxa I'm not sure we have request.query in micro's request object. Or I'm wrong?

@sergiodxa
Copy link

@diorahman yes, I just tried it and in the request object we don't have request.query, but it's possible to use url and querystring to get it in one line.

import qs from 'querystring';
import url from 'url';

async function requestHandler(request, response) {
  const query = qs.parse(url.parse(request.url).query);
  // do something
}

@onbjerg
Copy link
Contributor

onbjerg commented Apr 7, 2016

What about resources? E.g. the user with ID 1 would usually be /users/1. What would this look like in micro?

@sergiodxa
Copy link

I think you should do that yourself (I suppose with a RegEx), but I will quote @rauchg here:

If you need a sophisticated routing mechanism, you're probably doing too much in one Node process. You'll pay in the form of more complicated debugging, conflated logging and lack of error isolation.

@rauchg
Copy link
Member

rauchg commented Apr 8, 2016

A simple match utility would be nice for that. Not sure if it exists but:

const { id } = match('/users/:id', url);

would return {} for no match (eg: '/user'), { id: 1 } for /users/1 etc. Kind of saves you the trouble of checking that the regexp returns null, looking for the index of the capture group, etc.

@onbjerg
Copy link
Contributor

onbjerg commented Apr 8, 2016

@sergiodxa I don't think that quote applies here. I mean, you wouldn't want a micro service just for getting single users, and then a microservice for getting a list of users, would you?

@rauchg Sure, I guess that'd work.

@TooTallNate
Copy link
Member

@sergiodxa You can simplify your one-liner by adding true to url.parse():

const parsed = url.parse(request.url, true);
console.log(parsed.query);

@TooTallNate
Copy link
Member

@onbjerg
Copy link
Contributor

onbjerg commented Apr 8, 2016

Great resources, thanks @TooTallNate!

@rauchg Any plans on adding wiki pages with this content? I'm thinking of examples and/or resources. I think it would be beneficial. I think we already have some good ones, they just need a place to be put.

@rauchg
Copy link
Member

rauchg commented Apr 9, 2016

@onbjerg that sound fantastic. It'd be cool to index the Wiki pages on the README for quick reference as well. PRs welcome

@onbjerg
Copy link
Contributor

onbjerg commented Apr 10, 2016

@rauchg Cool! You just need to enable the wiki first 👍

@onbjerg
Copy link
Contributor

onbjerg commented Jul 13, 2016

@rauchg Any updates on the wiki? 💃

@nblackburn
Copy link

@rauchg I went ahead and created a little utility based on your idea for matching urls.

https://github.com/nblackburn/micro-match

@domachine
Copy link
Contributor

Just wanted you guys to know that I have worked a tiny router and architecture suggestions that work great with micro :) https://github.com/domachine/http-service-router

@diorahman
Copy link

When your intention is boilerplating, but it is ended up putting to much sugar https://github.com/diorahman/mcrsvc LOL!

@onbjerg
Copy link
Contributor

onbjerg commented Nov 7, 2016

@diorahman To be honest, routes shouldn't really be in a micro-service if possible. Limiting for HTTP method is alright.

@diorahman
Copy link

diorahman commented Nov 7, 2016

@onbjerg true, yeah. I'm still struggling on how to get the best workflow on building set of services, without sacrificing start time. I still think, when it comes to deployment, api gateway seems the best way (e.g. https://github.com/adobe-apiplatform/apigateway), but how about the development phase?

@just-boris
Copy link

For those, who are also looking for a routing solution I'd suggest looking at https://github.com/amio/awesome-micro

That repo contains interesting additions to the micro project, including several routing solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants