Fun stuff : node.js in an Umbraco site

Standard

Did you ever want to run node.js within your umbraco site, and edit the server side javascript from within your Umbraco backend? Not? Well, it’s still a fun thing to do, don’t you think?

Update 1: I actually think nodejs makes some sense within Umbraco. Why? Well, for example it makes it possible for js-peeps to stay js. And node has a _lot_ of nice plugins (as for example jade and express).

Update 2: I made a first version of a sample of Umbraco running with ExpressJs and Jade, it uses Tapas that serves published content for requested url.

The setup only takes a few minutes to do:

  1. Install NodeJs and IISNode on your computer / server
  2. Install an Umbraco site or use an existing, using WebMatrix
  3. Add the NPM plugin from WebMatrix Gallery
  4. Add the folders iisnode and node_modules for the NPM plugin to show up in the toolbar (and restart WebMatrix)
  5. Add the Express Node module with the help of NPM. It puts the module in a new folder in your root “node_modules”
  6. Add the following lines to your web.config, in the configuration section (at the bottom for example):
...
    <location path="scripts/node">
        <system.webServer>
            <handlers>
                <add name="iisnode" path="server.js" verb="*" modules="iisnode" />
            </handlers>

            <rewrite>
                <rules>
                    <rule name="express">
                        <match url="/*" />
                        <action type="Rewrite" url="/scripts/node/server.js" />
                    </rule>
                </rules>
            </rewrite>

        </system.webServer>

    </location>
</configuration
  • Add a new .js file and save it as /scripts/node/server.js
  • var express = require('express');
    
    var app = express.createServer();
    
    app.get('/scripts/node/hello/:name', function (req, res) {
        
    	
    	res.send('Hello ' + req.params.name + ' from node! [express sample]');
    	
    	
    });
    
    app.listen(process.env.PORT);
    

    That’s it! Now you should be able to surf to /scripts/node/hello/world and you should get “Hello world from node!” back.

    Update 2: What about Umbraco content?

    What if we like to do something a little bit more useful – make it possible to get and edit content? Well, our node application does not have the Umbraco contents available, so we need to get it in some other way : I guess http is the standard.

    Currently (in Umbraco 6.0) there is no available Restful Api out of the box (it will be added in 6.1 afaiu). However, the uRest package by Matt Brailsford seems to play nicely with u6 (edit : perhaps not, I could not get it to accept posts). Add that, and then change server.js and add a urest.js file as in this gist to get contents with internal http requests.

    app.get('/scripts/node/urest/documents/:id',function(req,res){
        var documentId = req.params.id;
        requestGetWithToken("/documents/" + documentId, function (response) {
            // just return the content to the response string:
            res.send(response);
        });
    });

    Now you can browse to the url /scripts/node/documents/{nnnn} to get a particular document.

    Check out the uRest documentation for information about how to create and edit content.

    Update 3: content straight from umbraco context with the help of EdgeJs

    I just managed to get actual content directly from umbraco context into nodejs with the help of the brilliant EdgeJs https://github.com/tjanczuk/edge by Tomasz Janczuk and https://github.com/sitereactor/umbraco-console-example by Morten Christensen.

    I will blog more details about it, but what I needed to do was this:
    1. Add Edge with the help of NPM.
    2. Create a class libary dll that fires up the Umbraco context (copied from Umbraco console example), with functions returning async Task

    public async Task<object> ListNodes(object input)
    {
      return "nodes: " + listNodes();
    }
    

    3. Setup functions inside my node app calling the .net functions

    var express = require('express');
    var edge = require('edge');
    var app = express.createServer();
    var listNodes = edge.func({
        assemblyFile: 'C:/harcodedpath-to-my-website/bin/umbracocontext.dll',
        typeName: 'UmbracoContext.Startup',
        methodName: 'ListNodes'
    }); 
     
    app.get('/scripts/node/hello', function (req, res) {         
        listNodes("",function(error,data){
            res.send(data);        
        })
    });
    app.listen(process.env.PORT);

    4. Add a node.exe.config into my node.exe folder with just a connectionstring to begin with:

    <?xml version="1.0" ?>
    <configuration>
        <connectionStrings>
            <remove name="umbracoDbDSN" />
            <add name="umbracoDbDSN"
                 connectionString="Datasource=C:\harcodedpath-to-my-website\App_Data\Umbraco.sdf"
                 providerName="System.Data.SqlServerCe.4.0" />
        </connectionStrings>
    </configuration>
    

    It’s obviously not ideal to set the connectionstring in the global node.exe folder. I think a good workaround to get local connectionstrings is to copy the node.exe to a folder within the site and set the iisnode handler to use the local node.exe instead.

    <iisnode
          nodeProcessCommandLine="&quot;c:\harcodedpath-to-my-website\nodejs\node.exe&quot;" 
    

    Edge does not make it possible (yet) to use the already existing instance of the asp.net application (share memory), so if we run umbraco in asp.net and node in parallell we will have two context instances.

    If you are concerned about the performance: read this.

    GetTreeById

    app.get('/scripts/node/getTreeById/:nodeId', function (req, res) {  
        getTreeById(Number(req.params.nodeId),function(error,data){
            res.send(data);        
        })
    });
    public async Task<object> GetTreeById(int nodeId)
    {
        var node = contentService.GetById(nodeId);
        return nodeToDictionary(node, true);
    
    }
    private Dictionary<string, object> nodeToDictionary(IContent content, bool includeChildren)
    {
        var nodeData = new Dictionary<string, object>();
        nodeData["Name"] = content.Name;
        nodeData["Id"] = content.Id;
                
        content.Properties.ForEach(p => { nodeData[p.Alias] = p.Value.ToString(); });
        if (includeChildren)
            nodeData["Children"] = content.Children().Select(child => nodeToDictionary(child, true)).ToList();
        return nodeData;
    }
    

    tree

    About these ads

    Leave a Reply

    Please log in using one of these methods to post your comment:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s