Web

June 6, 2026 ยท View on GitHub

Net HTTP Request/Response utilities.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  theme := request.query("theme") or { "dark" }

  return web.Response.html(content: "<h1>Hello world (theme: ${theme})</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

About

This package helps you get more out of the base net.http Request and Response structs by offering shortcut utility methods for commonly used data such as client IP, checking if a Mime type is accepted by client, etc...

This packages creates from and returns net.http Request and Response structs for full compatibility with the built-in net.http Server struct.

Features

  • Compatible with net.http Request/Response structs
  • Request and Response utility functions to speed up development

Installation

Using V installer

Install the required packages:

v install khalyomede.web

Manual installation

Examples

Get the client IP

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  ip := request.ip()

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

Note that the return type will be an Address union type (which can be an Ipv4 or Ipv6 struct). See khalyomede/ip for more information.

Important notice: For the moment, this method does not handle Proxies. For example, if your web server is behind a Cloudflare or Nginx Proxy, this method will always return the Proxy IP (and not the client IP). Instead, these proxy return the client IP in a specific header (which may change, hence the fact that for simplciity this method only returns the client IP in non-proxy situation).

For the moment, it is up to you to get the client IP using the proper Request.header() method.

In a future update, this package will allow you to declare a proxy configuration, such as calling Request.ip() will return the client IP as expect.

back to examples

Get the path

[!INFO] Install khalyomede/url to use this method.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  url := request.url // "/contact?theme=dark#whatsapp"
  path := request.path() // "/contact"

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get list of accepted content type

This will return a list of Mime enum. See khalyomede/mime for more information.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := Request.from_base(request)

  accepted_content_types := request.accepted_content_types()

  for accepted_content_type in accepted_content_types {
    // ...
  }

  return web.Response.text(content: "Hello world").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := Request.from_base(request)

  session := request.cookie("session") or { "" }

  return web.Response.html(content: "<h1>Hello world<h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all cookies

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := Request.from_base(request)

  for key, value in request.cookies() {
    // ...
  }

  return web.Response.html(content: "<h1>Hello world<h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get a query string by key

[!INFO] Install khalyomede/url to use this method.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  theme := request.query("theme") or { "dark" }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

Request.query() returns a QueryNotFound error when the query is not found, so you can handle this specific case.

module main

import khalyomede.web { QueryNotFound }
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  theme := match request.query("theme") or {
    match err {
      QueryNotFound { "light" }
      else { "none" }
    }
  }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

This package uses khalyomede/url to parse the path and access the query. You should check the errors to exhaustively match them.

module main

import khalyomede.web { QueryNotFound }
import khalyomede.url { BadlyEncodedQuery }
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  theme := match request.query("theme") or {
    match err {
      QueryNotFound { "light" }
      BadlyEncodedQuery { "dark" }
      else { "none" }
    }
  }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

You can find the full list on the parse URL method documentation of khalyomede/url.

back to examples

Get all queries

[!INFO] Install khalyomede/url to use this method.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  for key, value in request.queries() {
    // ...
  }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get an uploaded file

Given you have an HTML form with multipart/form-data enctype, this package will let you get a single file matching the same name attribute as specified in your <input type="file"> in your HTML.

The method web.Request.file(name) will return a V built-in FileData.

This method will return the first file. If you allow multiple files, use the web.Request.files(name) instead.

module main

import net.http { Server, Handler, Request, Response }
import os

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  // GET /upload-profile-picture
  if request.path() == '/upload-profile-picture' {
    return web.Response.html(content: "
      <form method="POST" action="validate-profile-picture" enctype="multipart/form-data">
        <input type="file" name="profilePicture" />
        <button type="submit">upload</button>
      </form>
    ")
  }

  // GET /validate-upload
  file := request.file('profilePicture') or {
    return web.Response.html(
      content: "<h1>Unable to download profile picture</h1>"
      status: .internal_server_error
    )
  }

  os.write_file(file.filename, file.data) or {
    return web.Response.html(
      content: "<h1>Unable to save file on the server.</h1>",
      status: .internal_server_error
    )
  }

  return web.Response.html(content: "<h1>Profile picture updated</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all files for a key

If your HTML form accepts multiples files for a given <input type="file" name="documents" multiple />, use the web.Request.files('documents') method.

If you only need a single file (the first one), use the web.Request.file() method instead.

module main

import net.http { Server, Handler, Request, Response }
import os

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  // GET /upload-documents
  if request.path() == '/upload-documents' {
    return web.Response.html(content: "
      <form method="POST" action="validate-documents" enctype="multipart/form-data">
        <input type="file" name="documents" multiple />
        <button type="submit">upload</button>
      </form>
    ")
  }

  // GET /validate-documents
  files := request.files('documents') or {
    return web.Response.html(
      content: "<h1>Unable to download documents</h1>"
      status: .internal_server_error
    )
  }

  for file in files {
    os.write_file(file.filename, file.data) or {
      return web.Response.html(
        content: "<h1>Unable to save document on the server.</h1>",
        status: .internal_server_error
      )
    }
  }

  return web.Response.html(content: "<h1>Documents updated</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all files

When you do not care from which <input type="file" /> the files are coming, and you want to get every files uploaded across the whole HTML forms, use the web.Request.all_files() method.

module main

import net.http { Server, Handler, Request, Response }
import os

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  // GET /upload-documents
  if request.path() == '/upload-documents' {
    return web.Response.html(content: "
      <form method="POST" action="validate-documents" enctype="multipart/form-data">
        <input type="file" name="documents" multiple />
        <button type="submit">upload</button>
      </form>
    ")
  }

  // GET /validate-documents
  files := request.all_files()

  for file in files {
    os.write_file(file.filename, file.data) or {
      return web.Response.html(
        content: "<h1>Unable to save document on the server.</h1>",
        status: .internal_server_error
      )
    }
  }

  return web.Response.html(content: "<h1>Documents updated</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get a body by key

module main

import net.http { Server, Handler, Request, Response }
import khalyomede.mime { Mime }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  email := request.body("email") or { "" }

  return web.Response.html(content: "<h1>Settings updated</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all body

module main

import net.http { Server, Handler, Request, Response }
import khalyomede.mime { Mime }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  for key, value in request.bodies() {
    // ...
  }

  return web.Response.html(content: "<h1>Settings updated</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get a specific header

module main

import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  client_hints := request.header("Accept-CH")

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get the bearer token

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  bearer_token := request.bearer_token() or { "" }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get basic auth username password

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  username, password := request.basic_auth() or { "", "" }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get the user agent

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  user_agent := request.user_agent() or { "Google Chrome v110" }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get the first support language

The lang will be a Lang enum. See khalyomede/lang for more information.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  language := request.language() or { Lang.en }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all supported languages

The list will be a Lang enum. See khalyomede/lang for more information.

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }
import khalyomede.lang { Lang }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  languages := request.languages() or { [Lang.en] }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Get all headers

module main

import net.http { Server, Handler, Request, Response, Status }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  request := web.Request.from_base(base_request)

  for key, value in request.headers() {
    // ...
  }

  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Return basic response

This example requires the Mime enum. See khalyomede/mime for more information.

module main

import net.http { Server, Handler, Request, Response, CommonHeader }
import khalyomede.mime { Mime }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  return khalyomede
    .http
    .Response(
      content: "<h1>Hello world</h1>"
      headers: {
        CommonHeader.content_type.str(): Mime.text_html.str()
      }
    )
    .to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Return HTML response

module main

import khalyomede.web
import net.http { Server, Handler, Request, Response }

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  return web.Response.html(content: "<h1>Hello world</h1>").to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples

Return JSON response

module main

import json
import net.http { Server, Handler, Request, Response }

struct JsonResponse {
  message string
}

struct RequestHandler implements Handler {}

fn (request_handler RequestHandler) handle(base_request Request) Response {
  json_response := json.encode(JsonResponse{
    message: "hello world"
  })

  return web.Response.json(content: json_response).to_base()
}

fn main() {
  mut server := Server{
    addr: "localhost:80"
    handler: RequestHandler{}
  }

  server.listen_and_serve()
}

back to examples