1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta http-equiv="X-UA-Compatible" content="chrome=1"> 6 <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 8 <link rel="shortcut icon" href="/images/favicon.png" type="image/png" /> 9 10 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Merriweather:400,700|Open+Sans:400,400italic,700"> 11 <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" /> 12 <link rel="stylesheet" href="/stylesheets/style.css" /> 13 14 <title>Seneca, a microservices toolkit for Node.js</title> </head> 15 <body> 16 <nav class="nav-main" role="navigation"> 17 <div class="container-fluid cf"> 18 <a class="logo logo-seneca" href="/" title="Seneca">Seneca</a> 19 20 <div class="nav-search"> 21 <input type="search" id="seneca-search-input" placeholder="Search the docs..."> 22 </div> 23 <!-- nav search --> 24 25 <div class="nav-bar"> 26 27 <input type="checkbox" name="nav-menu-handle" id="nav-menu-handle" class="nav-menu-handle"> 28 <label for="nav-menu-handle"></label> 29 30 <ul class="list-unstyled list-inline nav-items"> 31 <li><a href='/api/'>API</a></li> 32 <li><a href='/docs/'>Docs</a></li> 33 <li><a href='/faq/'>FAQ</a></li> 34 <li><a href='/plugins/'>Plugins</a></li> 35 <li><a href='/roadmap/'>Roadmap</a></li> 36 <li><a href='/support/'>Support</a></li> 37 </ul> 38 <!-- nav items --> 39 </div> 40 41 </div> 42 <!-- container --> 43 </nav> 44 <header role="banner"> 45 <div class="container-fluid center-xs"> 46 <img src="/images/illustration-top.svg" 47 alt="Design, develop and organize code" 48 title="Design, develop and organize code" 49 height="auto" 50 width="320" 51 class="mt" 52 /> 53 </div> 54 </header> 55 56 <div class="container-fluid"> 57 <div class="row center-xs"> 58 <div class="col-xs-12 col-md-10 col-lg-10 txt-left"> 59 <h1 id="frequently-asked-questions"><a href="#frequently-asked-questions" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>Frequently Asked Questions</h1> 60<ul> 61<li><a href="#q-gotq">Got a question?</a></li> 62<li><a href="#q-patuse">Why is pattern-matching so useful?</a></li> 63<li><a href="#q-recstruct">What is the recommended structure for Seneca apps?</a></li> 64<li><a href="#q-fatal">Why is the Seneca process dying with a FATAL error?</a></li> 65<li><a href="#q-reply">How do I respond to a message?</a></li> 66</ul> 67<p></p> 68 69 70 71<p><a href="#" class="linkable" style="color: rgba(41, 125, 134, 0.20); display:inline-block; float:left; margin: 8px -80px">[⇧]</a> 72<a name="q-gotq"></a></p> 73<h3 id="got-a-question"><a href="#got-a-question" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>Got a question?</h3> 74<p>Please post an issue to github, marking as “FAQ:” in the title: <a href="https://github.com/senecajs/senecajs.org/issues/new?title=FAQ:%20">I 75want to ask a 76question!</a></p> 77<p><a href="#" class="linkable" style="color: rgba(41, 125, 134, 0.20); display:inline-block; float:left; margin: 8px -80px">[⇧]</a> 78<a name="q-patuse"></a></p> 79<h3 id="why-is-pattern-matching-so-useful"><a href="#why-is-pattern-matching-so-useful" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>Why is pattern-matching so useful?</h3> 80<p>Because it gives you a clean component model. This lets you build big 81things out of small things without ending up with spaghetti code.</p> 82<p>The trick is to make composition easy. That way you can combine and 83extend microservices. Instead of modifying an existing microservice, 84simply add a new one with more functionality. This is a much more 85scalable way to handle changing requirements without building up 86technical debt.</p> 87<p>There’s a <a href="https://vimeo.com/151465912">great talk by Gerald Sussman</a> 88(co-inventor of Scheme) on the basic principles of this idea. There’s 89also a 90<a href="http://www.richardrodger.com/seneca-microservices-nodejs#.Vq
90i9LBiLT-k">maintainer blog post</a> 91explaining this from a Seneca perspective.</p> 92<pre style="margin-bottom:0px"> 93A diamond is very pretty. 94But it is hard to add to a diamond. 95  96A ball of mud is not so pretty. 97But you can always add more mud to a ball of mud. 98</pre> 99<div style="text-align:right"><small>[Joel Moses](https://en.wikipedia.org/wiki/Joel_Moses) / [Paul Penfield](http://www-mtl.mit.edu/~penfield/)</small></div> 100 101<p> </p> 102 103 104 105<p><a href="#" class="linkable" style="color: rgba(41, 125, 134, 0.20); display:inline-block; float:left; margin: 8px -80px">[⇧]</a> 106<a name="q-recstruct"></a></p> 107<h3 id="what-is-the-recommended-structure-for-seneca-apps"><a href="#what-is-the-recommended-structure-for-seneca-apps" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>What is the recommended structure for Seneca apps?</h3> 108<p>Some things that work well:</p> 109<ul> 110<li><p>Separate the business logic from the execution. Put your business 111logic into separate plugins - either separate node modules, 112different repositories, or simply different files in the same 113repository.</p> 114</li> 115<li><p>Use execution scripts to compose your app. Don’t be afraid to use 116different scripts for different contexts. They should be pretty 117short any way. You want your scripts to look something like:</p> 118</li> 119</ul> 120<pre><code>var SOME_CONFIG = process.env.SOME_CONFIG || 'some-default-value' 121 122require('seneca')({ some_options: 123 }) 123 124 // existing Seneca plugins 125 .use('community-plugin-0') 126 .use('community-plugin-1', {some_config: SOME_CONFIG}) 127 .use('community-plugin-2') 128 129 // your own plugins with your own business logic 130 .use('project-plugin-module') 131 .use('../plugin-repository') 132 .use('./lib/local-plugin') 133 134 .listen( ... ) 135 .client( ... ) 136 137 .ready( function() { 138 // your own custom code - executed once Seneca is spun up 139 })</code></pre><ul> 140<li>Plugin loading order <em>is significant</em>. This is a good thing. It 141lets you control the <a href="http://www.richardrodger.com/seneca-microservices-nodejs#.VqdKAhiLT-k">composition of your message language</a>.</li> 142</ul> 143<p>Things that don’t work well:</p> 144<ul> 145<li><p>Mixing Seneca initialization with other framework 146initialization. Define your express or hapi app in one file, and 147Seneca in another. Keep it separate and simple.</p> 148</li> 149<li><p>Passing the Seneca instance around. Don’t create a Seneca 150instance and then pass it as a parameter to stuff you’ve 151<code>required</code> in, and only then start adding plugins and 152actions. Instead, use small execution scripts with clear and 153linear construction of your service from a list of plugins.</p> 154</li> 155</ul> 156<p>Looking for an example structure to copy? The 157<a href="https://github.com/nodezoo/nodezoo-workshop">nodezoo workshop</a> is a good 158place to find one.</p> 159<p><a href="#" class="linkable" style="color: rgba(41, 125, 134, 0.20); display:inline-block; float:left; margin: 8px -80px">[⇧]</a> 160<a name="q-fatal"></a></p> 161<h3 id="why-is-the-seneca-process-dying-with-a-fatal-error"><a href="#why-is-the-seneca-process-dying-with-a-fatal-error" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>Why is the Seneca process dying with a FATAL error?</h3> 162<p>Your Seneca microservice will rely on a set of plugins, both <a href="https://github.com/senecajs?utf8=%E2%9C%93&q=seneca-*&type=source&language=">community 163plugins</a>, and your own, to provide the actions that respond to 164messages. All of the plugins need to load and initialize 165successfully. If they don’t, your microservice will be in an 166undefined state. The best thing to do in this case is bail out and 167die.</p> 168<p>For example, if you are using the 169<a href="https://github.com/senecajs/seneca-mongo-store"><code>seneca-mongo-store</code></a> 170plugin, and the plugin cannot connect to the database, then your 171microservice can’t do any work. Maybe the failure is transient. In 172that case, you want the microservice to die and restart. Your 173container system (Docker, Kubernetes, etc) will start a new instance 174of your microservice, but needs the old one to die first.</p> 175<p>When 176<a href="/docs/tutorials/how-to-write-a-plugin.html">Seneca loads a plugin</a>, 177it is first <em>defined</em>, then <em>initialized</em>. Plugin definition happens 178inside the plugin definition function, where you add your patterns:</p> 179<pre><code class="language-js">// file: foo.js 180// The string `foo` will be the name of the plugin. 181module.exports = function foo(options) { 182 183 // `this` === context Seneca instance with fatal$ === true 184 // the pattern is `a:1` 185 this.add('a:1', function (msg, reply) { 186 reply({x: msg.x}) 187 }) 188}</code></pre> 189<p>A failure of any sort here is fatal, as it means the plugin is not 190fully defined. Some patterns could be missing. Plugin definition is 191<em>synchronous</em>
191.</p> 192<p>Plugin initialization happens inside the <code>init:<plugin-name></code> 193action. Add this inside your plugin definition:</p> 194<pre><code class="language-js">// file: foo.js 195// The function `foo` will be the name of the plugin. 196module.exports = function foo(options) { 197 198 this.add('a:1', function (msg, reply) { 199 reply({x: msg.x}) 200 }) 201 202 this.add('init:foo', function (msg, reply) { 203 204 // do something asynchronous 205 database_setup(options, function(err) { 206 207 // call reply to indicate that the plugin is initialized, 208 // no need for response data 209 reply(err) 210 }) 211 }) 212}</code></pre> 213<p>Plugin initialization is optional, and only necessary if you need to 214perform an asynchronous operation such as interacting with the outside 215world. Failures in initialization are also fatal.</p> 216<p>The <a href="/api/#method-ready"><code>seneca.ready</code></a> callback is not called until 217all plugins are defined and initialized.</p> 218<p>To make an action fatal when it fails, use the <code>fatal$:true</code> directive 219as part of your message. This is what Seneca itself does for plugin 220definition (which is run inside an internally created one-off action) 221and initialization. The <code>fatal$</code> directive is set as a fixed argument 222of any messages sent by the plugin Seneca context.</p> 223<p>There are times when you want to avoid this behavior. One way, when 224debugging, is to set the option <code>debug.undead = true</code>:</p> 225<pre><code>var Seneca = require('seneca') 226var seneca = Seneca({debug: {undead: true}})</code></pre><p>Another is to create a new Seneca instance that does not have a fixed 227<code>fatal$ = true</code> argument:</p> 228<pre><code>var fresh_seneca = this.root.delegate()</code></pre><p>This is necessary for plugins that will send messages once Seneca is 229ready, such as the transport plugins.</p> 230<p><a href="#" class="linkable" style="color: rgba(41, 125, 134, 0.20); display:inline-block; float:left; margin: 8px -80px">[⇧]</a> 231<a name="q-reply"></a></p> 232<h3 id="how-do-i-respond-to-a-message"><a href="#how-do-i-respond-to-a-message" class="linkable" style="margin-left:-2rem; margin-right:1rem; display:inline-block;font-size:1.2rem;color:rgba(41, 125, 134, 0.20);text-decoration:none;vertical-align:middle">§</a>How do I respond to a message?</h3> 233<p>When a message pattern is matched, it triggers execution of the 234associated action function that you have defined. Your action function 235is passed three parameters:</p> 236<ul> 237<li><code>msg</code>: the message that triggered this action.</li> 238<li><code>reply</code>: a callback function that you can use to reply to the message.</li> 239<li><code>meta</code>: a meta data object for tracing and debugging (normally ignored).</li> 240</ul> 241<p>Thus a typical action definition looks like so:</p> 242<pre><code>const Seneca = require('seneca') 243 244const seneca = Seneca() 245 246seneca 247 .add({a: 1}, function(msg, reply) { 248 reply(null, {x: msg.y}) 249 })</code></pre><p>And to use it, you call:</p> 250<pre><code>seneca.act({a: 1, y: 2}, function(err, out) { 251 console.log(err) // prints null, as there was no error 252 console.log(out) // prints {x: 2}, as that was the response given to `reply` 253})</code></pre><p>The <code>reply</code> callback follows the normal signature for callbacks: 254<code>callback(err, result)</code>. But it also provides some convenience 255abbreviations. You can provide a data response using:</p> 256<ul> 257<li><code>reply(null, {z: 3})</code></li> 258<li><code>reply({z: 3})</code></li> 259</ul> 260<p>You can provide an error response using:</p> 261<ul> 262<li><code>reply(new Error('my error message')</code></li> 263</ul> 264<p>And you can provide an empty response using:</p> 265<ul> 266<li><code>reply()</code></li> 267</ul> 268<p>Thus, our example above can be more conveniently written as:</p> 269<pre><code>seneca 270 .add({a: 1}, function(msg, reply) { 271 reply({x: msg.y}) 272 })</code></pre><p>It is also common practice to pass the <code>reply</code> callback into another 273function expecting a standard signature callback. For example:</p> 274<pre><code>const Fs = require('fs') 275 276seneca 277 .add({file: 'read'}, function(msg, reply) {
278 Fs.stat(msg.path, reply) 279 })</code></pre> 280 </div> 281 </div> 282 283 <div class="row center-xs"> 284 <div class="col-xs-12 col-md-10 col-lg-10"> 285 <p class="txt-lead mb mt2x">Issues? From spelling errors to broken tutorials and everything in between, report 286 them <a href="https://github.com/senecajs/senecajs.org/issues">here.</a></p> 287 </div> 288 </div> 289 </div> 290 291 <footer role="contentinfo"> 292 293 <div class="footer-top"> 294 <div class="container-fluid"> 295 <div class="row center-xs"> 296 <div class="col-xs-12"> 297 <img src="/images/logo-seneca-inversed.svg" class="logo-seneca-lg" alt="Seneca" /> 298 </div> 299 300 <div class="col-xs-12 col-sm-4 col-md-3"> 301 <p class="mt"><a href="https://github.com/senecajs/seneca" class="link-has-icon icon icon-github">Github</a></p> 302 </div> 303 304 <div class="col-xs-12 col-sm-4 col-md-3"> 305 <p class="mt"><a href="/docs/" class="link-has-icon icon icon-docs">Documentation</a></p> 306 </div> 307 <div class="col-xs-12 col-sm-4 col-md-3"> 308 <p class="mt"><a href="https://twitter.com/senecajs" class="link-has-icon icon icon-twitter">Twitter</a></p> 309 </div> 310 </div> 311 <!-- row --> 312 </div> 313 </div> 314 <!-- footer top --> 315 316 <div class="footer-bottom"> 317 <div class="container-fluid txt-center"> 318 <p class="mb0">© <a href="https://github.com/rjrodger">Richard Rodger</a> and <a href="https://github.com/senecajs/senecajs.org/contributors">other</a> <a href="https://github.com/senecajs/seneca/contributors">contributors</a> 2010 - 2023. Powered by <a href="http://metalsmith.io">MetalSmith</a>, hosted by <a href="http://surge.sh">Surge</a>. A <a href="https://www.voxgig.com">
318Voxgig</a> project.</p> 319 </div> 320 </div> 321 <!-- footer bottom --> 322 </footer> 323 324
324<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js"></script>
324 325
325<script>hljs.initHighlightingOnLoad();</script>
325 326 327
327<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js" integrity="sha256-vrn14y7WH7zgEElyQqm2uCGSQrX/xjYDjniRUQx3NyU=" crossorigin="anonymous"></script>
327 328
328<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script>
328 329
329<script type="text/javascript"> docsearch({ 330 apiKey: 'a5afa8843727758cbaa7fbacb932b215', 331 indexName: 'senecajs', 332 inputSelector: '#seneca-search-input' 333 }); 334 </script>
334
334<script>
vendor: 363 bytes, lines 334-340
334 335 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 336 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 337 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 338 })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 339 340 ga('create', '
340UA-57673-6
vendor: 54 bytes, lines 340-342
340', 'senecajs.org'); 341 ga('send', 'pageview'); 342
342</script>
342 343
343<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"347dc49995754bb7b7965337d6cf85df","r":1,"spa":2}' crossorigin="anonymous"></script>
343 344</body> 345</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.