Web Development with veb

veb is V's built-in web framework (the successor to vweb, renamed and reworked in 0.4.x). It compiles routes and templates at compile time.

35.1 Minimal App

module main

import veb

pub struct Context {
	veb.Context
pub mut:
	user_id int
}

pub struct App {
	veb.StaticHandler
pub mut:
	visits int
}

@['/']
pub fn (mut app App) index(mut ctx Context) veb.Result {
	app.visits++
	return ctx.text('Hello, veb! Visits: ${app.visits}')
}

@['/users/:id'; get]
pub fn (app &App) user(mut ctx Context, id int) veb.Result {
	return ctx.json({'id': id})
}

@['/users'; post]
pub fn (app &App) create_user(mut ctx Context) veb.Result {
	name := ctx.form['name'] or { return ctx.request_error('name required') }
	return ctx.json({'created': name})
}

@['/page']
pub fn (app &App) page(mut ctx Context) veb.Result {
	title := 'My Page'
	items := ['a', 'b', 'c']
	return $veb.html()           // renders templates/page.html with local variables
}

fn main() {
	mut app := &App{}
	app.handle_static('static', true) or {}
	veb.run[App, Context](mut app, 8080)
}

35.2 Templates (templates/page.html)

Templates are compiled into the binary and type-checked.

<h1>@title</h1>
<ul>
  @for item in items
    <li>@item</li>
  @end
</ul>
@if items.len == 0
  <p>No items</p>
@end
@include 'partials/footer.html'
@css '/static/style.css'

35.3 Middleware, Sessions, Auth

Built-in pieces: veb.auth, veb.csrf, veb.sse (server-sent events), cookies (ctx.set_cookie, ctx.get_cookie), redirects, file responses, CORS middleware (veb.cors), and static files.

Combine with the ORM for full-stack apps (see gitly for a real-world example).

Deploy as a single binary behind nginx/Caddy, or in Docker (FROM thevlang/vlang:alpine for builds, then a scratch/alpine runtime image).

pub fn (app &App) before_request(mut ctx Context) {         // runs before each request
	println('${ctx.req.method} ${ctx.req.url}')
}
// veb.Middleware[Context]: app.use(handler: auth_middleware)
fn auth_middleware(mut ctx Context) bool {
	token := ctx.get_header(.authorization) or { '' }
	if token == '' {
		ctx.res.set_status(.unauthorized)
		ctx.text('unauthorized')
		return false
	}
	return true
}