DEV Community

Cover image for How to Write APIs Like a Pro in Go with GopherLight
Bruno Ciccarino λ for GopherLight

Posted on

How to Write APIs Like a Pro in Go with GopherLight

Docs

GopherLIght

Hey folks, first I would like to thank you for choosing to use our project. Even though he is small, we did it with great enthusiasm! To start using it you first have to have go installed, let's assume you already have it. Then install the main modules of the framework, which are req and router

go get github.com/BrunoCiccarino/GopherLight/router
go get github.com/BrunoCiccarino/GopherLight/req
Enter fullscreen mode Exit fullscreen mode

Already downloaded? Phew! Now we can make our first hello world.

package main

import (
    "fmt"
    "github.com/BrunoCiccarino/GopherLight/router"
    "github.com/BrunoCiccarino/GopherLight/req"
)


func main() {
    app := router.NewApp()

    // Define a route that responds to a GET request at "/hello".
    app.Get("/hello", func(r *req.Request, w *req.Response) {
        w.Send("Hello, World!")
    })

    fmt.Println("Server listening on port 3333")
    app.Listen(":3333")
}
Enter fullscreen mode Exit fullscreen mode

Pretty simple, right? And there’s way more we can do with GopherLight. Keep reading for a full breakdown of HTTP methods and our Request and Response tools.

Supported HTTP Methods
Here’s the list of HTTP methods you can use with router.App. Each of these allows you to set up routes to handle different types of requests. Let’s dive in!

GET

  • Usage: app.Get(path, handler)

Retrieves data without modifying anything.
Example: Fetching a list of items or reading user details.

POST

  • Usage: app.Post(path, handler)

Sends data to create a new resource.
Example: Submitting a form or adding a new item to a list.

PUT

Usage: app.Put(path, handler)

Updates or replaces a resource. It’s an “overwrite” action.
Example: Updating a full user profile.

DELETE

Usage: app.Delete(path, handler)

Deletes a resource.
Example: Removing a user or deleting a post.

PATCH

Usage: app.Patch(path, handler)

Partially updates a resource without replacing everything.
Example: Updating just the email on a user profile.

OPTIONS

Usage: app.Options(path, handler)

Returns allowed HTTP methods for a URL, mainly for CORS preflight requests.

HEAD

Usage: app.Head(path, handler)

Like GET, but no response body. Use it to check if a resource exists.

CONNECT and TRACE

Usage: app.Connect(path, handler), app.Trace(path, handler)

Advanced methods: CONNECT sets up a tunnel (for SSL), and TRACE is for debugging, echoing back the request.

Working with req.Request and req.Response

Now that you’ve seen the routes, let’s talk about the Request and Response objects, your go-to helpers for handling incoming requests and sending responses.

Request

Each request handler gets a Request object loaded with info on the incoming request. Here’s what you can do with it:

  • Query Parameters: Get query parameters with .QueryParam("key").
  • Headers: Access headers using .Header("key").
  • Body as String: Grab the request body with .BodyAsString().

Example:

app.Get("/greet", func(r *req.Request, w *req.Response) {
    name := r.QueryParam("name")
    if name == "" {
        name = "stranger"
    }
    w.Send("Hello, " + name + "!")
})
Enter fullscreen mode Exit fullscreen mode

Response

The Response object helps you send a reply back to the client. Here's what you can do:

  • Send Text: .Send(data string) writes plain text back.
  • Set Status: .Status(code) sets the HTTP status.
  • Send JSON: .JSON(data) serializes a Go object to JSON and sends it.
  • Handle Errors: .JSONError(message) sends a JSON-formatted error response.

Example:

app.Get("/user", func(r *req.Request, w *req.Response) {
    user := map[string]string{"name": "Gopher", "language": "Go"}
    w.JSON(user)
})
Enter fullscreen mode Exit fullscreen mode

Middlewares

We’ve got a batch of middlewares ready for you to add some serious functionality to your Go web app. Each of these middlewares brings its own magic—security, logging, timeouts, and more! Let’s break them down one by one. 👇

Authentication Middleware (JWT)

Our AuthMiddleware helps protect your routes with JSON Web Tokens (JWT). It’s flexible, letting you customize the secret key, error handling, and token extraction method.

Setup
To get started, configure your JWT settings using JWTConfig:

  • SecretKey: The secret key for signing JWTs.
  • SigningMethod: The JWT signing algorithm.
  • ErrorHandler: Custom error handler for handling auth errors (optional).
  • TokenExtractor: Extracts the token from the request header (optional).

Example

import (
    "github.com/BrunoCiccarino/GopherLight/middleware"
)

config := middleware.JWTConfig{
    SecretKey: []byte("your_secret_key"),
}
app.Use(middleware.NewAuthMiddleware(config))
Enter fullscreen mode Exit fullscreen mode

CORS Middleware

Need to allow cross-origin requests? No problem! Our CORSMiddleware configures the Cross-Origin Resource Sharing (CORS) settings to make your API accessible from other domains.

Config Options

  • AllowOrigin: Set to "*" to allow any origin or specify a domain (e.g., "http://example.com").
  • AllowMethods: Which HTTP methods are allowed? Common choices include "GET", "POST", etc.
  • AllowHeaders: Specify which headers clients can use.
  • AllowCredentials: Set to true if you want cookies or HTTP auth to be included.
  • ExposeHeaders: Let the client read specific headers from the response.
  • MaxAge: Cache time (in seconds) for preflight requests.

Example

corsOptions := middleware.CORSOptions{
    AllowOrigin: "*",
    AllowMethods: []string{"GET", "POST"},
}
app.Use(middleware.CORSMiddleware(corsOptions))
Enter fullscreen mode Exit fullscreen mode

CSRF Middleware

Our CSRFMiddleware protects against Cross-Site Request Forgery by validating a CSRF token sent with each request. Use GenerateCSRFToken() to create a secure token, then validate it with your own isValidToken function.

Example

app.Use(middleware.CSRFMiddleware(func(token string) bool {
    return token == "your_valid_token"
}))
Enter fullscreen mode Exit fullscreen mode

And don’t forget to generate tokens with:

csrfToken := middleware.GenerateCSRFToken()
Enter fullscreen mode Exit fullscreen mode

Logging Middleware

Want to keep track of what’s happening on your server? LoggingMiddleware logs each request, including the method, path, and time taken. It’s a great way to stay informed on app performance and any unusual activity.

Example

app.Use(middleware.LoggingMiddleware)
Enter fullscreen mode Exit fullscreen mode

Each request will be logged like this:

  • Started: Logs the request start time.
  • Completed: Logs when the request finishes, including the duration.

Timeout Middleware

Avoid those endless waits by setting time limits on request processing with TimeoutMiddleware. This middleware will cancel the request if it doesn’t complete in time, sending a 504 Gateway Timeout status to the client.

Example

import (
    "time"
    "github.com/BrunoCiccarino/GopherLight/middleware"
)

timeout := 2 * time.Second
app.Use(middleware.TimeoutMiddleware(timeout))
Enter fullscreen mode Exit fullscreen mode

The Plugin Interface

The Plugin interface is super simple but super powerful. It gives you a single method: Register. This lets you hook into the app’s routing system to add any routes you need—whether it’s a new API endpoint, a webhook, or anything else you can imagine.

The Register Method
Here’s the magic part of the Plugin interface:

type Plugin interface {
    Register(route func(method, path string, handler func(req *req.Request, res *req.Response)))
}
Enter fullscreen mode Exit fullscreen mode

The Register method accepts a route function that lets you define new routes in your plugin by specifying:

  • method: HTTP method (e.g., "GET", "POST", etc.)
  • path: The route path (e.g., "/my-plugin-route")
  • handler: The function to execute when the route is hit. This function receives:
    • req: The request object with access to query parameters, headers, and body.
    • res: The response object to send data back to the client.

Example Plugin

Let’s say you want to create a plugin that adds a simple endpoint at /hello-plugin to greet users. Here’s what the plugin would look like:

package main

import (
    "github.com/BrunoCiccarino/GopherLight/plugins"
    "github.com/BrunoCiccarino/GopherLight/req"
)

type HelloPlugin struct{}

// Register adds a new route for the HelloPlugin.
func (p *HelloPlugin) Register(route func(method, path string, handler func(req *req.Request, res *req.Response))) {
    route("GET", "/hello-plugin", func(req *req.Request, res *req.Response) {
        res.Send("Hello from the HelloPlugin!")
    })
}
Enter fullscreen mode Exit fullscreen mode

Adding the Plugin to Your App

To load a plugin, simply create an instance and call Register in your main app setup:

package main

import (
    "github.com/BrunoCiccarino/GopherLight/router"
)

func main() {
    app := router.NewApp()
    helloPlugin := &HelloPlugin{}
    helloPlugin.Register(app.Route)

    app.Listen(":3333")
}
Enter fullscreen mode Exit fullscreen mode

Customizing Your Plugins

Each plugin can add as many routes as needed. Just call route multiple times in your Register function to define additional endpoints. Use different HTTP methods, paths, and handlers to shape your plugin’s functionality however you want.

Top comments (0)