Welcome back!
In Chapter 1: File System & Indexing, we built the library infrastructure to manage files. In Chapter 2: Authentication & User Storage, we hired a security guard to check IDs.
But right now, our application is just a collection of silent code. It sits there waiting. It doesn't know how to talk to the outside world.
In this chapter, we will build the Receptionist. We will set up the web server that answers the phone, listens to requests from the browser, checks with the security guard, and directs the request to the right department.
Your browser speaks HTTP (HyperText Transfer Protocol). It sends messages like:
/photos."cat.jpg."
Without an HTTP Server and a Router, our Go code doesn't hear these messages. We need a system to translate a URL (like filebrowser.com/api/resources) into a specific Go function execution.
Filebrowser uses the standard Go library to handle this.
:8080) for incoming signals.deleteFile).http.ServeMux)Think of the Router as a phone directory. It maps a "Path" to an "Action".
/api/resourcesGETRun the function that lists files.
Middleware is code that runs before the main action.
In Chapter 2, we created the authentication logic. In this chapter, we wrap our API routes in a middleware called withUser.
withUser checks ID -> Handler executes -> Response sent.This is the end of the line. The handler takes the request, talks to the File System (Chapter 1), and sends back a JSON response (data formatted for the web).
Let's look at the most common scenario: The frontend asks to see the files in a folder.
GET /api/resources?path=/photos./api/resources and directs traffic to the resourceGetHandler.withUser wrapper ensures the user is logged in.[{"name": "vacation.jpg"}, ...].
In backend/http/httpRouter.go, we define the rules for our traffic controller.
// backend/http/httpRouter.go
// Create a new router
api := http.NewServeMux()
// Define a rule: When "GET /resources" is called...
// 1. Run 'withUser' (Middleware to check security)
// 2. Then run 'resourceGetHandler' (The actual logic)
api.HandleFunc("GET /resources", withUser(resourceGetHandler))
// Define a rule for Deleting
api.HandleFunc("DELETE /resources", withUser(resourceDeleteHandler))
Explanation:
api.HandleFunc: This adds a new entry to our phone directory.withUser(...): This is our Chapter 2 security logic being applied here. If the user isn't logged in, resourceGetHandler never runs!
Once the router accepts the request and the middleware approves the user, the resourceGetHandler runs. This is where we connect to Chapter 1.
// backend/http/resource.go
func resourceGetHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
// 1. Read the requested path from the URL (e.g., /photos)
path := r.URL.Query().Get("path")
// 2. Call the File System (from Chapter 1) to get the data
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
Path: path,
Source: r.URL.Query().Get("source"),
}, store.Access, d.user)
// 3. Convert the Go struct into JSON and send it back to the browser
return renderJSON(w, r, fileInfo)
}
Explanation:
r.URL.Query().Get("path"): Extracts ?path=/photos from the URL.files.FileInfoFaster: This is the "Librarian" from Chapter 1. It looks up the files in the Index.renderJSON: A helper that formats the result so the JavaScript frontend can understand it.What exactly happens from the moment you press "Enter" in the browser?
The application entry point is StartHttp. This function turns the lights on.
// backend/http/httpRouter.go
func StartHttp(ctx context.Context, storage *bolt.BoltStore, ...) {
// 1. Initialize the router
router := http.NewServeMux()
// 2. Setup the server settings (Port, Address)
srv := &http.Server{
Addr: ":8080", // Listens on port 8080
Handler: router, // Use our router to handle traffic
}
// 3. Start listening (this blocks the program and keeps it running)
go func() {
srv.ListenAndServe()
}()
// 4. Wait for a shutdown signal (Ctrl+C)
<-ctx.Done()
}
Explanation:
http.Server: This is the standard Go web server struct.ListenAndServe: This is the infinite loop. It waits for a request, processes it, waits for the next one, and so on.go func(): We run the server in a background thread (goroutine) so it doesn't freeze the rest of the application setup.Handling an upload is slightly more complex because data is streaming in.
// backend/http/resource.go
func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
// 1. Get destination path
path := r.URL.Query().Get("path") // e.g., /photos/new_image.jpg
// 2. Check Permissions (Authorization)
if !d.user.Permissions.Create {
return http.StatusForbidden, fmt.Errorf("you can't create files")
}
// 3. Stream the file directly from the HTTP body to the disk
// (This uses the WriteFile function we saw in Chapter 1)
err := files.WriteFile(source, path, r.Body)
return http.StatusOK, nil
}
Explanation:
r.Body: This represents the stream of bytes coming from the user's browser (the file content).files.WriteFile: We pass the stream directly to the file system adapter. This ensures we don't load the whole file into RAM, which is great for performance.In this chapter, we connected the brain (File System) and the security (Auth) to the outside world.
Now that the backend is fully functional—it can store files, secure users, and answer network requests—we need to control how it starts up and behaves on different computers.
Next Chapter: Configuration & Startup
Generated by Code IQ