aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/tutorials/building_a_simple_web_blog_with_vweb
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/tutorials/building_a_simple_web_blog_with_vweb')
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/README.md430
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore1
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v13
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite17
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v80
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html16
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html13
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.pngbin0 -> 105501 bytes
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.pngbin0 -> 110081 bytes
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello.pngbin0 -> 92436 bytes
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.pngbin0 -> 98295 bytes
-rw-r--r--v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/time.pngbin0 -> 91776 bytes
12 files changed, 570 insertions, 0 deletions
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/README.md b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/README.md
new file mode 100644
index 0000000..06804a5
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/README.md
@@ -0,0 +1,430 @@
+## Building a 150 KB web blog in V with 0 dependencies
+
+Hello,
+
+In this guide, we'll build a simple web blog in V.
+
+The benefits of using V for web:
+- A safe, fast, language with the development agility of Python or Ruby and
+the performance of C.
+- Zero dependencies: everything you need for web development comes with the language
+in a 1 MB package.
+- Very small resulting binaries: the blog we'll create in this tutorial is about 150 KB.
+- Easy deployments: a single binary file that even includes the precompiled templates.
+- Runs on the cheapest hardware with minimum footprint: for most apps a $3 instance
+is enough.
+- Fast development without any boilerplate.
+
+*Please note that V and Vweb are at a very early stage and are changing rapidly.*
+
+The code is available <a href='https://github.com/vlang/v/tree/master/tutorials/code/blog'>here</a>.
+
+
+### Installing V
+
+```
+wget https://github.com/vlang/v/releases/latest/download/v_linux.zip
+unzip v_linux.zip
+cd v
+sudo ./v symlink
+```
+
+Now V should be globally available on your system.
+
+> On macOS use `v_macos.zip`, on Windows - `v_windows.zip`.
+If you use a BSD system, Solaris, Android, or simply want to install V
+from source, follow the simple instructions here:
+https://github.com/vlang/v#installing-v-from-source
+
+
+### Install SQLite development dependency
+
+If you don't have it already installed, look at the
+[`sqlite` README](../../vlib/sqlite/README.md) for instructions.
+
+
+### Creating a new Vweb project
+
+V projects can be created anywhere and don't need to have a certain structure:
+
+```bash
+mkdir blog
+v init
+```
+
+First, let's create a simple hello world website:
+
+```v oksyntax
+// blog.v
+module main
+
+import vweb
+
+struct App {
+ vweb.Context
+}
+
+fn main() {
+ app := App{}
+ vweb.run(app, 8081)
+}
+
+['/index']
+pub fn (mut app App) index() vweb.Result {
+ return app.text('Hello world from vweb!')
+}
+```
+
+Run it with
+
+```bash
+v run blog.v
+```
+
+```
+Running a Vweb app on http://localhost:8081 ...
+```
+
+Vweb helpfully provided a link, open http://localhost:8081/ in your browser:
+
+<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png?raw=true">
+
+The `App` struct is an entry point of our web application. If you have experience
+with an MVC web framework, you can think of it as a controller. (Vweb is
+not an MVC framework however.) It embeds the vweb Context object, that's why we get access
+to methods like `.text()`.
+
+
+As you can see, there are no routing rules. The `index()` action handles the `/` request by default.
+Vweb often uses convention over configuration and adding a new action requires
+no routing rules either:
+
+```v oksyntax
+// blog.v
+import vweb
+import time
+
+fn (mut app App) time() vweb.Result {
+ return app.text(time.now().format())
+}
+```
+
+
+<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/time.png?raw=true">
+
+>You have to rebuild and restart the website every time you change the code.
+In the future, Vweb will detect changes and recompile the website in the background
+while it's running.
+
+The `.text(string)` method returns a plain text document with the provided
+text, which isn't frequently used in websites.
+
+
+### HTML View
+
+Let's return an HTML view instead. Create `index.html` in the same directory:
+
+```html
+<html>
+<head>
+ <title>V Blog</title>
+</head>
+<body>
+ <b>@message</b>
+ <br>
+ <img src='https://vlang.io/img/v-logo.png' width=100>
+</body>
+</html>
+```
+
+and update our `index()` action so that it returns the HTML view we just created:
+
+```v ignore
+// blog.v
+pub fn (mut app App) index() vweb.Result {
+ message := 'Hello, world from Vweb!'
+ return $vweb.html()
+}
+```
+
+<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png?raw=true">
+
+Good, now we have an actual HTML page.
+
+The V template language is similar to C#'s Razor: `@message` prints the value
+of `message`.
+
+You may notice something unusual: the `message` variable created in the `index()`
+action is automatically available in the view.
+
+It's another feature of Vweb to reduce the boilerplate in your web apps.
+No need to create view models just to pass data, or use an unsafe and untyped
+alternative, like C#'s `ViewBag["message"]`.
+
+Making all action variables available in the view may seem crazy,
+but V is a language with pure functions by default, and you won't be able
+to modify any data from a view. `<b>@foo.bar()</b>` will only work if the `bar()` method
+doesn't modify `foo`.
+
+The HTML template is compiled to V during the compilation of the website,
+that's done by the `$vweb.html()` line.
+(`$` always means compile time actions in V.) offering the following benefits:
+
+- Great performance, since the templates don't need to be compiled
+on every request, like in almost every major web framework.
+
+- Easier deployment, since all your HTML templates are compiled
+into a single binary file together with the web application itself.
+
+- All errors in the templates are guaranteed to be caught during compilation.
+
+
+### Fetching data with V ORM
+
+Now let's display some articles!
+
+We'll be using V's builtin ORM and a SQLite database.
+(V ORM will also support MySQL, Postgre, and SQL Server soon.)
+
+
+Add a SQLite handle to `App`:
+
+```v oksyntax
+// blog.v
+import sqlite
+import vweb
+
+struct App {
+ vweb.Context
+pub mut:
+ db sqlite.DB
+}
+```
+
+
+
+In `fn main()` we'll connect to a database.
+Code in the `main()` function is run only once during app's startup, so we are going
+to have one DB connection for all requests. This improves the performance of the web application,
+since a DB connection doesn't have to be set up for each request.
+
+
+```v oksyntax
+// blog.v
+fn main() {
+ mut app := App{
+ db: sqlite.connect(':memory:') or { panic(err) }
+ }
+ sql app.db {
+ create table Article
+ }
+
+ first_article := Article{
+ title: 'Hello, world!'
+ text: 'V is great.'
+ }
+
+ second_article := Article{
+ title: 'Second post.'
+ text: 'Hm... what should I write about?'
+ }
+
+ sql app.db {
+ insert first_article into Article
+ insert second_article into Article
+ }
+}
+```
+
+Create a new file `article.v`:
+
+```v oksyntax
+// article.v
+module main
+
+struct Article {
+ id int [primary; sql: serial]
+ title string
+ text string
+}
+
+pub fn (app &App) find_all_articles() []Article {
+ return sql app.db {
+ select from Article
+ }
+}
+```
+
+Notice that the `Article` structure conforms to the same structure and naming as
+the database table in the creation SQL statement. Also we need to add ORM decorators
+to our primary key to let it know that it is the primary key and it should auto-increment
+
+Let's fetch the articles in the `index()` action:
+
+```v ignore
+// blog.v
+pub fn (app App) index() vweb.Result {
+ articles := app.find_all_articles()
+ return $vweb.html()
+}
+```
+
+
+Finally, let's update our view:
+
+```html
+<body>
+ @for article in articles
+ <div>
+ <b>@article.title</b> <br>
+ @article.text
+ </div>
+ @end
+</body>
+```
+
+```bash
+v run .
+```
+
+<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png?raw=true">
+
+That was very simple, wasn't it?
+
+The built-in V ORM uses a syntax very similar to SQL. The queries are built with V.
+For example, if we only wanted to find articles with ids between 100 and 200, we'd do:
+
+```v oksyntax
+// article.v
+
+return sql app.db {
+ select from Article where id >= 100 && id <= 200
+}
+```
+
+Retrieving a single article is very simple:
+
+```v oksyntax
+// article.v
+pub fn (app &App) retrieve_article() ?Article {
+ return sql app.db {
+ select from Article limit 1
+ }
+}
+```
+
+V ORM uses V's optionals for single values, which is very useful, since
+bad queries will always be handled by the developer:
+
+```v ignore
+// article.v
+article := app.retrieve_article(10) or {
+ app.text('Article not found')
+ return
+}
+```
+
+
+### Adding new articles
+
+Create `new.html`:
+
+```html
+<html>
+<head>
+ <title>V Blog</title>
+</head>
+<body>
+ <form action='/new_article' method='post'>
+ <input type='text' placeholder='Title' name='title'> <br>
+ <textarea placeholder='Text' name='text'></textarea>
+ <input type='submit'>
+ </form>
+</body>
+</html>
+```
+
+```v ignore
+// article.v
+import vweb
+['/new_article'; post]
+pub fn (mut app App) new_article() vweb.Result {
+ title := app.form['title']
+ text := app.form['text']
+ if title == '' || text == '' {
+ return app.text('Empty text/title')
+ }
+ article := Article{
+ title: title
+ text: text
+ }
+ println(article)
+ sql app.db {
+ insert article into Article
+ }
+ return app.redirect('/')
+}
+```
+
+> Untyped `form['key']` is temporary. Very soon Vweb will accept query and form
+parameters via function arguments: `new_article(title, text string) {`.
+
+The decorator on our function tells vweb the path to our endpoint, `/new_article`,
+and that it is an HTTP POST type operation.
+
+We need to update `index.html` to add a link to the "new article" page:
+
+```html
+<a href='/new'>New article</a>
+```
+
+Next we need to add the HTML endpoint to our code like we did with `index.html`:
+
+```v ignore
+['/new']
+pub fn (mut app App) new() vweb.Result {
+ return $vweb.html()
+}
+```
+
+Re-running this code will now allow us to add new posts to our blog endpoint
+
+
+### JSON endpoints
+
+This tutorial used the traditional server-side rendering. If you prefer
+to render everything on the client or need an API, creating JSON endpoints
+in V is very simple:
+
+```v oksyntax
+// article.v
+import vweb
+import json
+
+['/articles'; get]
+pub fn (mut app App) articles() vweb.Result {
+ articles := app.find_all_articles()
+ return app.json(json.encode(articles))
+}
+```
+
+
+<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png?raw=true">
+
+### Persistent data
+If one wants to persist data they need to use a file instead of memory SQLite Database.
+Replace the db setup code with this instead:
+
+```
+db: sqlite.connect('blog.db') or { panic(err) }
+```
+
+As we can see it attempts to open a file in the current directory named `blog.db`.
+If the database file doesn't exist it will create it. The second command will
+create the table `Article` if none exists already. Now every time the
+app is run you will see the articles created from the previous executions
+
+
+To be continued...
+
+For an example of a more sophisticated web app written in V, check out Vorum: https://github.com/vlang/vorum
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore
new file mode 100644
index 0000000..2fdf6da
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore
@@ -0,0 +1 @@
+blog.db
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v
new file mode 100644
index 0000000..f127532
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v
@@ -0,0 +1,13 @@
+module main
+
+struct Article {
+ id int [primary; sql: serial]
+ title string
+ text string
+}
+
+pub fn (app &App) find_all_articles() []Article {
+ return sql app.db {
+ select from Article
+ }
+}
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite
new file mode 100644
index 0000000..c5956d6
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite
@@ -0,0 +1,17 @@
+drop table if exists Article;
+
+create table Article (
+ id integer primary key,
+ title text default "",
+ text text default ""
+);
+
+insert into Article (title, text) values (
+ "Hello, world!",
+ "V is great."
+);
+
+insert into Article (title, text) values (
+ "Second post.",
+ "Hm... what should I write about?"
+);
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v
new file mode 100644
index 0000000..d6284ce
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v
@@ -0,0 +1,80 @@
+module main
+
+import vweb
+import time
+import sqlite
+import json
+
+struct App {
+ vweb.Context
+pub mut:
+ db sqlite.DB
+ user_id string
+}
+
+fn main() {
+ mut app := App{
+ db: sqlite.connect('blog.db') or { panic(err) }
+ }
+ sql app.db {
+ create table Article
+ }
+ vweb.run(app, 8081)
+}
+
+/*
+pub fn (mut app App) index_text() vweb.Result {
+ app.vweb.text('Hello, world from vweb!')
+ return vweb.Result{}
+}
+
+pub fn (app &App) index_html() vweb.Result {
+ message := 'Hello, world from Vweb!'
+ return $vweb.html()
+}
+*/
+['/index']
+pub fn (app &App) index() vweb.Result {
+ articles := app.find_all_articles()
+ return $vweb.html()
+}
+
+pub fn (mut app App) before_request() {
+ app.user_id = app.get_cookie('id') or { '0' }
+}
+
+['/new']
+pub fn (mut app App) new() vweb.Result {
+ return $vweb.html()
+}
+
+['/new_article'; post]
+pub fn (mut app App) new_article() vweb.Result {
+ title := app.form['title']
+ text := app.form['text']
+ if title == '' || text == '' {
+ return app.text('Empty text/title')
+ }
+ article := Article{
+ title: title
+ text: text
+ }
+ println('posting article')
+ println(article)
+ sql app.db {
+ insert article into Article
+ }
+
+ return app.redirect('/')
+}
+
+['/articles'; get]
+pub fn (mut app App) articles() vweb.Result {
+ articles := app.find_all_articles()
+ json_result := json.encode(articles)
+ return app.json(json_result)
+}
+
+fn (mut app App) time() {
+ app.text(time.now().format())
+}
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html
new file mode 100644
index 0000000..399b267
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html
@@ -0,0 +1,16 @@
+<html>
+<header>
+ <title>V Blog</title>
+</header>
+<body>
+ user id = @app.user_id <br>
+ @for article in articles
+ <div>
+ <b>@article.title</b> <br>
+ @article.text
+ </div>
+ <br>
+ @end
+ <a href='/new'>New article</a>
+</body>
+</html>
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html
new file mode 100644
index 0000000..04ca000
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html
@@ -0,0 +1,13 @@
+<html>
+<header>
+ <title>V Blog</title>
+</header>
+<body>
+ <form action='/new_article' method='post'>
+ <input type='text' placeholder='Title' name='title'> <br>
+ <textarea placeholder='Text' name='text'></textarea>
+ <input type='submit'>
+ </form>
+</body>
+</html>
+
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png
new file mode 100644
index 0000000..2e17732
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png
Binary files differ
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png
new file mode 100644
index 0000000..dd95810
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png
Binary files differ
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png
new file mode 100644
index 0000000..aa45962
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png
Binary files differ
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png
new file mode 100644
index 0000000..a6d2195
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png
Binary files differ
diff --git a/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/time.png b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/time.png
new file mode 100644
index 0000000..62c372e
--- /dev/null
+++ b/v_windows/v/tutorials/building_a_simple_web_blog_with_vweb/img/time.png
Binary files differ