I've been using SwiftUI for a while and loving it. I've been wanting to program in swift on server side for so long. But all other server side swift solutions are same and makes no new ground.
For example most server side swift libraries(Kitura, Perfect etc) has nearly identical syntax to other server side scripting languages(node, python etc)
So I created this library for server side programming. Only the syntax is working its not handling any requests or sql connections right now but can be implemented if we can get enough people.
Every request has its input(like post parameters or get params) and it can manipulate its environment with those inputs. My main goal is to write safe server side code with as less code as possible.
The syntax here is very compelling. Although I'm not a bug server-side Swift user at this point, I'd be quite interested to see how you grow with this project.
This really inspired me, but there are some details in the syntax I'm not crazy about. I decided to put together my own prototype. Here is a working example server (you can also see the code on GitHub here).
import Firn
let api = API()
api.configureAuthentication(for: User.self) { request in
// An authorization configuration must return a user if the auth is valid.
// If it returns nil, the API will return an unauthorized response.
guard request.headers["Authorization"].contains("Bearer SECURE") else {
return nil
}
return User(name: "Username")
}
try api.addRoutes {
// Returns an empty 200 status
GET("ok")
// Returns a gone status
GET("gone")
.status(.gone)
// Returns "Hello World!"
GET("hello")
.toText({ _ in "Hello World!"})
// Returns whatever data is passed in
POST("echo")
Auth(User.self) {
// Returns a message confirming authentication as long as it
// includes the "Authorization: Bearer SECURE" header as defined
// in the auth configuration above
GET("restricted")
.toText({ req in
let user = try req.authorizedUser(User.self)
return "Look at you, \(user.name), you're authorized!"
})
}
Auth(User.self) {
// Defines a group of endpoints all at "/tasks"
Group("tasks") {
// Return a json representation of a Task
GET("new")
.toObject({ _ in Task(name: "") })
// Parses and returns an json representation of a Task
POST()
.toObject(Task.self)
// Returns a Task with the id in the path e.g. 3 for "/tasks/3"
GET(Var.int)
.toObject({ Task(id: try $0.pathParams.int(at: 0), name: "Some task", isComplete: false) })
// Parses and returns an json representation of a Task
PUT(Var.int)
.toObject(Task.self)
}
}
}
api.run()
The big differences I see from what you implemented:
Eliminate a lot of the boilerplate repetition of MyEnvironment.
Grouping endpoints
Variable path components (capture strings and ints from the path automatically)
Eliminate "response" method. All states of mapping will result in some sort of logical response so that boilerplate isn't necessary).
Language differences in the methods on the endpoints
Auth does not encourage forced unwrapping and instead throws an error
Mine is also implemented on Swift NIO (and also very rough right now). I got pretty far implementing my own DB functionality based on my existing SQL libraries, even to the point of it functioning to do things like Get("posts").list(Post.self) to automatically retrieve all of the posts from the DB, but decided to pull back for now and rethink the DB implementation.
I've also started to play around with adding the ability for it to automatically generate working Swift client code on this branch. It's going well but the major sticking point will be properly generating the input and output declarations when using custom types.
My big design goal is to create as fast and easy of a dev experience as possible. I'm still deciding if I want to try to bring in an existing DB implementation (like Vapors), use my existing one (SQL), or start a new one.
Looks lovely. I was just about to create a grouping method like this and I even solved authentication without any optional types or try catch methods. I’ll push my method tomorrow you can check it out!
I really want to see this becoming a new way of server side programming.