Documentation

First steps

First, install the DUB package manager to let it handle downloading and building of vibe.d and derived applications. On non-Windows systems, a number of additional dependencies needs to be installed. See the project description on GitHub for details.

Manual building (e.g. using RDMD) is a possible alternative, but you have to make sure that external libraries are linked in (such as libevent) and that a version identifier for the used driver is passed to the compiler (e.g. -version=VibeLibeventDriver). Please take a look at vibe.d's dub.json to determine how to build on a certain platform.

The easiest way to get started is to run the following command from your projects directory:

$ cd /path/to/my/projects
$ dub init <project-name> vibe.d

This will create a new directory with the given name and creates the basic directory structure that is recommended for a vibe.d project. You can then go ahead and edit the source/app.d file. You should also edit the dub.json file and properly fill in all fields, including a "license" field, if you want to publish your project.

The recommended structure for a project is about as follows:

appname/
	source/
		app.d
	public/
		images/
		styles/
			style.css
	views/
		layout.dt
		index.dt
	dub.json
Example structure of a web application project

For a simple web application, the 'app.d' file could look similar to this one:

import vibe.d;

void index(HTTPServerRequest req, HTTPServerResponse res)
{
	res.render!("index.dt", req);

	// Use the compatibility version for DMD < 2.064 as render()
	// suffers from DMD bugs 2962/10086/10857
	//res.renderCompat!("index.dt", HTTPServerRequest, "req")(req);
}

shared static this()
{
	auto router = new URLRouter;
	router.get("/", &index);

	auto settings = new HTTPServerSettings;
	settings.port = 8080;

	listenHTTP(settings, router);
}
Simple app.d file with minimal routing

The 'views' sub folder is automatically searched for templates instantiated with render(). The two templates in the example structure might look like this:

doctype html
html
	head
		title Example page
	body
		block body
Example layout.dt
extends layout

block body
	h1 Example page - Home
	p Hello, World!
Example index.dt

This structure makes use of the blocks/extensions feature of the template parser. For more advanced template features see the Diet template documentation.

Finally, you can add a "dub.json" file to let the vibe.d package manager automatically download and compile extension libraries. This file will also provide the description that is needed to later put your own library into the public extension registry.

{
	"name": "appname",
	"description": "My fabulous new app",
	"copyright": "Copyright (C) 2000 Me. All rights reserved.",
	"license": "AGPL-3.0",
	"homepage": "http://appname.org",
	"authors": ["Hans Wurst"],
	"dependencies": {
		"vibe-d": "~>0.7.20"
	},
	"versions": ["VibeDefaultMain"]
}
Example dub.json

Once you have the project in place, simply run vibe from the project's root directory and it will get all dependencies, compile the application, and run it:

$ cd path/to/project
$ dub
Checking dependencies in 'path/to/project'
Listening on 0.0.0.0 port 8080 succeeded
Listening on :: port 8080 succeeded
Running event loop...

HTTP

Server configuration

The HTTP server supports a number of configuration options to customize its behavior. By default, the server will listen on all local network adapters on port 80 and will perform complete request parsing. The following list gives an overview of the most common settings:

port
The port on which the HTTP server shall listen
bindAddresses
A list of all interfaces on which the server shall listen. IPv4 and IPv6 addresses, as well as domain names are supported.
options
Controls optional features of the web server. Certain options can be disabled to increase speed or to decrease memory usage. By default, the following options are enabled: parseURL, parseQueryString, parseFormBody, parseJsonBody, parseMultiPartBody, parseCookies. Enabled options are ORed together.
errorPageHandler
Provides a way to customize error pages. For example:
void errorPage(HTTPServerRequest req,
	HTTPServerResponse res,
	HTTPServerErrorInfo error)
{
	res.render!("error.dt", req, error);

	// NOTE: On DMD < 2.064 use renderCompat instead:
	//res.renderCompat!("error.dt",
	//	HTTPServerRequest, "req",
	//	HTTPServerErrorInfo, "error")
	//	(req, error);
}

shared static this()
{
	auto settings = new HTTPServerSettings;
	settings.errorPageHandler = toDelegate(&errorPage);
	// ...
}
Inside of the error.dt template, the variables req, code and msg are available in this example.
sslContext
Lets the server operate as an HTTPS server. You should probably also set the port to 443 in case this field is set.

Routing

The URLRouter class provides a convenient way to let different functions handle different URLs. It supports static path matching, variable placeholders and wild-cards. Any matched variable will be available as an entry in the params dictionary.

In addition to the path, the HTTP method is also used for matching requests. Each HTTP method has a corresponding method in the URLRouter class (e.g. get() or post()). The following example will route all GET requests matching the path scheme "/users/*" to the userInfo handler and serve all other GET requests using the files in the public folder, see serveStaticFiles.

import vibe.d;

void userInfo(HTTPServerRequest req, HTTPServerResponse res)
{
	auto username = req.params["user"];
	render!("userinfo.dt", username)(res);
}

void addUser(HTTPServerRequest req, HTTPServerResponse res)
{
	enforceHTTP("user" in req.form, HTTPStatus.badRequest, "Missing user field.");
	res.redirect("/users/"~req.form["user"]);
}

shared static this()
{
	auto router = new URLRouter;
	router.get("/users/:user", &userInfo);
	router.post("/adduser", &addUser);
	router.get("*", serveStaticFiles("./public/"));

	// To reduce code redundancy, you can also
	// use method chaining:
	router
		.get("/users/:user", &userInfo)
		.post("/adduser", &addUser)
		.get("*", serveStaticFiles("./public/"));

	listenHTTP(new HTTPServerSettings, router);
}
Example: GET/POST routing and static file serving

Diet templates

Vibe supports HTML templates with a syntax mostly compatible to jade templates. They provide a concise way to dynamically generate the HTML code of the final web pages. D expressions and statements can be embedded and the full vibe API is available within templates.

Templates should reside somewhere inside the 'views' folder of a project. They are then rendered using the render function, which takes the template file name as the first template argument, followed by a list of variables that should be available to the template. Finally, it takes a HTTPServerResponse to render into.

The following example shows a number of features of the template parser. A full reference of the template syntax is found on the Diet templates page.

doctype html
html
	head
		title My page: #{pageTitle}
	body
		h1= pageTitle
		p This is the content of this page. The
			| title "#{pageTitle}" is inserted dynamically.
			| We can also use loops and other D statements:
		block placeholder
		p
			- foreach(i, ch; pageTitle)
				| #{i+1}. character: #{ch}
		p.special.small This paragraph has the 'special'
			| and 'small' CSS classes
		p#footer This paragraph has the id 'footer'.
		#somediv.
			I'm a multiline text
			inside the div #somediv
Example: Template file with dynamic code inserts and several other template features

Error pages

There are thrww ways in which an error page is sent back to the client:

  • An exception is thrown from the request handler or while parsing the request. By default, a 500 "Internal Server Error" is returned. By throwing a HTTPStatusException , the status code can be customized.
  • The request handler does not write a response. In this case the server automatically returns a 404 "Not Found" error.
  • The request handler manually sets an error status to HTTPServerResponse.statusCode and writes a body. In this case, the error page will be written exactly as specified.

The HTTPServerSettings can be used to provide a custom error page handler. If one is provided, it is called for any of the two conditions and must render an error page to the response object. If no handler is given, a simple plain-text error page is generated. See the HTTP server configuration section for an example.

Authentication

Currently, the only built-in authentication method is HTTP-Basic-Auth. HTTP-Digest-Auth will follow later and higher level mechanisms such as OAuth will be provided using extension libraries.

The recommended practice for plugging authentication into a web application is to use the fall-through feature of the URLRouter. If the user is properly authenticated, the performBasicAuth function will not do anything and the URLRouter will continue to match the request to all following routes. If, however, there is no authentication or if the username/password pair is not valid, it will throw a HTTPStatusException exception which generates a 403 error page so that the user is prompted with a password dialog on the browser side. The routing stops in this case.

import vibe.d;

bool checkPassword(string user, string password)
{
	return user == "admin" && password == "secret";
}

shared static this()
{
	auto router = new URLRouter;
	// the following routes are accessible without authentication:
	router.get("/", staticTemplate!"index.dl");
	router.get("/about", staticTemplate!"about.dl");

	// now any request is matched and checked for authentication:
	router.any("*", performBasicAuth("Site Realm", toDelegate(&checkPassword)));

	// the following routes can only be reached if authenticated:
	router.get("/profile", staticTemplate!"profile.dl");
	router.get("/internal", staticTemplate!"internal.dl");

	// ...
}
Example: Using HTTP-Basic-Auth to restrict access

Sessions

Cookie based HTTP sessions are supported directly by the HTTP server. To be able to use them, you first have to set a SessionStore in the HTTPServerSettings . Sessions are then established by calling HTTPServerResponse.startSession and stopped using HTTPServerResponse.terminateSession . The returned Session object behaves as a key value store taking strings as keys and values.

import vibe.d;

void login(HTTPServerRequest req, HTTPServerResponse res)
{
	enforceHTTP("username" in req.form && "password" in req.form,
		HTTPStatus.badRequest, "Missing username/password field.");

	// todo: verify user/password here

	auto session = res.startSession();
	session["username"] = req.form["username"];
	session["password"] = req.form["password"];
	res.redirect("/home");
}

void logout(HTTPServerRequest req, HTTPServerResponse res)
{
	res.terminateSession();
	res.redirect("/");
}

void checkLogin(HTTPServerRequest req, HTTPServerResponse res)
{
	// force a redirect to / for unauthenticated users
	if( req.session is null )
		res.redirect("/");
}

shared static this()
{
	auto router = new URLRouter;
	router.get("/", staticTemplate!"index.dl");
	router.post("/login", &login);
	router.post("/logout", &logout);
	// restrict all following routes to authenticated users:
	router.any("*", &checkLogin);
	router.get("/home", staticTemplate!"home.dl");

	auto settings = new HTTPServerSettings;
	settings.sessionStore = new MemorySessionStore;
	// ...
}
Example: Using a HTTP session for user authentication

Client requests

Client request are done using the requestHTTP() function.

import vibe.vibe

void main()
{
	requestHTTP("http://google.com",
		(scope req) {
			/* could e.g. add headers here before sending*/
		},
		(scope res) {
			logInfo("Response: %s", res.bodyReader.readAllUTF8());
		}
	);
}
Example: Performing a simple HTTP request

A connection pool is used internally in conjunction with persistent HTTP connection to reduce the number of connection establishments.

Web framework

Based on the low-level HTTP/HTML foundation, the high-level web application framework allows for more rapid and less error prone web application development. It uses a class based declarative approach to avoid the boilerplate code that is usually needed otherwise. Static typing is exploited as much as possible to avoid conversion errors, or errors accessing wrong runtime keys (such as a non-existent or mistyped form field).

The framework comes in two flavors, one for front end development, targeted at generating HTML pages and processing form requests, and one for REST based back end development, providing a transparent JSON/REST based RPC mechanism. Both of these components share the same basis and use the same approach to map class methods to routes of the URLRouter.

For the usual case, both, the HTTP method and the matching path are inferred from the class method's name. In particular, the name is mapped to a certain HTTP method according to its prefix and the rest of the name is converted to a path name in a configurable style. On top of that, it is possible to use the @path and @method attributes to override these defaults.

HTTP method Corresponding prefixes
GET get, query, getter @property methods, methods named index
POST post, create, add, no prefix
PUT put, set, setter @property methods
PATCH patch, update
DELETE delete, erase, remove

Web interface generator

The front end web interface generator automatically maps incoming query or form fields to method parameters, performing the necessary conversions and validations automatically. On top of that, it offers several convenience functions for rendering Diet templates, handling sessions and performing redirects - without directly accessing the HTTPServerRequest or HTTPServerResponse object. This enables a completely statically checked and clean code style in most cases.

import vibe.http.router;
import vibe.http.server;
import vibe.web.web;

shared static this()
{
	auto router = new URLRouter;
	router.registerWebInterface(new WebInterface);

	auto settings = new HTTPServerSettings;
	settings.port = 8080;
	settings.sessionStore = new MemorySessionStore;
	listenHTTP(settings, router);
}

class WebInterface {
	private {
		// stored in the session store
		SessionVar!(bool, "authenticated") ms_authenticated;
	}

	// GET /
	void index()
	{
		bool authenticated = ms_authenticated;
		render!("index.dt", authenticated);
	}

	// POST /login  (username and password are automatically read as form fields)
	void postLogin(string username, string password)
	{
		enforceHTTP(username == "user" && password == "secret",
			HTTPStatus.forbidden, "Invalid user name or password.");
		ms_authenticated = true;
		redirect("/");
	}

	// POST /logout
	@method(HTTPMethod.POST) @path("logout")
	void postLogout()
	{
		ms_authenticated = false;
		terminateSession();
		redirect("/");
	}
}
Example: source/app.d
doctype 5
html
	head
		title Welcome
	body
		h1 Welcome

		- if (authenticated)
			form(action="logout", method="POST")
				button(type="submit") Log out
		- else
			h2 Log in
			form(action="login", method="POST")
				p User name:
					input(type="text", name="username")
				p Password:
					input(type="password", name="password")
				button(type="submit")
Example: views/index.dt

Localization

Using GNU gettext compatible .po translation files, it's possible to localize Diet templates at compile time. This just requires putting the translation files with the naming scheme <name>.<language>.po into a path that is registered in the "stringImportPaths" field of the dub.json. <language> must be a language identifier of the form en_US.

import vibe.web.web;

struct TranslationContext {
	alias languages = TypeTuple!("en_US", "de_DE");
	mixin translationModule!"example";
}

@translationContext!TranslationContext
class WebInterface {
	void index()
	{
		render!("index.dt");
	}
}
Example: source/app.d
doctype 5
html
	head
		title& Welcome
	body
		h1& Welcome

		p& home.welcome.text
Example: views/index.dt
msgid "Welcome"
msgstr "Welcome"

msgid "home.welcome-text"
msgstr "Hello, this is a translation example."
Example: translations/example.en_US.po
msgid "Welcome"
msgstr "Willkommen"

msgid "home.welcome-text"
msgstr "Hallo, dies ist ein Übersetzungs-Beispiel."
Example: translations/example.de_DE.po

REST interface generator

Similar to the browser oriented web interface generator, there is a machine communication oriented JSON/REST based interface generator. Method parameters are mapped to JSON fields and get serialized according to the usual serialization rules. In addition to the interface generator, there is also a client generator, which automatically implements a class that emits the proper REST calls to access the REST interface.

import vibe.core.core;
import vibe.core.log;
import vibe.http.router;
import vibe.http.server;
import vibe.web.rest;

struct Weather {
	string text;
	double temperature; // °C
}

interface MyAPI {
	// GET /weather -> responds {"text": "...", "temperature": ...}
	Weather getWeather();

	// PUT /location -> accepts {"location": "..."}
	@property void location(string location);

	// GET /location -> responds "..."
	@property string location();
}

class MyAPIImplementation : MyAPI {
	private {
		string m_location;
	}

	Weather getWeather() { return Weather("sunny", 25); }

	@property void location(string location) { m_location = location; }
	@property string location() { return m_location; }
}

shared static this()
{
	auto router = new URLRouter;
	router.registerRestInterface(new MyAPIImplementation);

	auto settings = new HTTPServerSettings;
	settings.port = 8080;
	listenHTTP(settings, router);

	// create a client to talk to the API implementation over the REST interface
	runTask({
		auto client = new RestInterfaceClient!MyAPI("http://127.0.0.1:8080/");
		auto weather = client.getWeather();
		logInfo("Weather: %s, %s °C", weather.text, weather.temperature);
		client.location = "Paris";
		logInfo("Location: %s", client.location);
	});
}

Database support

MongoDB

A native MongoDB driver is part of the distribution supporting the standard set of database operations. Data is exchanged using the Bson struct.

For an comprehensive documentation of MongoDB's operations see the MongoDB manual . The API reference contains the documentation for the driver.

import vibe.d;

MongoClient client;

void test()
{
	auto coll = client.getCollection("test.collection");
	foreach (doc; coll.find(["name": "Peter"]))
		logInfo("Found entry: %s", doc.toJson());
}

shared static this()
{
	client = connectMongoDB("127.0.0.1");
}
Example: Finding entries in a MongoDB

Redis

A rudimentary client for the structured storage server Redis is included in Vibe. Commands and operations on Redis data types are implemented as instance methods of the client. The methods are named after their corresponding Redis command documented in the command reference .

Raw TCP

Low level TCP connections are handled using the TCPConnection class, which implements the Stream interface. Connections can be established by either listening on a specific port for incoming connections or by actively connecting to a remote server.

Server

Listening for TCP connections is done using the listenTCP function. An implementation of a very simple echo server could look like this:

import vibe.d;

shared static this()
{
	listenTCP(7, (conn){ conn.write(conn) });
}

Calling listenTCP like this will listen on all local network devices. To listen only on a specific device, the bind address can be given as an additional parameter:

import vibe.d;

shared static this()
{
	listenTCP(7, conn => conn.write(conn), "127.0.0.1");
}

The address can be given as an IP address string (IPv4 or IPv6 format), as an uint for numeric IPv4 addresses or as a in6_addr for numeric IPv6 addresses.

Client

Connecting to a TCP server is done with the connectTCP function. The following example gets the current time using the Daytime Protocol .

import vibe.d;

shared static this()
{
	auto conn = connectTCP("time-b.timefreq.bldrdoc.gov", 13);
	logInfo("The time is: %s", cast(string)conn.readAll());
}

Raw UDP

In addition to stream based TCP connections, packet based UDP communication is also supported. To open a UDP socket, use the listenUDP function with the appropriate port. It will return a UDPConnection instance, which can then be used to send and receive individual packets.

import vibe.appmain;
import vibe.core.core;
import vibe.core.log;
import vibe.core.net;

import core.time;


shared static this()
{
	runTask({
		auto udp_listener = listenUDP(1234);
		while (true) {
			auto pack = udp_listener.recv();
			logInfo("Got packet: %s", cast(string)pack);
		}
	});

	runTask({
		auto udp_sender = listenUDP(0);
		udp_sender.connect("127.0.0.1", 1234);
		while (true) {
			sleep(dur!"msecs"(500));
			logInfo("Sending packet...");
			udp_sender.send(cast(ubyte[])"Hello, World!");
		}
	});
}
Example: Simple UDP based communication on localhost

Advanced topics

Publishing on the DUB registry

The DUB registry contains packages that use the same package management as vibe.d. Many of them extend and supplement vibe.d's functionality. The packages are automatically downloaded if the corresponding entry in the "dependencies" section of the project's dub.json file is present.

If you have written an extension library yourself, you can register it in the DUB registry so others can easily make use of it. For this to work, you will first have to write a proper dub.json, which has to reside in the root directory of your project (the project should adhere to the structure mentioned in the first steps). The project currently also has to be hosted either on GitHub or on BitBucket. See http://core.dlang.org/publish for more information.

{
	"name": "vibelog",
	"description": "A light-weight embeddable blog implementation",
	"homepage": "https://github.com/rejectedsoftware/vibelog",
	"license": "AGPL-3.0",
	"authors": [
		"Sönke Ludwig"
	],
	"dependencies": {
		"vibe-d": "~master"
	}
}
Example: A dub.json file suitable for registration as a DUB package.

You can then register your project on registry.vibed.org and as soon as you then add a new git tag with the name matching the current version prefixed with "v" ("v0.0.1" for the example here), it should appear there. Note that the update can take up to 30 minutes. Anyone can then add an entry similar to the following to their project to make use of the library:

{
	...
	"dependencies": {
		"vibelog": ">=0.0.1"
	}
}
Example: Corresponding dependency to use the published DUB package.

The main function

To simplify development of server-like applications which are command line based and run an event loop, vibe.d includes a default application entry point (main()). To use it, simply define the version VibeDefaultMain when building the project. To do this, simply add this entry to your package description file: "versions": ["VibeDefaultMain"]

When writing a client application, maybe something like wget, it may not be desirable to start an explicit event loop, but instead exit right after the work is done. Some applications might also need more control of how vibe is initialized or when the event loop is started. To support such usage scenarios, you can implement your own program entry function.

Note: Projects that have their own program entry point currently also need to define a VibeCustomMain version instead of VibeDefaultMain. This requirement will be lifted in a later version of vibe.d.

import vibe.vibe;

void main()
{
	// returns false if a help screen has been requested and displayed (--help)
	if (!finalizeCommandLineOptions())
		return;
	lowerPrivileges();
	runEventLoop();
}
Example: Simple custom main function that runs an event loop.
import vibe.vibe;

void main()
{
	auto f = openFile("test.html", FileMode.CreateTrunc);
	f.write(download("http://google.com/"));
}
Example: Client application that downloads a document

Privilege lowering

For server applications it may be desirable to start the application as root so that it can listen on privileged ports or open system log files for writing. After these setup tasks, such applications impose a security risk because a hole in the application that allows to somehow execute commands on the server will give the attacker full access to the system. For this reason vibe supports privilege lowering, where the user id and group id of the process are changed to an unprivileged user after the setup process, right before the event loop is started.

Privilege lowering can be configured in the configuration file /etc/vibe/vibe.conf (on Windows vibe.conf must be in the application's root directory instead). The two fields "user" and "group" have to be set to the the name or numeric ID of the unprivileged user/group.

{
	"user": "www-data",
	"group": "www-data"
}
Example vibe.conf file

Compile time configuration

There is a number of features that can be controlled via "versions". To defined a certain version, add the corresponding entry to the "versions" in your dub.json:

{
	...
	"versions": ["VibeManualMemoryManagement"],
	...
}
Declaring a "version" in dub.json
Version constant Meaning
VibeCustomMain Tells vibe.d to not use its own predefined main() function. This is mutually exclusive to VibeDefaultMain.
VibeDefaultMain Tells vibe.d to use its own predefined main() function. This is mutually exclusive to VibeCustomMain.
VibeDisableCommandLineParsing Disables automatic parsing of command line options passed to the application, such as "--help" or "--verbose".
VibeManualMemoryManagement Performs a number of operations using manual memory management instead of the garbage collector, most notably in the HTTP server. This can lead to considerable performance improvements, but also makes certain operations unsafe and thus may open up the possibility for certain attacks. Especially any code needs to make sure to not escape any data (including string data) from HTTPServerRequest.
VibeIdleCollect Perform manual garbage collection runs when the application is idle for more than two seconds.
VibePartialAutoExit Partial workaround for issue #212 - exits the event loop after no mode file and socket based events exist. Note that this will exit the event loop, even if other kinds of events, such as timers, are still active. It is currently recommended to use exitEventLoop instead.
VibeOldSerialization Forces the use of the old (pre 0.7.18) serialization code for JSON and BSON. This may be necessary under rare circumstances, where the new code has different semantics.
VibeJsonFieldNames Stores field names for JSON values for better error messages. This will use a few bytes of additional memory per Json value.
JsonLineNumbers Stores line numbers for each JSON value parsed from a JSON string for improved error messages. This will store an additional int for each Json value.
VibeDebugCatchAll Enables catching of exceptions that derive from Error. This can be useful during application development to get useful error information while keeping the application running, but can generally be dangerous, because the application may be left in a bad state after an Error has been thrown.
VibeNoSSL Does not use OpenSSL to provide TLS/SSL functionality. The vibe.stream.ssl module will be dysfunctional.
VibeUseOldOpenSSL Disables the use of features introduced in OpenSSL 1.0.1 or later, most notably ECDH curve selection. Use this to link against old versions of the OpenSSL library.
VibePragmaLib Uses a pragma(lib) to link against OpenSSL and libevent. This is only useful for building without DUB.
VibeLibevDriver Enables the libev based event driver (not fully functional). Do not use this version directly, but select the "libev" configuration in your dub.json instead.
VibeLibeventDriver Enables the libevent based event driver (currently the default). Do not use this version directly, but select the "libevent" configuration in your dub.json instead.
VibeWin32Driver Enables the Win32 based event driver (provides GUI message support on Windows). Do not use this version directly, but select the "win32" configuration in your dub.json instead.
VibeWinrtDriver Enables the WinRT based event driver (not yet implemented). Do not use this version directly, but select the "winrt" configuration in your dub.json instead.

Handling segmentation-faults on Linux

While access violations on Windows usually trigger an exception with a convenient stack trace, on other operating systems D applications just terminate and possibly cause a core dump file to be written. However, there is a module hidden in the D runtime that enables such a stack trace also on Linux. Simply run the following code at program initialization:

import etc.linux.memoryerror;
static if (is(typeof(registerMemoryErrorHandler)))
	registerMemoryErrorHandler();
currently works only on Linux x86 and x86-64.