Show HN: I built a web framework in C

github.com

410 points by ashtonjamesd 4 days ago


faxmeyourcode - 4 days ago

This is some of the cleanest, modern looking, beautiful C code I've seen in a while. I know it's not the kernel, and there's probably good reasons for lots of #ifdef conditionals, random underscored types, etc in bigger projects, but this is actually a great learning piece to teach folks the beauty of C.

I've also never seen tests written this way in C. Great work.

C was the first programming language I learned when I was still in middle/high school, raising the family PC out of the grave by installing free software - which I learned was mostly built in C. I never had many options for coursework in compsci until I was in college, where we did data structures and algorithms in C++, so I had a leg up as I'd already understood pointers. :-)

Happy to see C appreciated for what it is, a very clean and nice/simple language if you stay away from some of the nuts and bolts. Of course, the accessibility of the underlying nuts and bolts is one of the reasons for using C, so there's a balance.

codegeek - 4 days ago

People, stop trying to be so serious and nitpick this project. This is a great example of an actual HN worthy share. Someone built a cool project and explored the possibilities with C. This is not something we need to analyze with "oh can it replace PHP" etc.

Good job OP. Now if you can add HTML templating, this may become a complete framework :)

hgs3 - 4 days ago

The code is very readable and well organized. My only major critique is that there's very little error checking, e.g. there are many calls to snprintf and malloc without checking the result. There is also an unused loop here [1].

As an aside, I don't see any support for parallelization. That's fine for an initial implementation, but web servers do benefit from threading off requests. If you go that route (pun intended) you might consider using something like libuv [2].

[1] https://github.com/ashtonjamesd/lavandula/blob/51d86a284dc7d...

[2] https://github.com/libuv/libuv

sroerick - 4 days ago

Hi, I think this is great. I've really enjoyed working with Jetzig, which is sort of similar.

I also love the BSD C CGI Postgres stack. I'm just a CRUDmonkey with mostly python skills, so getting to explore low language and memory concepts is a lot of fun for me.

People will whine and moan about how this is not practical, but as embedded devices become more ubiquitous I think a clear value add may actually emerge.

I've been playing with the pico calc, and if I was building something as a "mobile app" for that I would much rather reach for C for my framework code.

Cheers, great work

lubesGordi - 4 days ago

Well I don't know about others here, but I think its cool. If you can make the setup super readable and get the performance of C then why not? Especially now when you can get claude to write a bunch of the framework for you. Add in whatever you need whenever you need it and you automatically have a platform independent web framework that's no bigger than what you need and likely decently performant.

levkk - 4 days ago

That's awesome. With macros, you can go far and most modern web frameworks use whatever complex tools their language allows (like metaprogramming in Rails).

Mad props for building this. It's hard and it's fun!

As to other comments in the thread about the "why": why not. For the love of the craft.

mistivia - 4 days ago

It's very dangerous to write a http parser from scratch in C. This can be very vulnerable without rigorous testing. To get a useful web framework for production in C, I think it's a better idea to start from libmicrohttpd, libevent_http, or even fastcgi, which are battle-tested.

fallingmeat - 4 days ago

wow that’s a lot of HATE for a really well organized project with some great ideas. Killer job Ashton, you just built some skills they can’t take away from you.

201984 - 3 days ago

Very nice!

A couple of notes: you'll want to use non-blocking I/O and an event loop to prevent one slow client from locking up the whole server. You should also check for partial read and write calls, so that if a client sends a couple bytes at a time, you can buffer up their full response and still be able to respond to it. A fixed size buffer for requests isn't ideal either since POST requests can easily blow through your 4096 byte buffer.

You might also want to look into using an AF_INET6 socket. You can still accept IPv4 connections, but you'll also gain IPv6 basically for free, and in 2025, you really should support IPv6.

freetonik - 4 days ago

I hope you don't feel discouraged by some comments questioning the meaningfulness of this. It's a cool project, and you obviously put some thought into it. Congrats!

faichai - 3 days ago

Some unsolicited feedback:

I think the appRoute macro obfuscates the types and signatures, and introduces some unnecessary indirection. I would get rid of it.

Related, the AppContext type could be renamed RequestContext or ControllerContext or something as its App + HTTP Request + DB and not just the App.

Otherwise, I agree with other commenters that this is some of the cleanest C code I’ve seen in a while! Great effort!

p0w3n3d - 4 days ago

Great work! Thank you! That's what I've been looking for for a long time.

Still probably I'm going to continue learning golang in most situations, because that's where the money is (i.e. job offers), but I will create a hobby project based on your framework.

--- EDIT ---

> 5 hours ago

Ohh it's fresh. I almost smell the freshly baked buns with my mind

koito17 - 4 days ago

The README gets straight to the point and I really like that.

Additionally, the .env file parser is quite clean.

https://github.com/ashtonjamesd/lavandula/blob/main/src/dote...

However, it doesn't seem that the parser supports comments. I guess a "good first issue" for anyone interested in contributing would be extending the `skipWhitespace` function to detect `#` tokens and skip the rest of the line when present.

Would also need to handle edge cases like env vars having values containing `#` tokens inside (but these will be quoted, so it's probably not too tricky to handle.)

jll29 - 3 days ago

Thanks for sharing; small is beautiful. A couple of points of feedback:

- check return value from malloc();

- consider using your own arena allocator (which gets a larger block of memory with a one-time call of malloc, then calls an in-process allocator that assigns part of that block);

- use a library prefix e.g. Lavandula_ before API functions like get() or runApp() to avoid name collisions.

- The JSON function is not spec-compliant; why not use an existing library? (I understand external dependecies may introduce unwanted bloat, but in this case, there are many compact and efficient options.)

elevation - 4 days ago

I have considered porting a couple production apps from python to C; at this stage in their lifecycle they would benefit more from C's execution speed than from python's development speed.

Your work is a nice reference, it is neat to see someone else working in this space!

dboon - 4 days ago

C is really, really ripe for tooling and modern libraries. There are a lot of great ones already that don’t resemble what I’ll call university C in the slightest (i.e. the C most of us remember writing; awful, bug filled, segfaulting)

I’ve been building out my C standard library replacement in earnest for a little while. If you like this framework, check it out.

https://github.com/tspader/sp

jacquesm - 4 days ago

If you're going to use local allocation of short lived buffers then don't use malloc but use alloca. That's much cleaner.

http.c around line 398, that looks wrong.

coreyp_1 - 4 days ago

I'm wanting to do the same thing. I've also already written a language (in C) to generate HTML (a template language), so these two go hand-in-hand!

notepad0x90 - 3 days ago

OP, Turn on CodeQL workflows in GH, you'll thank me later! Even if your code is bug free, it makes it easier to find out when common mistakes are introduced later on. Especially since you're asking for help with features and others will be doing PRs.

Great project. I remember using mongoose a while back that's also written in C. Personally, the more library independent and self-sufficient it is, the more I'm likely to use it. Like, if you can even avoid using the stdlib! Even thought that sounds crazy (but a server in C is a bit crazy anyways?). The more standalone it is, the more transformable and embeddable it can be.

OneLessThing - 4 days ago

There is a heap overflow in the http parser. Should I spoil it or let people find it on their own?

lebimas - 3 days ago

Real question, how did you learn how to code this well? I found your LinkedIn from your Github, and as someone who is just committing to becoming a SWE at 26, having learned a bit of Python and Matlab in college, and a bit of Java in high school, yet never fully grasped it and thus avoided it for as long as possible, I'm impressed by people who have this caliber of abilities at such a young age. Are there any tips or bits of advice you (or anyone else on HN for that matter) would give to someone who really wants to be the best they can possibly be at coding?

orochimaaru - 4 days ago

This is very cool. I may take the same concepts you have and do this in rust and zig for fun and learning.

Yeah, I know those languages have a the frameworks but nothing really beats understanding something like doing it ground up on your own.

lordleft - 3 days ago

I am convinced that well written C code is more aesthetically pleasing than well written code in other languages. Great job and thanks for sharing.

kahlonel - 4 days ago

That's a great example of how to write C in 2025. Congrats and well done.

kopirgan - 3 days ago

I created something similar few months back, just to learn. Integrated with sqlite.

It is fun to try but I realised there is a huge Mt. Everest to climb to make it anything close to fit for use.

https://github.com/fullobug/gttp

OutputRiff - 4 days ago

The repo looks fantastic! I'd love to see a demo and didn't seen one readily available in the readme.

I had such a bad experience with GWT back in the Java days of my life that I've steered clear of any "server" language for web frameworks since. I'd love for that to change though. I definitely will be trying this out.

globalnode - 4 days ago

I like this, thanks for sharing. I recently did some work with a python web server using the basehttpserver and it was amazingly easy. Pythons even got built in tls support, would that be doable in your server? Its not that necessary with reverse proxies but its still nice for hobby projects.

sim7c00 - 4 days ago

really nicely written. inrespect this is maybe known / unneeded comment, but why bother with basic auth at all, especially when there is no TLS?

i understand other auth schemes are more complicated, and maybe theres no desire to pull in big libraries. just that if theres no TLS or proper auth, you can also just skip basic auth. its only use would be to trick someone who's not familiar (unlikely with such a repo but not impossible) into a false sense of security.

ofc, not really an issue with the code, and its an excellent base to look into how this stuff works and if you want since its pretty clean and easy to ready, expand upon it. well done! love ppl churning out good ol C projects. respect!

elevation - 4 days ago

Nice work! I like the little test framework you built. Have you considered making runTest a macro so that you can print the name of the test along with the test result?

jcmontx - 4 days ago

I often forget how similar to Golang C looks and feels

Silphendio - 3 days ago

Choosing names like `App`, `ok` or `get` in a language without namespaces is a bold choice.

shevy-java - 3 days ago

Very courageous. I would have fatigued in the middle, probably using ruby or python instead.

krowek - 4 days ago

Curious, why did you decide to go with your own test helpers rather than using something like check?

leptons - 4 days ago

Does it do HTTPS? I'd be interested to try it on ESP32 but it has to support HTTPS.

Maksadbek - 4 days ago

Couldn't believe my eyes, this is the cleanest C code I've ever seen!!

SvenL - 4 days ago

I like it. I was looking for something like this and I will take a look into it.

dariosalvi78 - 4 days ago

How compatible is this with embedded devices? How much does this depend on OS APIs?

ak39 - 3 days ago

Nice.

Is if a one thread per request model?

Does it support async await type of architecture?

z3ratul163071 - 3 days ago

awesome!

hope hw vendors will adopt it so their management web pages are less ass than they actually are currently.

cyberax - 4 days ago

Uhh... This is an example why C is so bad for network-facing stuff:

https://github.com/ashtonjamesd/lavandula/blob/2dbefe6da16bf... - is it intended?

https://github.com/ashtonjamesd/lavandula/blob/2dbefe6da16bf... - pain....

BiraIgnacio - 4 days ago

Great work, thanks for this!

gwbas1c - 4 days ago

I don't understand the example. Does it even compile?

It's been a long time since I've used C, so maybe it's using some syntax that I'm unaware of?

IE: What defines "home" that is referenced as an argument to the "appRoute" function, and then passed to the "get" function to set up the "/home" route? Is "home" defined in lavandula.h, or is this really pseudocode?

defraudbah - 4 days ago

github is giving me 503, the project is too good for mS

Thanks for sharing, this looks amazing

ranger_danger - 4 days ago

which version of C does this conform to?

- 4 days ago
[deleted]
chuliomartinez - 3 days ago

[dead]

capestart - 4 days ago

[dead]

entelechy0 - 4 days ago

[dead]

alwahi - 4 days ago

[flagged]

EGreg - 4 days ago

Take a look at https://www.reddit.com/r/programming/comments/225ovy/okws_ok...

This was years ago (20 years ago?)

camillomiller - 4 days ago

[flagged]

tobyhinloopen - 4 days ago

[flagged]

guerrilla - 4 days ago

For fun or why?

JKCalhoun - 4 days ago

I'm stupid — is this to create web apps that run on the server? More or less replacing PHP or whatever?