musings on pure websites

Posted 2022-07-25

cgi.d's dispatcher system can turn normal-looking D functions into websites. But what about database mutations?

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

What Adam is working on

arsd 10.9 probably gonna be tagged soon, includes a few minor compat fixes with dmd master, new http2.d redirection controls and bug fixes, Windows .ico file reading support, among other things. I've been busy with other projects so been a while since the last tag and will probably be another while until the next one.

Pure websites

By pure, I mean as in the D keyword, and by websites, I mean the kind of things you do on the web, whether through traditional html sites or json apis or whatever else - get data, submit forms, etc.

cgi.d can currently do things like:

import arsd.cgi;

// this is everything stored in the session
struct SessionData {
	string user;
}

struct User {
	string name;

	// this means the system and get it automatically as a parameter...
	static User getAutomaticallyForCgi(Cgi cgi) {
		auto data = cgi.getSessionObject!SessionData;
		if(data.user)
			return User(data.user);
		else
			return User("Not logged in");
		// of course, you might also check an Authorization
		// header or whatever too.
	}
}

class Site : WebObject {
	// so this currentUser is not provided by the user per se, it
	// comes out of the session data through the above code
	string hello(User currentUser) {
		return "Hello, " ~ currentUser.name;
	}
}
mixin DispatcherMain!("/".serveApi!Site);

I like a lot about this. It lets you focus on writing normal looking functions moreso than web glue, you can see what its dependencies are, and you can more easily reuse functions internally, in unit tests, or in calls from other functions.

I also dislike a bit about this - the parameter lists can get a bit long when you are making several calls. Of course, it is possible to group things into helper structs or member variables, but that's got its own trade offs, like making it less obvious what the individual function needs. Anyway, I'm digressing.

But, this only does data retrieval. What if you want to write a login function?

Right now, I'd have to take the Cgi object as a parameter (which the system knows is special and passes the right one along) and set the session through there. It gets the job done - and you can even pass mock objects for unit testing still - but it doesn't feel quite as purely pure.

The return values can currently be packed up with multiple response types, and the Presenter object extracts it and does the right thing. For example, you might return a redirection object that is turned into a http 303. Or some data, with a runtime if check. I could possibly add other commands to this, like session and database updates. Heck, the current Presenter system could let you define your own, but that'd be abusing the system!

I believe this is the functional programming concept called a monad, but as many of you know, I dropped out of school, so I don't know all the fancy vocab and jargon.

What would be cool about this though is you can do dry runs easily, just returning the update object without running it, which you can show in the browser ui as well as running in unittests. Additionally, it might know it is an insert and return different http codes automatically too. (One awkward thing there is I like to POST then redirect for the browser, but you want to 201 Created and set Location in rest... and I think you can do both by doing a meta refresh header and i experimented with that a bit and might automate it when you return a newly created object. but im digressing again)

So you'd return a command to create the object instead of the object itself, then it is run and returned depending on the settings. Maybe something like:

MultipleResponses!(NewSession, AuthDenied) login(string username, string password) {
    if(auto user = check_login(username, password)) {
        return typeof(return)(NewSession("user", user));
    } else {
        return typeof(return)(AuthDenied());
    }
}

(yes, that typeof(return) is annoying, i wish we had implicit construction in at least limited places)

Realistically, it'd probably need to do a heterogeneous array of update commands, and be somehow user-defined so it can use other databases etc., and possibly still go through template systems for final presentation to html or something. It'd be pretty complicated I suspect....

Not sure how I really feel about this, but part of me wants to spend more time working on it. I really do like being able to get data through the parameters, so setting through return value might be interesting too.